blob: 95036d14884b27389a2b8bdec77a1bfb100490e9 [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_DATA_SOURCE_H_
#define INCLUDE_PERFETTO_TRACING_DATA_SOURCE_H_
// This header contains the key class (DataSource) that a producer app should
// override in order to create a custom data source that gets tracing Start/Stop
// notifications and emits tracing data.
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <array>
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include "perfetto/protozero/message_handle.h"
#include "perfetto/tracing/buffer_exhausted_policy.h"
#include "perfetto/tracing/core/forward_decls.h"
#include "perfetto/tracing/internal/basic_types.h"
#include "perfetto/tracing/internal/data_source_internal.h"
#include "perfetto/tracing/internal/data_source_type.h"
#include "perfetto/tracing/internal/tracing_muxer.h"
#include "perfetto/tracing/locked_handle.h"
#include "perfetto/tracing/trace_writer_base.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
// DEPRECATED: Instead of using this macro, prefer specifying symbol linkage
// attributes explicitly using the `_WITH_ATTRS` macro variants (e.g.,
// PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS). This avoids
// potential macro definition collisions between two libraries using Perfetto.
//
// PERFETTO_COMPONENT_EXPORT is used to mark symbols in Perfetto's headers
// (typically templates) that are defined by the user outside of Perfetto and
// should be made visible outside the current module. (e.g., in Chrome's
// component build).
#if !defined(PERFETTO_COMPONENT_EXPORT)
#if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_MSVC)
// Workaround for C4003: not enough arguments for function-like macro invocation
// 'PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE'
#define PERFETTO_COMPONENT_EXPORT __declspec()
#else
#define PERFETTO_COMPONENT_EXPORT
#endif
#endif
namespace perfetto {
namespace internal {
class TracingMuxerImpl;
class TrackEventCategoryRegistry;
template <typename, const internal::TrackEventCategoryRegistry*>
class TrackEventDataSource;
} // namespace internal
namespace test {
class DataSourceInternalForTest;
} // namespace test
// Base class with the virtual methods to get start/stop notifications.
// Embedders are supposed to derive the templated version below, not this one.
class PERFETTO_EXPORT_COMPONENT DataSourceBase {
public:
virtual ~DataSourceBase();
// TODO(primiano): change the const& args below to be pointers instead. It
// makes it more awkward to handle output arguments and require mutable(s).
// This requires synchronizing a breaking API change for existing embedders.
// OnSetup() is invoked when tracing is configured. In most cases this happens
// just before starting the trace. In the case of deferred start (see
// deferred_start in trace_config.proto) start might happen later.
class SetupArgs {
public:
// This is valid only within the scope of the OnSetup() call and must not
// be retained.
const DataSourceConfig* config = nullptr;
// The index of this data source instance (0..kMaxDataSourceInstances - 1).
uint32_t internal_instance_index = 0;
};
virtual void OnSetup(const SetupArgs&);
class StartArgs {
public:
// The index of this data source instance (0..kMaxDataSourceInstances - 1).
uint32_t internal_instance_index = 0;
};
virtual void OnStart(const StartArgs&);
class StopArgs {
public:
virtual ~StopArgs();
// HandleAsynchronously() can optionally be called to defer the tracing
// session stop and write tracing data just before stopping.
// This function returns a closure that must be invoked after the last
// trace events have been emitted. The returned closure can be called from
// any thread. The caller also needs to explicitly call TraceContext.Flush()
// from the last Trace() lambda invocation because no other implicit flushes
// will happen after the stop signal.
// When this function is called, the tracing service will defer the stop of
// the tracing session until the returned closure is invoked.
// However, the caller cannot hang onto this closure for too long. The
// tracing service will forcefully stop the tracing session without waiting
// for pending producers after TraceConfig.data_source_stop_timeout_ms
// (default: 5s, can be overridden by Consumers when starting a trace).
// If the closure is called after this timeout an error will be logged and
// the trace data emitted will not be present in the trace. No other
// functional side effects (e.g. crashes or corruptions) will happen. In
// other words, it is fine to accidentally hold onto this closure for too
// long but, if that happens, some tracing data will be lost.
virtual std::function<void()> HandleStopAsynchronously() const = 0;
// The index of this data source instance (0..kMaxDataSourceInstances - 1).
uint32_t internal_instance_index = 0;
};
virtual void OnStop(const StopArgs&);
class ClearIncrementalStateArgs {
public:
// The index of this data source instance (0..kMaxDataSourceInstances - 1).
uint32_t internal_instance_index = 0;
};
virtual void WillClearIncrementalState(const ClearIncrementalStateArgs&);
class FlushArgs {
public:
virtual ~FlushArgs();
// HandleFlushAsynchronously() can be called to postpone acknowledging the
// flush request. This function returns a closure that must be invoked after
// the flush request has been processed. The returned closure can be called
// from any thread.
virtual std::function<void()> HandleFlushAsynchronously() const = 0;
// The index of this data source instance (0..kMaxDataSourceInstances - 1).
uint32_t internal_instance_index = 0;
};
// Called when the tracing service requests a Flush. Users can override this
// to tell other threads to flush their TraceContext for this data source
// (the library cannot execute code on all the threads on its own).
virtual void OnFlush(const FlushArgs&);
};
struct DefaultDataSourceTraits {
// |IncrementalStateType| can optionally be used store custom per-sequence
// incremental data (e.g., interning tables).
using IncrementalStateType = void;
// |TlsStateType| can optionally be used to store custom per-sequence
// session data, which is not reset when incremental state is cleared
// (e.g. configuration options).
using TlsStateType = void;
// Allows overriding what type of thread-local state configuration the data
// source uses. By default every data source gets independent thread-local
// state, which means every instance uses separate trace writers and
// incremental state even on the same thread. Some data sources (most notably
// the track event data source) want to share trace writers and incremental
// state on the same thread.
static internal::DataSourceThreadLocalState* GetDataSourceTLS(
internal::DataSourceStaticState* static_state,
internal::TracingTLS* root_tls) {
auto* ds_tls = &root_tls->data_sources_tls[static_state->index];
// ds_tls->static_state can be:
// * nullptr
// * equal to static_state
// * equal to the static state of a different data source, in tests (when
// ResetForTesting() has been used)
// In any case, there's no need to do anything, the caller will reinitialize
// static_state.
return ds_tls;
}
};
// Templated base class meant to be derived by embedders to create a custom data
// source. DerivedDataSource must be the type of the derived class itself, e.g.:
// class MyDataSource : public DataSource<MyDataSource> {...}.
//
// |DataSourceTraits| allows customizing the behavior of the data source. See
// |DefaultDataSourceTraits|.
template <typename DerivedDataSource,
typename DataSourceTraits = DefaultDataSourceTraits>
class DataSource : public DataSourceBase {
struct DefaultTracePointTraits;
public:
// The BufferExhaustedPolicy to use for TraceWriters of this DataSource.
// Override this in your DataSource class to change the default, which is to
// drop data on shared memory overruns.
constexpr static BufferExhaustedPolicy kBufferExhaustedPolicy =
BufferExhaustedPolicy::kDrop;
// When this flag is false, we cannot have multiple instances of this data
// source. When a data source is already active and if we attempt
// to start another instance of that data source (via another tracing
// session), it will fail to start the second instance of data source.
static constexpr bool kSupportsMultipleInstances = true;
// When this flag is true, DataSource callbacks (OnSetup, OnStart, etc.) are
// called under the lock (the same that is used in GetDataSourceLocked
// function). This is not recommended because it can lead to deadlocks, but
// it was the default behavior for a long time and some embedders rely on it
// to protect concurrent access to the DataSource members. So we keep the
// "true" value as the default.
static constexpr bool kRequiresCallbacksUnderLock = true;
// Argument passed to the lambda function passed to Trace() (below).
class TraceContext {
public:
using TracePacketHandle =
::protozero::MessageHandle<::perfetto::protos::pbzero::TracePacket>;
TraceContext(TraceContext&&) noexcept = default;
~TraceContext() {
// If the data source is being intercepted, flush the trace writer after
// each trace point to make sure the interceptor sees the data right away.
if (PERFETTO_UNLIKELY(tls_inst_->is_intercepted))
Flush();
}
// Adds an empty trace packet to the trace to ensure that the service can
// safely read the last event from the trace buffer.
// See PERFETTO_INTERNAL_ADD_EMPTY_EVENT macros for context.
void AddEmptyTracePacket() {
// If nothing was written since the last empty packet, there's nothing to
// scrape, so adding more empty packets serves no purpose.
if (tls_inst_->trace_writer->written() ==
tls_inst_->last_empty_packet_position) {
return;
}
tls_inst_->trace_writer->NewTracePacket();
tls_inst_->last_empty_packet_position =
tls_inst_->trace_writer->written();
}
TracePacketHandle NewTracePacket() {
return tls_inst_->trace_writer->NewTracePacket();
}
// Forces a commit of the thread-local tracing data written so far to the
// service. This is almost never required (tracing data is periodically
// committed as trace pages are filled up) and has a non-negligible
// performance hit (requires an IPC + refresh of the current thread-local
// chunk). The only case when this should be used is when handling OnStop()
// asynchronously, to ensure sure that the data is committed before the
// Stop timeout expires.
// The TracePacketHandle obtained by the last NewTracePacket() call must be
// finalized before calling Flush() (either implicitly by going out of scope
// or by explicitly calling Finalize()).
// |cb| is an optional callback. When non-null it will request the
// service to ACK the flush and will be invoked on an internal thread after
// the service has acknowledged it. The callback might be NEVER INVOKED if
// the service crashes or the IPC connection is dropped.
void Flush(std::function<void()> cb = {}) {
tls_inst_->trace_writer->Flush(cb);
}
// Returns the number of bytes written on the current thread by the current
// data-source since its creation.
// This can be useful for splitting protos that might grow very large.
uint64_t written() { return tls_inst_->trace_writer->written(); }
// Returns a RAII handle to access the data source instance, guaranteeing
// that it won't be deleted on another thread (because of trace stopping)
// while accessing it from within the Trace() lambda.
// The returned handle can be invalid (nullptr) if tracing is stopped
// immediately before calling this. The caller is supposed to check for its
// validity before using it. After checking, the handle is guaranteed to
// remain valid until the handle goes out of scope.
LockedHandle<DerivedDataSource> GetDataSourceLocked() const {
auto* internal_state = type_.static_state()->TryGet(instance_index_);
if (!internal_state)
return LockedHandle<DerivedDataSource>();
std::unique_lock<std::recursive_mutex> lock(internal_state->lock);
return LockedHandle<DerivedDataSource>(
std::move(lock),
static_cast<DerivedDataSource*>(internal_state->data_source.get()));
}
// Post-condition: returned ptr will be non-null.
typename DataSourceTraits::TlsStateType* GetCustomTlsState() {
PERFETTO_DCHECK(tls_inst_->data_source_custom_tls);
return reinterpret_cast<typename DataSourceTraits::TlsStateType*>(
tls_inst_->data_source_custom_tls.get());
}
typename DataSourceTraits::IncrementalStateType* GetIncrementalState() {
return static_cast<typename DataSourceTraits::IncrementalStateType*>(
type_.GetIncrementalState(tls_inst_, instance_index_));
}
private:
friend class DataSource;
template <typename, const internal::TrackEventCategoryRegistry*>
friend class internal::TrackEventDataSource;
TraceContext(internal::DataSourceInstanceThreadLocalState* tls_inst,
uint32_t instance_index)
: tls_inst_(tls_inst), instance_index_(instance_index) {}
TraceContext(const TraceContext&) = delete;
TraceContext& operator=(const TraceContext&) = delete;
internal::DataSourceInstanceThreadLocalState* const tls_inst_;
uint32_t const instance_index_;
};
// The main tracing method. Tracing code should call this passing a lambda as
// argument, with the following signature: void(TraceContext).
// The lambda will be called synchronously (i.e., always before Trace()
// returns) only if tracing is enabled and the data source has been enabled in
// the tracing config.
// The lambda can be called more than once per Trace() call, in the case of
// concurrent tracing sessions (or even if the data source is instantiated
// twice within the same trace config).
template <typename Lambda>
static void Trace(Lambda tracing_fn) {
CallIfEnabled<DefaultTracePointTraits>([&tracing_fn](uint32_t instances) {
TraceWithInstances<DefaultTracePointTraits>(instances,
std::move(tracing_fn));
});
}
// An efficient trace point guard for checking if this data source is active.
// |callback| is a function which will only be called if there are active
// instances. It is given an instance state parameter, which should be passed
// to TraceWithInstances() to actually record trace data.
template <typename Traits = DefaultTracePointTraits, typename Callback>
static void CallIfEnabled(Callback callback,
typename Traits::TracePointData trace_point_data =
{}) PERFETTO_ALWAYS_INLINE {
// |instances| is a per-class bitmap that tells:
// 1. If the data source is enabled at all.
// 2. The index of the slot within
// internal::DataSourceStaticState::instances that holds the instance
// state. In turn this allows to map the data source to the tracing
// session and buffers.
// memory_order_relaxed is okay because:
// - |instances| is re-read with an acquire barrier below if this succeeds.
// - The code between this point and the acquire-load is based on static
// storage which has indefinite lifetime.
uint32_t instances = Traits::GetActiveInstances(trace_point_data)
->load(std::memory_order_relaxed);
// This is the tracing fast-path. Bail out immediately if tracing is not
// enabled (or tracing is enabled but not for this data source).
if (PERFETTO_LIKELY(!instances))
return;
callback(instances);
}
// The "lower half" of a trace point which actually performs tracing after
// this data source has been determined to be active.
// |instances| must be the instance state value retrieved through
// CallIfEnabled().
// |tracing_fn| will be called to record trace data as in Trace().
//
// |trace_point_data| is an optional parameter given to |Traits::
// GetActiveInstances| to make it possible to use custom storage for
// the data source enabled state. This is, for example, used by TrackEvent to
// implement per-tracing category enabled states.
template <typename Traits = DefaultTracePointTraits, typename Lambda>
static void TraceWithInstances(
uint32_t cached_instances,
Lambda tracing_fn,
typename Traits::TracePointData trace_point_data = {}) {
PERFETTO_DCHECK(cached_instances);
if (!type_.TracePrologue<DataSourceTraits, Traits>(
&tls_state_, &cached_instances, trace_point_data)) {
return;
}
for (internal::DataSourceType::InstancesIterator it =
type_.BeginIteration<Traits>(cached_instances, tls_state_,
trace_point_data);
it.instance;
type_.NextIteration<Traits>(&it, tls_state_, trace_point_data)) {
tracing_fn(TraceContext(it.instance, it.i));
}
type_.TraceEpilogue(tls_state_);
}
// Registers the data source on all tracing backends, including ones that
// connect after the registration. Doing so enables the data source to receive
// Setup/Start/Stop notifications and makes the Trace() method work when
// tracing is enabled and the data source is selected.
// This must be called after Tracing::Initialize().
// Can return false to signal failure if attemping to register more than
// kMaxDataSources (32) data sources types or if tracing hasn't been
// initialized.
// The optional |constructor_args| will be passed to the data source when it
// is constructed.
template <class... Args>
static bool Register(const DataSourceDescriptor& descriptor,
const Args&... constructor_args) {
// Silences -Wunused-variable warning in case the trace method is not used
// by the translation unit that declares the data source.
(void)type_;
(void)tls_state_;
auto factory = [constructor_args...]() {
return std::unique_ptr<DataSourceBase>(
new DerivedDataSource(constructor_args...));
};
internal::DataSourceParams params{
DerivedDataSource::kSupportsMultipleInstances,
DerivedDataSource::kRequiresCallbacksUnderLock};
return type_.Register(
descriptor, factory, params, DerivedDataSource::kBufferExhaustedPolicy,
GetCreateTlsFn(
static_cast<typename DataSourceTraits::TlsStateType*>(nullptr)),
GetCreateIncrementalStateFn(
static_cast<typename DataSourceTraits::IncrementalStateType*>(
nullptr)),
nullptr);
}
// Updates the data source descriptor.
static void UpdateDescriptor(const DataSourceDescriptor& descriptor) {
type_.UpdateDescriptor(descriptor);
}
private:
friend ::perfetto::test::DataSourceInternalForTest;
// Traits for customizing the behavior of a specific trace point.
struct DefaultTracePointTraits {
// By default, every call to DataSource::Trace() will record trace events
// for every active instance of that data source. A single trace point can,
// however, use a custom set of enable flags for more fine grained control
// of when that trace point is active.
//
// DANGER: when doing this, the data source must use the appropriate memory
// fences when changing the state of the bitmap.
//
// |TraceWithInstances| may be optionally given an additional parameter for
// looking up the enable flags. That parameter is passed as |TracePointData|
// to |GetActiveInstances|. This is, for example, used by TrackEvent to
// implement per-category enabled states.
struct TracePointData {};
static constexpr std::atomic<uint32_t>* GetActiveInstances(TracePointData) {
return type_.valid_instances();
}
};
template <typename T>
static internal::DataSourceInstanceThreadLocalState::ObjectWithDeleter
CreateIncrementalState(internal::DataSourceInstanceThreadLocalState*,
uint32_t,
void*) {
return internal::DataSourceInstanceThreadLocalState::ObjectWithDeleter(
reinterpret_cast<void*>(new T()),
[](void* p) { delete reinterpret_cast<T*>(p); });
}
// The second parameter here is used to specialize the case where there is no
// incremental state type.
template <typename T>
static internal::DataSourceType::CreateIncrementalStateFn
GetCreateIncrementalStateFn(const T*) {
return &CreateIncrementalState<T>;
}
static internal::DataSourceType::CreateIncrementalStateFn
GetCreateIncrementalStateFn(const void*) {
return nullptr;
}
template <typename T>
static internal::DataSourceInstanceThreadLocalState::ObjectWithDeleter
CreateDataSourceCustomTls(
internal::DataSourceInstanceThreadLocalState* tls_inst,
uint32_t instance_index,
void*) {
return internal::DataSourceInstanceThreadLocalState::ObjectWithDeleter(
reinterpret_cast<void*>(new T(TraceContext(tls_inst, instance_index))),
[](void* p) { delete reinterpret_cast<T*>(p); });
}
// The second parameter here is used to specialize the case where there is no
// tls state type.
template <typename T>
static internal::DataSourceType::CreateCustomTlsFn GetCreateTlsFn(const T*) {
return &CreateDataSourceCustomTls<T>;
}
static internal::DataSourceType::CreateCustomTlsFn GetCreateTlsFn(
const void*) {
return nullptr;
}
// The type of this data source. Accessed by the static Trace() method
// fastpaths.
static internal::DataSourceType type_;
// This TLS object is a cached raw pointer and has deliberately no destructor.
// The Platform implementation is supposed to create and manage the lifetime
// of the Platform::ThreadLocalObject and take care of destroying it.
// This is because non-POD thread_local variables have subtleties (global
// destructors) that we need to defer to the embedder. In chromium's platform
// implementation, for instance, the tls slot is implemented using
// chromium's base::ThreadLocalStorage.
static PERFETTO_THREAD_LOCAL internal::DataSourceThreadLocalState* tls_state_;
};
// static
template <typename T, typename D>
internal::DataSourceType DataSource<T, D>::type_;
// static
template <typename T, typename D>
PERFETTO_THREAD_LOCAL internal::DataSourceThreadLocalState*
DataSource<T, D>::tls_state_;
} // namespace perfetto
// If placed at the end of a macro declaration, eats the semicolon at the end of
// the macro invocation (e.g., "MACRO(...);") to avoid warnings about extra
// semicolons.
#define PERFETTO_INTERNAL_SWALLOW_SEMICOLON() \
extern int perfetto_internal_unused
// This macro must be used once for each data source next to the data source's
// declaration.
#define PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(...) \
PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS( \
PERFETTO_COMPONENT_EXPORT, __VA_ARGS__)
// Similar to `PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS` but it also takes
// custom attributes, which are useful when DataSource is defined in a component
// where a component specific export macro is used.
#define PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(attrs, ...) \
template <> \
attrs perfetto::internal::DataSourceType \
perfetto::DataSource<__VA_ARGS__>::type_
// This macro must be used once for each data source in one source file to
// allocate static storage for the data source's static state.
//
// Note: if MSVC fails with a C2086 (redefinition) error here, use the
// permissive- flag to enable standards-compliant mode. See
// https://developercommunity.visualstudio.com/content/problem/319447/
// explicit-specialization-of-static-data-member-inco.html.
#define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(...) \
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS( \
PERFETTO_COMPONENT_EXPORT, __VA_ARGS__)
// Similar to `PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS` but it also takes
// custom attributes, which are useful when DataSource is defined in a component
// where a component specific export macro is used.
#define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(attrs, ...) \
template <> \
attrs perfetto::internal::DataSourceType \
perfetto::DataSource<__VA_ARGS__>::type_ {}
#endif // INCLUDE_PERFETTO_TRACING_DATA_SOURCE_H_