blob: 937aa9132fecb7620b3ebb6b9b3733a23b961bab [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_TRACING_H_
#define INCLUDE_PERFETTO_TRACING_TRACING_H_
#include <stddef.h>
#include <stdint.h>
#include <functional>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "perfetto/base/compiler.h"
#include "perfetto/base/export.h"
#include "perfetto/base/logging.h"
#include "perfetto/tracing/backend_type.h"
#include "perfetto/tracing/core/forward_decls.h"
#include "perfetto/tracing/internal/in_process_tracing_backend.h"
#include "perfetto/tracing/internal/system_tracing_backend.h"
#include "perfetto/tracing/tracing_policy.h"
namespace perfetto {
namespace internal {
class TracingMuxerImpl;
}
class TracingBackend;
class Platform;
class StartupTracingSession; // Declared below.
class TracingSession; // Declared below.
struct TracingError {
enum ErrorCode : uint32_t {
// Peer disconnection.
kDisconnected = 1,
// The Start() method failed. This is typically because errors in the passed
// TraceConfig. More details are available in |message|.
kTracingFailed = 2,
};
ErrorCode code;
std::string message;
TracingError(ErrorCode cd, std::string msg)
: code(cd), message(std::move(msg)) {
PERFETTO_CHECK(!message.empty());
}
};
using LogLev = ::perfetto::base::LogLev;
using LogMessageCallbackArgs = ::perfetto::base::LogMessageCallbackArgs;
using LogMessageCallback = ::perfetto::base::LogMessageCallback;
struct TracingInitArgs {
uint32_t backends = 0; // One or more BackendTypes.
TracingBackend* custom_backend = nullptr; // [Optional].
// [Optional] Platform implementation. It allows the embedder to take control
// of platform-specific bits like thread creation and TLS slot handling. If
// not set it will use Platform::GetDefaultPlatform().
Platform* platform = nullptr;
// [Optional] Tune the size of the shared memory buffer between the current
// process and the service backend(s). This is a trade-off between memory
// footprint and the ability to sustain bursts of trace writes (see comments
// in shared_memory_abi.h).
// If set, the value must be a multiple of 4KB. The value can be ignored if
// larger than kMaxShmSize (32MB) or not a multiple of 4KB.
uint32_t shmem_size_hint_kb = 0;
// [Optional] Specifies the preferred size of each page in the shmem buffer.
// This is a trade-off between IPC overhead and fragmentation/efficiency of
// the shmem buffer in presence of multiple writer threads.
// Must be one of [4, 8, 16, 32].
uint32_t shmem_page_size_hint_kb = 0;
// [Optional] The length of the period during which shared-memory-buffer
// chunks that have been filled with data are accumulated (batched) on the
// producer side, before the service is notified of them over an out-of-band
// IPC call. If, while this period lasts, the shared memory buffer gets too
// full, the IPC call will be sent immediately. The value of this parameter is
// a trade-off between IPC traffic overhead and the ability to sustain bursts
// of trace writes. The higher the value, the more chunks will be batched and
// the less buffer space will be available to hide the latency of the service,
// and vice versa. For more details, see the SetBatchCommitsDuration method in
// shared_memory_arbiter.h.
//
// Note: With the default value of 0ms, batching still happens but with a zero
// delay, i.e. commits will be sent to the service at the next opportunity.
uint32_t shmem_batch_commits_duration_ms = 0;
// [Optional] If set, the policy object is notified when certain SDK events
// occur and may apply policy decisions, such as denying connections. The
// embedder is responsible for ensuring the object remains alive for the
// lifetime of the process.
TracingPolicy* tracing_policy = nullptr;
// [Optional] If set, log messages generated by perfetto are passed to this
// callback instead of being logged directly.
LogMessageCallback log_message_callback = nullptr;
// When this flag is set to false, it overrides
// `DataSource::kSupportsMultipleInstances` for all the data sources.
// As a result when a tracing session is already running and if we attempt to
// start another session, it will fail to start the data source which were
// already active.
bool supports_multiple_data_source_instances = true;
// If this flag is set the default clock for taking timestamps is overridden
// with CLOCK_MONOTONIC (for use in Chrome).
bool use_monotonic_clock = false;
// If this flag is set the default clock for taking timestamps is overridden
// with CLOCK_MONOTONIC_RAW on platforms that support it.
bool use_monotonic_raw_clock = false;
// This flag can be set to false in order to avoid enabling the system
// consumer in Tracing::Initialize(), so that the linker can remove the unused
// consumer IPC implementation to reduce binary size. This setting only has an
// effect if kSystemBackend is specified in |backends|. When this option is
// false, Tracing::NewTrace() will instatiate the system backend only if
// explicitly specified as kSystemBackend: kUndefinedBackend will consider
// only already instantiated backends.
bool enable_system_consumer = true;
// When true, sets disallow_merging_with_system_tracks in TrackDescriptor,
// making sure that Trace Processor doesn't merge track event and system
// event tracks for the same thread.
bool disallow_merging_with_system_tracks = false;
protected:
friend class Tracing;
friend class internal::TracingMuxerImpl;
using BackendFactoryFunction = TracingBackend* (*)();
using ProducerBackendFactoryFunction = TracingProducerBackend* (*)();
using ConsumerBackendFactoryFunction = TracingConsumerBackend* (*)();
BackendFactoryFunction in_process_backend_factory_ = nullptr;
ProducerBackendFactoryFunction system_producer_backend_factory_ = nullptr;
ConsumerBackendFactoryFunction system_consumer_backend_factory_ = nullptr;
bool dcheck_is_on_ = PERFETTO_DCHECK_IS_ON();
};
// The entry-point for using perfetto.
class PERFETTO_EXPORT_COMPONENT Tracing {
public:
// Initializes Perfetto with the given backends in the calling process and/or
// with a user-provided backend. It's possible to call this function more than
// once to initialize different backends. If a backend was already initialized
// the call will have no effect on it. All the members of `args` will be
// ignored in subsequent calls, except those require to initialize new
// backends (`backends`, `enable_system_consumer`, `shmem_size_hint_kb`,
// `shmem_page_size_hint_kb` and `shmem_batch_commits_duration_ms`).
static inline void Initialize(const TracingInitArgs& args)
PERFETTO_ALWAYS_INLINE {
TracingInitArgs args_copy(args);
// This code is inlined to allow dead-code elimination for unused backends.
// This saves ~200 KB when not using the in-process backend (b/148198993).
// The logic behind it is the following:
// Nothing other than the code below references the two GetInstance()
// methods. From a linker-graph viewpoint, those GetInstance() pull in many
// other pieces of the codebase (e.g. InProcessTracingBackend pulls the
// whole TracingServiceImpl, SystemTracingBackend pulls the whole //ipc
// layer). Due to the inline, the compiler can see through the code and
// realize that some branches are always not taken. When that happens, no
// reference to the backends' GetInstance() is emitted and that allows the
// linker GC to get rid of the entire set of dependencies.
if (args.backends & kInProcessBackend) {
args_copy.in_process_backend_factory_ =
&internal::InProcessTracingBackend::GetInstance;
}
if (args.backends & kSystemBackend) {
args_copy.system_producer_backend_factory_ =
&internal::SystemProducerTracingBackend::GetInstance;
if (args.enable_system_consumer) {
args_copy.system_consumer_backend_factory_ =
&internal::SystemConsumerTracingBackend::GetInstance;
}
}
InitializeInternal(args_copy);
}
// Checks if tracing has been initialized by calling |Initialize|.
static bool IsInitialized();
// Start a new tracing session using the given tracing backend. Use
// |kUnspecifiedBackend| to select an available backend automatically.
static inline std::unique_ptr<TracingSession> NewTrace(
BackendType backend = kUnspecifiedBackend) PERFETTO_ALWAYS_INLINE {
// This code is inlined to allow dead-code elimination for unused consumer
// implementation. The logic behind it is the following:
// Nothing other than the code below references the GetInstance() method
// below. From a linker-graph viewpoint, those GetInstance() pull in many
// other pieces of the codebase (ConsumerOnlySystemTracingBackend pulls
// ConsumerIPCClient). Due to the inline, the compiler can see through the
// code and realize that some branches are always not taken. When that
// happens, no reference to the backends' GetInstance() is emitted and that
// allows the linker GC to get rid of the entire set of dependencies.
TracingConsumerBackend* (*system_backend_factory)();
system_backend_factory = nullptr;
// In case PERFETTO_IPC is disabled, a fake system backend is used, which
// always panics. NewTrace(kSystemBackend) should fail if PERFETTO_IPC is
// diabled, not panic.
#if PERFETTO_BUILDFLAG(PERFETTO_IPC)
if (backend & kSystemBackend) {
system_backend_factory =
&internal::SystemConsumerTracingBackend::GetInstance;
}
#endif
return NewTraceInternal(backend, system_backend_factory);
}
// Shut down Perfetto, releasing any allocated OS resources (threads, files,
// sockets, etc.). Note that Perfetto cannot be reinitialized again in the
// same process[1]. Instead, this function is meant for shutting down all
// Perfetto-related code so that it can be safely unloaded, e.g., with
// dlclose().
//
// It is only safe to call this function when all threads recording trace
// events have been terminated or otherwise guaranteed to not make any further
// calls into Perfetto.
//
// [1] Unless static data is also cleared through other means.
static void Shutdown();
// Uninitialize Perfetto. Only exposed for testing scenarios where it can be
// guaranteed that no tracing sessions or other operations are happening when
// this call is made.
static void ResetForTesting();
// Start a new startup tracing session in the current process. Startup tracing
// can be used in anticipation of a session that will be started by the
// specified backend in the near future. The data source configs in the
// supplied TraceConfig have to (mostly) match those in the config that will
// later be provided by the backend.
// Learn more about config matching at ComputeStartupConfigHash.
//
// Note that startup tracing requires that either:
// (a) the service backend already has an SMB set up, or
// (b) the service backend to support producer-provided SMBs if the backend
// is not yet connected or no SMB has been set up yet
// (See `use_producer_provided_smb`). If necessary, the
// client library will briefly disconnect and reconnect the backend to
// supply an SMB to the backend. If the service does not accept the SMB,
// startup tracing will be aborted, but the service may still start the
// corresponding tracing session later.
//
// Startup tracing is NOT supported with the in-process backend. For this
// backend, you can just start a regular tracing session and block until it is
// set up instead.
//
// The client library will start the data sources instances specified in the
// config with a placeholder target buffer. Once the backend starts a matching
// tracing session, the session will resume as normal. If no matching session
// is started after a timeout (or the backend doesn't accept the
// producer-provided SMB), the startup tracing session will be aborted
// and the data source instances stopped.
struct OnStartupTracingSetupCallbackArgs {
int num_data_sources_started;
};
struct SetupStartupTracingOpts {
BackendType backend = kUnspecifiedBackend;
uint32_t timeout_ms = 10000;
// If set, this callback is executed (on an internal Perfetto thread) when
// startup tracing was set up.
std::function<void(OnStartupTracingSetupCallbackArgs)> on_setup;
// If set, this callback is executed (on an internal Perfetto thread) if any
// data sources were aborted, e.g. due to exceeding the timeout or as a
// response to Abort().
std::function<void()> on_aborted;
// If set, this callback is executed (on an internal Perfetto thread) after
// all data sources were adopted by a tracing session initiated by the
// backend.
std::function<void()> on_adopted;
};
static std::unique_ptr<StartupTracingSession> SetupStartupTracing(
const TraceConfig& config,
SetupStartupTracingOpts);
// Blocking version of above method, so callers can ensure that tracing is
// active before proceeding with app startup. Calls into
// DataSource::Trace() or trace macros right after this method are written
// into the startup session.
static std::unique_ptr<StartupTracingSession> SetupStartupTracingBlocking(
const TraceConfig& config,
SetupStartupTracingOpts);
// Informs the tracing services to activate any of these triggers if any
// tracing session was waiting for them.
//
// Sends the trigger signal to all the initialized backends that are currently
// connected and that connect in the next `ttl_ms` milliseconds (but
// returns immediately anyway).
static void ActivateTriggers(const std::vector<std::string>& triggers,
uint32_t ttl_ms);
private:
static void InitializeInternal(const TracingInitArgs&);
static std::unique_ptr<TracingSession> NewTraceInternal(
BackendType,
TracingConsumerBackend* (*system_backend_factory)());
Tracing() = delete;
};
class PERFETTO_EXPORT_COMPONENT TracingSession {
public:
virtual ~TracingSession();
// Configure the session passing the trace config.
// If a writable file handle is given through |fd|, the trace will
// automatically written to that file. Otherwise you should call ReadTrace()
// to retrieve the trace data. This call does not take ownership of |fd|.
// TODO(primiano): add an error callback.
virtual void Setup(const TraceConfig&, int fd = -1) = 0;
// Enable tracing asynchronously. Use SetOnStartCallback() to get a
// notification when the session has fully started.
virtual void Start() = 0;
// Enable tracing and block until tracing has started. Note that if data
// sources are registered after this call was initiated, the call may return
// before the additional data sources have started. Also, if other producers
// (e.g., with system-wide tracing) have registered data sources without start
// notification support, this call may return before those data sources have
// started.
virtual void StartBlocking() = 0;
// This callback will be invoked when all data sources have acknowledged that
// tracing has started. This callback will be invoked on an internal perfetto
// thread.
virtual void SetOnStartCallback(std::function<void()>) = 0;
// This callback can be used to get a notification when some error occured
// (e.g., peer disconnection). Error type will be passed as an argument. This
// callback will be invoked on an internal perfetto thread.
virtual void SetOnErrorCallback(std::function<void(TracingError)>) = 0;
// Issues a flush request, asking all data sources to ack the request, within
// the specified timeout. A "flush" is a fence to ensure visibility of data in
// the async tracing pipeline. It guarantees that all data written before the
// Flush() call will be visible in the trace buffer and hence by the
// ReadTrace() / ReadTraceBlocking() methods.
// Args:
// callback: will be invoked on an internal perfetto thread when all data
// sources have acked, or the timeout is reached. The bool argument
// will be true if all data sources acked within the timeout, false if
// the timeout was hit or some other error occurred (e.g. the tracing
// session wasn't started or ended).
// timeout_ms: how much time the service will wait for data source acks. If
// 0, the global timeout specified in the TraceConfig (flush_timeout_ms)
// will be used. If flush_timeout_ms is also unspecified, a default value
// of 5s will be used.
// Known issues:
// Because flushing is still based on service-side scraping, the very last
// trace packet for each data source thread will not be visible. Fixing
// this requires either propagating the Flush() to the data sources or
// changing the order of atomic operations in the service (b/162206162).
// Until then, a workaround is to make sure to call
// DataSource::Trace([](TraceContext ctx) { ctx.Flush(); }) just before
// stopping, on each thread where DataSource::Trace has been previously
// called.
virtual void Flush(std::function<void(bool)>, uint32_t timeout_ms = 0) = 0;
// Blocking version of Flush(). Waits until all data sources have acked and
// returns the success/failure status.
bool FlushBlocking(uint32_t timeout_ms = 0);
// Disable tracing asynchronously.
// Use SetOnStopCallback() to get a notification when the tracing session is
// fully stopped and all data sources have acked.
virtual void Stop() = 0;
// Disable tracing and block until tracing has stopped.
virtual void StopBlocking() = 0;
// This callback will be invoked when tracing is disabled.
// This can happen either when explicitly calling TracingSession.Stop() or
// when the trace reaches its |duration_ms| time limit.
// This callback will be invoked on an internal perfetto thread.
virtual void SetOnStopCallback(std::function<void()>) = 0;
// Changes the TraceConfig for an active tracing session. The session must
// have been configured and started before. Note that the tracing service
// only supports changing a subset of TraceConfig fields,
// see ConsumerEndpoint::ChangeTraceConfig().
virtual void ChangeTraceConfig(const TraceConfig&) = 0;
// Struct passed as argument to the callback passed to ReadTrace().
// [data, size] is guaranteed to contain 1 or more full trace packets, which
// can be decoded using trace.proto. No partial or truncated packets are
// exposed. If the trace is empty this returns a zero-sized nullptr with
// |has_more| == true to signal EOF.
// This callback will be invoked on an internal perfetto thread.
struct ReadTraceCallbackArgs {
const char* data = nullptr;
size_t size = 0;
// When false, this will be the last invocation of the callback for this
// read cycle.
bool has_more = false;
};
// Reads back the trace data (raw protobuf-encoded bytes) asynchronously.
// Can be called at any point during the trace, typically but not necessarily,
// after stopping. If this is called before the end of the trace (i.e. before
// Stop() / StopBlocking()), in almost all cases you need to call
// Flush() / FlushBlocking() before Read(). This is to guarantee that tracing
// data in-flight in the data sources is committed into the tracing buffers
// before reading them.
// Reading the trace data is a destructive operation w.r.t. contents of the
// trace buffer and is not idempotent.
// A single ReadTrace() call can yield >1 callback invocations, until
// |has_more| is false.
using ReadTraceCallback = std::function<void(ReadTraceCallbackArgs)>;
virtual void ReadTrace(ReadTraceCallback) = 0;
// Synchronous version of ReadTrace(). It blocks the calling thread until all
// the trace contents are read. This is slow and inefficient (involves more
// copies) and is mainly intended for testing.
std::vector<char> ReadTraceBlocking();
// Struct passed as an argument to the callback for GetTraceStats(). Contains
// statistics about the tracing session.
struct GetTraceStatsCallbackArgs {
// Whether or not querying statistics succeeded.
bool success = false;
// Serialized TraceStats protobuf message. To decode:
//
// perfetto::protos::gen::TraceStats trace_stats;
// trace_stats.ParseFromArray(args.trace_stats_data.data(),
// args.trace_stats_data.size());
//
std::vector<uint8_t> trace_stats_data;
};
// Requests a snapshot of statistical data for this tracing session. Only one
// query may be active at a time. This callback will be invoked on an internal
// perfetto thread.
using GetTraceStatsCallback = std::function<void(GetTraceStatsCallbackArgs)>;
virtual void GetTraceStats(GetTraceStatsCallback) = 0;
// Synchronous version of GetTraceStats() for convenience.
GetTraceStatsCallbackArgs GetTraceStatsBlocking();
// Struct passed as an argument to the callback for QueryServiceState().
// Contains information about registered data sources.
struct QueryServiceStateCallbackArgs {
// Whether or not getting the service state succeeded.
bool success = false;
// Serialized TracingServiceState protobuf message. To decode:
//
// perfetto::protos::gen::TracingServiceState state;
// state.ParseFromArray(args.service_state_data.data(),
// args.service_state_data.size());
//
std::vector<uint8_t> service_state_data;
};
// Requests a snapshot of the tracing service state for this session. Only one
// request per session may be active at a time. This callback will be invoked
// on an internal perfetto thread.
using QueryServiceStateCallback =
std::function<void(QueryServiceStateCallbackArgs)>;
virtual void QueryServiceState(QueryServiceStateCallback) = 0;
// Synchronous version of QueryServiceState() for convenience.
QueryServiceStateCallbackArgs QueryServiceStateBlocking();
};
class PERFETTO_EXPORT_COMPONENT StartupTracingSession {
public:
// Note that destroying the StartupTracingSession object will not abort the
// startup session automatically. Call Abort() explicitly to do so.
virtual ~StartupTracingSession();
// Abort any active but still unbound data source instances that belong to
// this startup tracing session. Does not affect data source instances that
// were already bound to a service-controlled session.
virtual void Abort() = 0;
// Same as above, but blocks the current thread until aborted.
// Note some of the internal (non observable from public APIs) cleanup might
// be done even after this method returns.
virtual void AbortBlocking() = 0;
};
} // namespace perfetto
#endif // INCLUDE_PERFETTO_TRACING_TRACING_H_