blob: aee830a37420a545db55722a5b208e1eb655bbbd [file] [log] [blame]
#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_DATA_SOURCE_TYPE_H_
#define INCLUDE_PERFETTO_TRACING_INTERNAL_DATA_SOURCE_TYPE_H_
#include "perfetto/base/build_config.h"
#include "perfetto/base/export.h"
#include "perfetto/tracing/core/forward_decls.h"
#include "perfetto/tracing/internal/data_source_internal.h"
#include "perfetto/tracing/internal/tracing_muxer.h"
namespace perfetto {
namespace internal {
// Represents a data source type (not an instance).
//
// All the static state of a DataSource<T> lives here (including
// DataSourceStaticState).
//
// The C shared library API wrapper cannot use DataSource<T>, because it needs
// to create new data source types at runtime, so it uses this directly.
//
// The main reason why this intermediate class exist is to decouple the
// DataSourceStaticState from the specific DataSource<T>. The C API cannot
// dynamically create template instances and it needs a way to decouple those at
// runtime.
class PERFETTO_EXPORT_COMPONENT DataSourceType {
public:
// Function pointer type used to create custom per instance thread local
// state.
using CreateCustomTlsFn =
DataSourceInstanceThreadLocalState::ObjectWithDeleter (*)(
DataSourceInstanceThreadLocalState* tls_inst,
uint32_t instance_index,
void* user_arg);
// Function pointer type used to create custom per instance thread local
// incremental state (which might be cleared periodically by the tracing
// service).
using CreateIncrementalStateFn =
DataSourceInstanceThreadLocalState::ObjectWithDeleter (*)(
DataSourceInstanceThreadLocalState* tls_inst,
uint32_t instance_index,
void* user_arg);
// Registers the data source type with the central tracing muxer.
// * `descriptor` is the data source protobuf descriptor.
// * `factory` is a std::function used to create instances of the data source
// type.
// * `buffer_exhausted_policy` specifies what to do when the shared memory
// buffer runs out of chunks.
// * `create_custom_tls_fn` and `create_incremental_state_fn` are function
// pointers called to create custom state. They will receive `user_arg` as
// an extra param.
bool Register(const DataSourceDescriptor& descriptor,
TracingMuxer::DataSourceFactory factory,
internal::DataSourceParams params,
BufferExhaustedPolicy buffer_exhausted_policy,
CreateCustomTlsFn create_custom_tls_fn,
CreateIncrementalStateFn create_incremental_state_fn,
void* user_arg) {
buffer_exhausted_policy_ = buffer_exhausted_policy;
create_custom_tls_fn_ = create_custom_tls_fn;
create_incremental_state_fn_ = create_incremental_state_fn;
user_arg_ = user_arg;
auto* tracing_impl = TracingMuxer::Get();
return tracing_impl->RegisterDataSource(descriptor, factory, params,
&state_);
}
// Updates the data source type descriptor.
void UpdateDescriptor(const DataSourceDescriptor& descriptor) {
auto* tracing_impl = TracingMuxer::Get();
tracing_impl->UpdateDataSourceDescriptor(descriptor, &state_);
}
// The beginning of a trace point.
//
// `tls_state` must point to a thread local variable that caches a pointer to
// an internal per data source type thread local state.
//
// `instances` must point to a copy of the current active instances for the
// data source type.
//
// `DataSourceTraits` can be used to customize the thread local storage used
// for the data source type.
//
// `TracePointTraits` and `trace_point_data` are customization point for
// getting the active instances bitmap.
//
// If this returns false, the trace point must be skipped.
template <typename DataSourceTraits, typename TracePointTraits>
bool TracePrologue(
DataSourceThreadLocalState** tls_state,
uint32_t* instances,
typename TracePointTraits::TracePointData trace_point_data) {
// See tracing_muxer.h for the structure of the TLS.
if (PERFETTO_UNLIKELY(!*tls_state)) {
*tls_state = GetOrCreateDataSourceTLS<DataSourceTraits>();
// If the TLS hasn't been obtained yet, it's possible that this thread
// hasn't observed the initialization of global state like the muxer yet.
// To ensure that the thread "sees" the effects of such initialization,
// we have to reload |instances| with an acquire fence, ensuring that any
// initialization performed before instances was updated is visible
// in this thread.
*instances &= TracePointTraits::GetActiveInstances(trace_point_data)
->load(std::memory_order_acquire);
if (!*instances)
return false;
}
auto* tracing_impl = TracingMuxer::Get();
// Avoid re-entering the trace point recursively.
if (PERFETTO_UNLIKELY((*tls_state)->root_tls->is_in_trace_point))
return false;
(*tls_state)->root_tls->is_in_trace_point = true;
// TracingTLS::generation is a global monotonic counter that is incremented
// every time a tracing session is stopped. We use that as a signal to force
// a slow-path garbage collection of all the trace writers for the current
// thread and to destroy the ones that belong to tracing sessions that have
// ended. This is to avoid having too many TraceWriter instances alive, each
// holding onto one chunk of the shared memory buffer.
// Rationale why memory_order_relaxed should be fine:
// - The TraceWriter object that we use is always constructed and destructed
// on the current thread. There is no risk of accessing a half-initialized
// TraceWriter (which would be really bad).
// - In the worst case, in the case of a race on the generation check, we
// might end up using a TraceWriter for the same data source that belongs
// to a stopped session. This is not really wrong, as we don't give any
// guarantee on the global atomicity of the stop. In the worst case the
// service will reject the data commit if this arrives too late.
if (PERFETTO_UNLIKELY(
(*tls_state)->root_tls->generation !=
tracing_impl->generation(std::memory_order_relaxed))) {
// Will update root_tls->generation.
tracing_impl->DestroyStoppedTraceWritersForCurrentThread();
}
return true;
}
// Must be called at the ending of a trace point that was not skipped.
void TraceEpilogue(DataSourceThreadLocalState* tls_state) {
tls_state->root_tls->is_in_trace_point = false;
}
struct InstancesIterator {
// A bitmap of the currenly active instances.
uint32_t cached_instances;
// The current instance index.
uint32_t i;
// The current instance. If this is `nullptr`, the iteration is over.
DataSourceInstanceThreadLocalState* instance;
};
// Returns an iterator to the active instances of this data source type.
//
// `cached_instances` is a copy of the bitmap of the active instances for this
// data source type (usually just a copy of ValidInstances(), but can be
// customized).
//
// `tls_state` is the thread local pointer obtained from TracePrologue.
//
// `TracePointTraits` and `trace_point_data` are customization point for
// getting the active instances bitmap.
template <typename TracePointTraits>
InstancesIterator BeginIteration(
uint32_t cached_instances,
DataSourceThreadLocalState* tls_state,
typename TracePointTraits::TracePointData trace_point_data)
PERFETTO_ALWAYS_INLINE {
InstancesIterator it{};
it.cached_instances = cached_instances;
FirstActiveInstance<TracePointTraits>(&it, tls_state, trace_point_data);
return it;
}
// Advances `*iterator` to point to the next active instance of this data
// source type.
//
// `tls_state` is the thread local pointer obtained from TracePrologue.
//
// `TracePointTraits` and `trace_point_data` are customization point for
// getting the active instances bitmap.
template <typename TracePointTraits>
void NextIteration(InstancesIterator* iterator,
DataSourceThreadLocalState* tls_state,
typename TracePointTraits::TracePointData trace_point_data)
PERFETTO_ALWAYS_INLINE {
iterator->i++;
FirstActiveInstance<TracePointTraits>(iterator, tls_state,
trace_point_data);
}
void* GetIncrementalState(
internal::DataSourceInstanceThreadLocalState* tls_inst,
uint32_t instance_index) {
// Recreate incremental state data if it has been reset by the service.
if (tls_inst->incremental_state_generation !=
static_state()->incremental_state_generation.load(
std::memory_order_relaxed)) {
tls_inst->incremental_state.reset();
CreateIncrementalState(tls_inst, instance_index);
}
return tls_inst->incremental_state.get();
}
std::atomic<uint32_t>* valid_instances() { return &state_.valid_instances; }
DataSourceStaticState* static_state() { return &state_; }
private:
void CreateIncrementalState(
internal::DataSourceInstanceThreadLocalState* tls_inst,
uint32_t instance_index) {
PERFETTO_DCHECK(create_incremental_state_fn_ != nullptr);
tls_inst->incremental_state =
create_incremental_state_fn_(tls_inst, instance_index, user_arg_);
tls_inst->incremental_state_generation =
static_state()->incremental_state_generation.load(
std::memory_order_relaxed);
}
void PopulateTlsInst(DataSourceInstanceThreadLocalState* tls_inst,
DataSourceState* instance_state,
uint32_t instance_index);
// Advances `*iterator` to the first active instance whose index is greater or
// equal than `iterator->i`.
template <typename TracePointTraits>
void FirstActiveInstance(
InstancesIterator* iterator,
DataSourceThreadLocalState* tls_state,
typename TracePointTraits::TracePointData trace_point_data) {
iterator->instance = nullptr;
for (; iterator->i < kMaxDataSourceInstances; iterator->i++) {
DataSourceState* instance_state =
state_.TryGetCached(iterator->cached_instances, iterator->i);
if (!instance_state)
continue;
// Even if we passed the check above, the DataSourceInstance might be
// still destroyed concurrently while this code runs. The code below is
// designed to deal with such race, as follows:
// - We don't access the user-defined data source instance state. The only
// bits of state we use are |backend_id| and |buffer_id|.
// - Beyond those two integers, we access only the TraceWriter here. The
// TraceWriter is always safe because it lives on the TLS.
// - |instance_state| is backed by static storage, so the pointer is
// always valid, even after the data source instance is destroyed.
// - In the case of a race-on-destruction, we'll still see the latest
// backend_id and buffer_id and in the worst case keep trying writing
// into the tracing shared memory buffer after stopped. But this isn't
// really any worse than the case of the stop IPC being delayed by the
// kernel scheduler. The tracing service is robust against data commit
// attemps made after tracing is stopped.
// There is a theoretical race that would case the wrong behavior w.r.t
// writing data in the wrong buffer, but it's so rare that we ignore it:
// if the data source is stopped and started kMaxDataSourceInstances
// times (so that the same id is recycled) while we are in this function,
// we might end up reusing the old data source's backend_id and buffer_id
// for the new one, because we don't see the generation change past this
// point. But stopping and starting tracing (even once) takes so much
// handshaking to make this extremely unrealistic.
auto& tls_inst = tls_state->per_instance[iterator->i];
if (PERFETTO_UNLIKELY(!tls_inst.trace_writer)) {
// Here we need an acquire barrier, which matches the release-store made
// by TracingMuxerImpl::SetupDataSource(), to ensure that the backend_id
// and buffer_id are consistent.
iterator->cached_instances &=
TracePointTraits::GetActiveInstances(trace_point_data)
->load(std::memory_order_acquire);
instance_state =
state_.TryGetCached(iterator->cached_instances, iterator->i);
if (!instance_state || !instance_state->trace_lambda_enabled.load(
std::memory_order_relaxed))
continue;
PopulateTlsInst(&tls_inst, instance_state, iterator->i);
}
iterator->instance = &tls_inst;
break;
}
}
// Note that the returned object is one per-thread per-data-source-type, NOT
// per data-source *instance*.
template <typename DataSourceTraits>
DataSourceThreadLocalState* GetOrCreateDataSourceTLS() {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_IOS)
PERFETTO_FATAL("Data source TLS not supported on iOS, see b/158814068");
#endif
auto* tracing_impl = TracingMuxer::Get();
TracingTLS* root_tls = tracing_impl->GetOrCreateTracingTLS();
DataSourceThreadLocalState* ds_tls =
DataSourceTraits::GetDataSourceTLS(&state_, root_tls);
// We keep re-initializing as the initialization is idempotent and not worth
// the code for extra checks. Also, ds_tls->static_state might point to
// another data source if ResetForTesting() has been used.
ds_tls->static_state = &state_;
assert(!ds_tls->root_tls || ds_tls->root_tls == root_tls);
ds_tls->root_tls = root_tls;
return ds_tls;
}
DataSourceStaticState state_;
BufferExhaustedPolicy buffer_exhausted_policy_{};
CreateCustomTlsFn create_custom_tls_fn_ = nullptr;
CreateIncrementalStateFn create_incremental_state_fn_ = nullptr;
// User defined pointer that carries extra content for the fn_ callbacks
// above. Only used in the C shared library.
void* user_arg_ = nullptr;
};
} // namespace internal
} // namespace perfetto
#endif // INCLUDE_PERFETTO_TRACING_INTERNAL_DATA_SOURCE_TYPE_H_