blob: d1ad82c9c9edd1c2b0eee1d892b3e0ef9067eb34 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDE_PERFETTO_TRACING_CORE_STARTUP_TRACE_WRITER_H_
#define INCLUDE_PERFETTO_TRACING_CORE_STARTUP_TRACE_WRITER_H_
#include <memory>
#include <mutex>
#include <set>
#include <vector>
#include "perfetto/base/export.h"
#include "perfetto/base/optional.h"
#include "perfetto/base/thread_checker.h"
#include "perfetto/protozero/message_handle.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "perfetto/protozero/scattered_stream_writer.h"
#include "perfetto/tracing/core/basic_types.h"
#include "perfetto/tracing/core/trace_writer.h"
namespace perfetto {
class SharedMemoryArbiterImpl;
class StartupTraceWriterRegistryHandle;
namespace protos {
namespace pbzero {
class TracePacket;
} // namespace pbzero
} // namespace protos
// Facilitates writing trace events in early phases of an application's startup
// when the perfetto service is not available yet.
//
// Until the service is available, producer threads instantiate an unbound
// StartupTraceWriter instance (via a StartupTraceWriterRegistry) and use it to
// emit trace events. Each writer will record the serialized trace events into a
// temporary local memory buffer.
//
// Once the service is available, the producer binds each StartupTraceWriter to
// the SMB by calling SharedMemoryArbiter::BindStartupTraceWriter(). The data in
// the writer's local buffer will then be copied into the SMB and the any future
// writes will proxy directly to a new SMB-backed TraceWriter.
//
// Writing to the temporary local trace buffer is guarded by a lock and flag to
// allow binding the writer from a different thread. When the writer starts
// writing data by calling NewTracePacket(), the writer thread acquires the lock
// to set a flag indicating that a write is in progress. Once the packet is
// finalized, the flag is reset. To bind the writer, the lock is acquired while
// the flag is unset and released only once binding completed, thereby blocking
// the writer thread from starting a write concurrently.
//
// While unbound, the writer thread should finalize each TracePacket as soon as
// possible to ensure that it doesn't block binding the writer.
class PERFETTO_EXPORT StartupTraceWriter
: public TraceWriter,
public protozero::MessageHandleBase::FinalizationListener {
public:
// Create a StartupTraceWriter bound to |trace_writer|. Should only be called
// on the writer thread.
explicit StartupTraceWriter(std::unique_ptr<TraceWriter> trace_writer);
~StartupTraceWriter() override;
// TraceWriter implementation. These methods should only be called on the
// writer thread.
TracePacketHandle NewTracePacket() override;
void Flush(std::function<void()> callback = {}) override;
// Note that this will return 0 until the first TracePacket was started after
// binding.
WriterID writer_id() const override;
uint64_t written() const override;
// Returns |true| if the writer thread has observed that the writer was bound
// to an SMB. Should only be called on the writer thread.
//
// The writer thread can use the return value to determine whether it needs to
// finalize the current TracePacket as soon as possible. It is only safe for
// the writer to batch data into a single TracePacket over a longer time
// period when this returns |true|.
bool was_bound() const {
PERFETTO_DCHECK_THREAD(writer_thread_checker_);
return was_bound_;
}
// Should only be called on the writer thread.
size_t used_buffer_size();
private:
friend class StartupTraceWriterRegistry;
friend class StartupTraceWriterTest;
// Create an unbound StartupTraceWriter associated with the registry pointed
// to by the handle. The writer can later be bound by calling
// BindToTraceWriter(). The registry handle may be nullptr in tests.
StartupTraceWriter(std::shared_ptr<StartupTraceWriterRegistryHandle>);
StartupTraceWriter(const StartupTraceWriter&) = delete;
StartupTraceWriter& operator=(const StartupTraceWriter&) = delete;
// Bind this StartupTraceWriter to the provided SharedMemoryArbiterImpl.
// Called by StartupTraceWriterRegistry::BindToArbiter().
//
// This method can be called on any thread. If any data was written locally
// before the writer was bound, BindToArbiter() will copy this data into
// chunks in the provided target buffer via the SMB. Any future packets will
// be directly written into the SMB via a newly obtained TraceWriter from the
// arbiter.
//
// Will fail and return |false| if a concurrent write is in progress. Returns
// |true| if successfully bound and should then not be called again.
bool BindToArbiter(SharedMemoryArbiterImpl*,
BufferID target_buffer) PERFETTO_WARN_UNUSED_RESULT;
// protozero::MessageHandleBase::FinalizationListener implementation.
void OnMessageFinalized(protozero::Message* message) override;
void OnTracePacketCompleted();
ChunkID CommitLocalBufferChunks(SharedMemoryArbiterImpl*, WriterID, BufferID);
PERFETTO_THREAD_CHECKER(writer_thread_checker_)
std::shared_ptr<StartupTraceWriterRegistryHandle> registry_handle_;
// Only set and accessed from the writer thread. The writer thread flips this
// bit when it sees that trace_writer_ is set (while holding the lock).
// Caching this fact in this variable avoids the need to acquire the lock to
// check on later calls to NewTracePacket().
bool was_bound_ = false;
// All variables below this point are protected by |lock_|.
std::mutex lock_;
// Never reset once it is changed from |nullptr|.
std::unique_ptr<TraceWriter> trace_writer_ = nullptr;
// Local memory buffer for trace packets written before the writer is bound.
std::unique_ptr<protozero::ScatteredHeapBuffer> memory_buffer_;
std::unique_ptr<protozero::ScatteredStreamWriter> memory_stream_writer_;
std::vector<uint32_t> packet_sizes_;
// Whether the writer thread is currently writing a TracePacket.
bool write_in_progress_ = false;
// The packet returned via NewTracePacket() while the writer is unbound. Reset
// to |nullptr| once bound. Owned by this class, TracePacketHandle has just a
// pointer to it.
std::unique_ptr<protos::pbzero::TracePacket> cur_packet_;
};
} // namespace perfetto
#endif // INCLUDE_PERFETTO_TRACING_CORE_STARTUP_TRACE_WRITER_H_