| /* |
| * 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_ |