TrackEvent: Add support for dynamically enabled tracing categories
This patch introduces support for annotating track events with named
tracing categories. Each category can be individually enabled or
disabled using a trace config.
The set of categories is defined at compile time using a macro:
PERFETTO_DEFINE_CATEGORIES(
PERFETTO_CATEGORY(cat1),
PERFETTO_CATEGORY(cat2),
PERFETTO_CATEGORY(cat3));
We also introduce a set of macros for efficiently emitting track events
with category annotation:
TRACE_EVENT_BEGIN("cat1", "EventName");
TRACE_EVENT_END();
The trace point implementation replaces the per-data source instance
enable bitmap with a per-category bitmap in order to avoid doing any
extra work for categories that aren't enabled for tracing.
Bug: 132678367
Change-Id: I84cdb13fc2608a23f311f49a3402640c92199909
diff --git a/Android.bp b/Android.bp
index 3b14ab4..bb3f96b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4974,9 +4974,10 @@
"src/tracing/internal/in_process_tracing_backend.cc",
"src/tracing/internal/system_tracing_backend.cc",
"src/tracing/internal/tracing_muxer_impl.cc",
+ "src/tracing/internal/track_event_internal.cc",
"src/tracing/platform.cc",
"src/tracing/tracing.cc",
- "src/tracing/track_event.cc",
+ "src/tracing/track_event_category_registry.cc",
"src/tracing/virtual_destructors.cc",
],
}
@@ -4986,6 +4987,8 @@
name: "perfetto_src_tracing_client_api_integrationtests",
srcs: [
"src/tracing/api_integrationtest.cc",
+ "src/tracing/test/tracing_module.cc",
+ "src/tracing/test/tracing_module2.cc",
],
}
diff --git a/BUILD b/BUILD
index 587d616..f1f8f15 100644
--- a/BUILD
+++ b/BUILD
@@ -400,12 +400,15 @@
"include/perfetto/tracing/internal/tracing_muxer.h",
"include/perfetto/tracing/internal/tracing_tls.h",
"include/perfetto/tracing/internal/track_event_data_source.h",
+ "include/perfetto/tracing/internal/track_event_internal.h",
+ "include/perfetto/tracing/internal/track_event_macros.h",
"include/perfetto/tracing/locked_handle.h",
"include/perfetto/tracing/platform.h",
"include/perfetto/tracing/trace_writer_base.h",
"include/perfetto/tracing/tracing.h",
"include/perfetto/tracing/tracing_backend.h",
"include/perfetto/tracing/track_event.h",
+ "include/perfetto/tracing/track_event_category_registry.h",
],
)
@@ -940,9 +943,10 @@
"src/tracing/internal/system_tracing_backend.h",
"src/tracing/internal/tracing_muxer_impl.cc",
"src/tracing/internal/tracing_muxer_impl.h",
+ "src/tracing/internal/track_event_internal.cc",
"src/tracing/platform.cc",
"src/tracing/tracing.cc",
- "src/tracing/track_event.cc",
+ "src/tracing/track_event_category_registry.cc",
"src/tracing/virtual_destructors.cc",
],
)
diff --git a/include/perfetto/base/compiler.h b/include/perfetto/base/compiler.h
index a4cb6ec..fa44b6b 100644
--- a/include/perfetto/base/compiler.h
+++ b/include/perfetto/base/compiler.h
@@ -30,10 +30,12 @@
#if defined(__clang__)
#define PERFETTO_ALWAYS_INLINE __attribute__((__always_inline__))
+#define PERFETTO_NO_INLINE __attribute__((__noinline__))
#else
// GCC is too pedantic and often fails with the error:
// "always_inline function might not be inlinable"
#define PERFETTO_ALWAYS_INLINE
+#define PERFETTO_NO_INLINE
#endif
// TODO(lalitm): is_trivially_constructible is currently not available
diff --git a/include/perfetto/tracing/BUILD.gn b/include/perfetto/tracing/BUILD.gn
index 8be1aa8..289abf3 100644
--- a/include/perfetto/tracing/BUILD.gn
+++ b/include/perfetto/tracing/BUILD.gn
@@ -29,11 +29,14 @@
"internal/tracing_muxer.h",
"internal/tracing_tls.h",
"internal/track_event_data_source.h",
+ "internal/track_event_internal.h",
+ "internal/track_event_macros.h",
"locked_handle.h",
"platform.h",
"trace_writer_base.h",
"tracing.h",
"tracing_backend.h",
"track_event.h",
+ "track_event_category_registry.h",
]
}
diff --git a/include/perfetto/tracing/data_source.h b/include/perfetto/tracing/data_source.h
index c6fa774..312ba34 100644
--- a/include/perfetto/tracing/data_source.h
+++ b/include/perfetto/tracing/data_source.h
@@ -44,6 +44,9 @@
#include "protos/perfetto/trace/trace_packet.pbzero.h"
namespace perfetto {
+namespace internal {
+class TracingMuxerImpl;
+} // namespace internal
class DataSourceConfig;
@@ -65,10 +68,17 @@
// 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 {};
+ 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 {
@@ -94,20 +104,49 @@
// 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&);
};
+struct DefaultDataSourceTraits {
+ // |IncrementalStateType| can optionally be used store custom per-sequence
+ // incremental data (e.g., interning tables). It should have a Clear() method
+ // for when incremental state needs to be cleared. See
+ // TraceContext::GetIncrementalState().
+ using IncrementalStateType = 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];
+ // The per-type TLS is either zero-initialized or must have been initialized
+ // for this specific data source type.
+ assert(!ds_tls->static_state ||
+ ds_tls->static_state->index == static_state->index);
+ return ds_tls;
+ }
+};
+
// Templated base class meant to be derived by embedders to create a custom data
// source. DataSourceType must be the type of the derived class itself, e.g.:
// class MyDataSource : public DataSourceBase<MyDataSource> {...}.
//
-// |IncrementalStateType| can optionally be used store custom per-sequence
-// incremental data (e.g., interning tables). It should have a Clear() method
-// for when incremental state needs to be cleared. See
-// TraceContext::GetIncrementalState().
-template <typename DataSourceType, typename IncrementalStateType = void>
+// |DataSourceTraits| allows customizing the behavior of the data source. See
+// |DefaultDataSourceTraits|.
+template <typename DataSourceType,
+ 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
@@ -162,11 +201,13 @@
static_cast<DataSourceType*>(internal_state->data_source.get()));
}
- IncrementalStateType* GetIncrementalState() {
- return reinterpret_cast<IncrementalStateType*>(
+ typename DataSourceTraits::IncrementalStateType* GetIncrementalState() {
+ return reinterpret_cast<typename DataSourceTraits::IncrementalStateType*>(
tls_inst_->incremental_state.get());
}
+ uint32_t internal_instance_index() const { return instance_index_; }
+
private:
friend class DataSource;
TraceContext(internal::DataSourceInstanceThreadLocalState* tls_inst,
@@ -189,33 +230,55 @@
// twice within the same trace config).
template <typename Lambda>
static void Trace(Lambda tracing_fn) {
- constexpr auto kMaxDataSourceInstances = internal::kMaxDataSourceInstances;
+ 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) 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 |valid_instances| that holds the instance
+ // 2. The index of the slot within |static_state_| 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.
- auto instances =
- static_state_.valid_instances.load(std::memory_order_relaxed);
+ uint32_t instances =
+ Traits::GetActiveInstances()->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);
+ }
- // TODO(primiano): all the stuff below should be outlined. Or at least
- // we should have some compile-time traits like kOptimizeBinarySize /
- // kOptimizeTracingLatency.
+ // 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().
+ //
+ // TODO(primiano): all the stuff below should be outlined from the trace
+ // point. Or at least we should have some compile-time traits like
+ // kOptimizeBinarySize / kOptimizeTracingLatency.
+ template <typename Traits = DefaultTracePointTraits, typename Lambda>
+ static void TraceWithInstances(uint32_t instances, Lambda tracing_fn) {
+ PERFETTO_DCHECK(instances);
+ constexpr auto kMaxDataSourceInstances = internal::kMaxDataSourceInstances;
// See tracing_muxer.h for the structure of the TLS.
auto* tracing_impl = internal::TracingMuxer::Get();
if (PERFETTO_UNLIKELY(!tls_state_))
- tls_state_ = tracing_impl->GetOrCreateDataSourceTLS(&static_state_);
+ tls_state_ = GetOrCreateDataSourceTLS(&static_state_);
// 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
@@ -276,7 +339,7 @@
// by TracingMuxerImpl::SetupDataSource(), to ensure that the backend_id
// and buffer_id are consistent.
instances =
- static_state_.valid_instances.load(std::memory_order_acquire);
+ Traits::GetActiveInstances()->load(std::memory_order_acquire);
instance_state = static_state_.TryGetCached(instances, i);
if (!instance_state || !instance_state->trace_lambda_enabled)
return;
@@ -284,8 +347,10 @@
tls_inst.buffer_id = instance_state->buffer_id;
tls_inst.trace_writer = tracing_impl->CreateTraceWriter(
instance_state, DataSourceType::kBufferExhaustedPolicy);
- CreateIncrementalState(&tls_inst,
- static_cast<IncrementalStateType*>(nullptr));
+ CreateIncrementalState(
+ &tls_inst,
+ static_cast<typename DataSourceTraits::IncrementalStateType*>(
+ nullptr));
// Even in the case of out-of-IDs, SharedMemoryArbiterImpl returns a
// NullTraceWriter. The returned pointer should never be null.
@@ -320,6 +385,20 @@
}
private:
+ // 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.
+ static constexpr std::atomic<uint32_t>* GetActiveInstances() {
+ return &static_state_.valid_instances;
+ }
+ };
+
// Create the user provided incremental state in the given thread-local
// storage. Note: The second parameter here is used to specialize the case
// where there is no incremental state type.
@@ -337,6 +416,22 @@
internal::DataSourceInstanceThreadLocalState*,
const void*) {}
+ // Note that the returned object is one per-thread per-data-source-type, NOT
+ // per data-source *instance*.
+ static internal::DataSourceThreadLocalState* GetOrCreateDataSourceTLS(
+ internal::DataSourceStaticState* static_state) {
+ auto* tracing_impl = internal::TracingMuxer::Get();
+ internal::TracingTLS* root_tls = tracing_impl->GetOrCreateTracingTLS();
+ internal::DataSourceThreadLocalState* ds_tls =
+ DataSourceTraits::GetDataSourceTLS(static_state, root_tls);
+ // We keep re-initializing as the initialization is idempotent and not worth
+ // the code for extra checks.
+ ds_tls->static_state = static_state;
+ assert(!ds_tls->root_tls || ds_tls->root_tls == root_tls);
+ ds_tls->root_tls = root_tls;
+ return ds_tls;
+ }
+
// Static state. Accessed by the static Trace() method fastpaths.
static internal::DataSourceStaticState static_state_;
@@ -350,17 +445,16 @@
static thread_local internal::DataSourceThreadLocalState* tls_state_;
};
+template <typename T, typename D>
+internal::DataSourceStaticState DataSource<T, D>::static_state_;
+template <typename T, typename D>
+thread_local internal::DataSourceThreadLocalState* DataSource<T, D>::tls_state_;
+
} // namespace perfetto
-// If a data source is used across translation units, this declaration must be
-// placed into the header file defining the data source.
-#define PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(...) \
- template <> \
- perfetto::internal::DataSourceStaticState \
- perfetto::DataSource<__VA_ARGS__>::static_state_; \
- template <> \
- thread_local perfetto::internal::DataSourceThreadLocalState* \
- perfetto::DataSource<__VA_ARGS__>::tls_state_
+// Not needed -- only here for backwards compatibility.
+// TODO(skyostil): Remove this macro.
+#define PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(...)
// The API client must use this in a translation unit. This is because it needs
// to instantiate the static storage for the datasource to allow the fastpath
diff --git a/include/perfetto/tracing/internal/data_source_internal.h b/include/perfetto/tracing/internal/data_source_internal.h
index 6a373f7..8e06ee4 100644
--- a/include/perfetto/tracing/internal/data_source_internal.h
+++ b/include/perfetto/tracing/internal/data_source_internal.h
@@ -75,6 +75,11 @@
// Only the tuple (backend_id, data_source_instance_id) is globally unique.
uint64_t data_source_instance_id = 0;
+ // A hash of the trace config used by this instance. This is used to
+ // de-duplicate instances for data sources with identical names (e.g., track
+ // event).
+ uint64_t config_hash = 0;
+
// This lock is not held to implement Trace() and it's used only if the trace
// code wants to access its own data source state.
// This is to prevent that accessing the data source on an arbitrary embedder
@@ -93,8 +98,8 @@
// Per-DataSource-type global state.
struct DataSourceStaticState {
- uint32_t index =
- kMaxDataSources; // Unique ID, assigned at registration time.
+ // Unique index of the data source, assigned at registration time.
+ uint32_t index = kMaxDataSources;
// A bitmap that tells about the validity of each |instances| entry. When the
// i-th bit of the bitmap it's set, instances[i] is valid.
diff --git a/include/perfetto/tracing/internal/tracing_muxer.h b/include/perfetto/tracing/internal/tracing_muxer.h
index 6cc7187..4556ed3 100644
--- a/include/perfetto/tracing/internal/tracing_muxer.h
+++ b/include/perfetto/tracing/internal/tracing_muxer.h
@@ -57,23 +57,6 @@
return static_cast<TracingTLS*>(platform_->GetOrCreateThreadLocalObject());
}
- // Note that the returned object is one per-thread per-data-source-type, NOT
- // per data-soruce *instance*.
- DataSourceThreadLocalState* GetOrCreateDataSourceTLS(
- DataSourceStaticState* static_state) {
- TracingTLS* root_tls = GetOrCreateTracingTLS();
- auto* ds_tls = &root_tls->data_sources_tls[static_state->index];
-
- // The per-type TLS is either zero-initialized or must have been initialized
- // for this specific data source type. We keep re-initializing as the
- // initialization is idempotent and not worth the code for extra checks.
- assert(!ds_tls->static_state || ds_tls->static_state == static_state);
- ds_tls->static_state = static_state;
- assert(!ds_tls->root_tls || ds_tls->root_tls == root_tls);
- ds_tls->root_tls = root_tls;
- return ds_tls;
- }
-
// This method can fail and return false if trying to register more than
// kMaxDataSources types.
using DataSourceFactory = std::function<std::unique_ptr<DataSourceBase>()>;
diff --git a/include/perfetto/tracing/internal/tracing_tls.h b/include/perfetto/tracing/internal/tracing_tls.h
index 8c39dfa..67f0637 100644
--- a/include/perfetto/tracing/internal/tracing_tls.h
+++ b/include/perfetto/tracing/internal/tracing_tls.h
@@ -74,7 +74,14 @@
// thread-local TraceWriter(s) is issued.
uint32_t generation = 0;
+ // By default all data source instances have independent thread-local state
+ // (see above).
std::array<DataSourceThreadLocalState, kMaxDataSources> data_sources_tls{};
+
+ // Track event data sources, however, share the same thread-local state in
+ // order to be able to share trace writers and interning state across all
+ // track event categories.
+ DataSourceThreadLocalState track_event_tls{};
};
} // namespace internal
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index a2a8199..742e438 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -17,61 +17,105 @@
#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_
#define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_
+#include "perfetto/base/compiler.h"
+#include "perfetto/protozero/message_handle.h"
#include "perfetto/tracing/data_source.h"
+#include "perfetto/tracing/internal/track_event_internal.h"
+#include "perfetto/tracing/track_event_category_registry.h"
#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
#include <unordered_map>
namespace perfetto {
-class TrackEvent;
-
namespace internal {
-struct TrackEventIncrementalState {
- bool was_cleared = true;
+struct TrackEventDataSourceTraits : public perfetto::DefaultDataSourceTraits {
+ using IncrementalStateType = TrackEventIncrementalState;
- // Interned data.
- // TODO(skyostil): Replace this with something more clever that supports
- // dynamic strings too.
- std::unordered_map<const char*, uint64_t> event_names;
- std::unordered_map<const char*, uint64_t> categories;
+ // Use a one shared TLS slot so that all track event data sources write into
+ // the same sequence and share interning dictionaries.
+ static DataSourceThreadLocalState* GetDataSourceTLS(DataSourceStaticState*,
+ TracingTLS* root_tls) {
+ return &root_tls->track_event_tls;
+ }
};
+// A generic track event data source which is instantiated once per track event
+// category namespace.
+template <typename DataSourceType, const TrackEventCategoryRegistry* Registry>
class TrackEventDataSource
- : public DataSource<TrackEventDataSource, TrackEventIncrementalState> {
+ : public DataSource<DataSourceType, TrackEventDataSourceTraits> {
+ using Base = DataSource<DataSourceType, TrackEventDataSourceTraits>;
+
public:
- void OnSetup(const SetupArgs&) override;
- void OnStart(const StartArgs&) override;
- void OnStop(const StopArgs&) override;
-
- private:
- friend class perfetto::TrackEvent;
-
- static void WriteEvent(const char* category,
- const char* name,
- perfetto::protos::pbzero::TrackEvent::Type type) {
- Trace([category, name, type](TraceContext ctx) {
- WriteEventImpl(std::move(ctx), category, name, type);
- });
+ // DataSource implementation.
+ void OnSetup(const DataSourceBase::SetupArgs& args) override {
+ TrackEventInternal::EnableTracing(*Registry, *args.config,
+ args.internal_instance_index);
}
- // Outlined to reduce binary size.
- static void WriteEventImpl(TraceContext ctx,
- const char* category,
- const char* name,
- perfetto::protos::pbzero::TrackEvent::Type type);
+ void OnStart(const DataSourceBase::StartArgs&) override {}
- static void WriteSequenceDescriptors(
- internal::TrackEventDataSource::TraceContext*,
- uint64_t timestamp);
+ void OnStop(const DataSourceBase::StopArgs& args) override {
+ TrackEventInternal::DisableTracing(*Registry, args.internal_instance_index);
+ }
+
+ static void Flush() {
+ Base::template Trace([](typename Base::TraceContext ctx) { ctx.Flush(); });
+ }
+
+ // This is the inlined entrypoint for all track event trace points. It tries
+ // to be as lightweight as possible in terms of instructions and aims to
+ // compile down to an unlikely conditional jump to the actual trace writing
+ // function.
+ template <size_t CategoryIndex, typename Callback>
+ static void CallIfCategoryEnabled(Callback callback) PERFETTO_ALWAYS_INLINE {
+ Base::template CallIfEnabled<CategoryTracePointTraits<CategoryIndex>>(
+ [&callback](uint32_t instances) { callback(instances); });
+ }
+
+ // Once we've determined tracing to be enabled for this category, actually
+ // write a trace event. Outlined to avoid bloating code at the actual trace
+ // point.
+ // TODO(skyostil): Investigate whether this should be fully outlined to reduce
+ // binary size.
+ template <size_t CategoryIndex>
+ static void TraceForCategory(uint32_t instances,
+ const char* event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type)
+ PERFETTO_NO_INLINE {
+ Base::template TraceWithInstances<CategoryTracePointTraits<CategoryIndex>>(
+ instances, [&](typename Base::TraceContext ctx) {
+ TrackEventTraceContext track_event_ctx(
+ ctx.GetIncrementalState(),
+ [&ctx]() { return ctx.NewTracePacket(); });
+ // TODO(skyostil): Intern categories at compile time.
+ TrackEventInternal::WriteEvent(
+ &track_event_ctx, Registry->GetCategory(CategoryIndex)->name,
+ event_name, type);
+ });
+ }
+
+ static bool Register() {
+ TrackEventInternal::Initialize();
+ perfetto::DataSourceDescriptor dsd;
+ // TODO(skyostil): Advertise the known categories.
+ dsd.set_name("track_event");
+ return Base::Register(dsd);
+ }
+
+ private:
+ // Each category has its own enabled/disabled state, stored in the category
+ // registry.
+ template <size_t CategoryIndex>
+ struct CategoryTracePointTraits {
+ static constexpr std::atomic<uint8_t>* GetActiveInstances() {
+ return Registry->GetCategoryState(CategoryIndex);
+ }
+ };
};
} // namespace internal
-
} // namespace perfetto
-PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(
- perfetto::internal::TrackEventDataSource,
- perfetto::internal::TrackEventIncrementalState);
-
#endif // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
new file mode 100644
index 0000000..5f594fb
--- /dev/null
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -0,0 +1,84 @@
+/*
+ * 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_INTERNAL_TRACK_EVENT_INTERNAL_H_
+#define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNAL_H_
+
+#include "perfetto/protozero/message_handle.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
+
+#include <unordered_map>
+
+namespace perfetto {
+class DataSourceConfig;
+
+namespace internal {
+class TrackEventCategoryRegistry;
+
+struct TrackEventIncrementalState {
+ bool was_cleared = true;
+
+ // Interned data.
+ // TODO(skyostil): Replace this with something more clever that supports
+ // dynamic strings too.
+ std::unordered_map<const char*, uint64_t> event_names;
+ std::unordered_map<const char*, uint64_t> categories;
+};
+
+class TrackEventTraceContext {
+ public:
+ using TracePacketHandle =
+ ::protozero::MessageHandle<::perfetto::protos::pbzero::TracePacket>;
+ using TracePacketCreator = std::function<TracePacketHandle()>;
+
+ TrackEventTraceContext(TrackEventIncrementalState* incremental_state,
+ TracePacketCreator new_trace_packet);
+
+ TrackEventIncrementalState* incremental_state() const {
+ return incremental_state_;
+ }
+
+ TracePacketHandle NewTracePacket();
+
+ private:
+ TrackEventIncrementalState* incremental_state_;
+ TracePacketCreator new_trace_packet_;
+};
+
+// The backend portion of the track event trace point implemention. Outlined to
+// a separate .cc file so it can be shared by different track event category
+// namespaces.
+class TrackEventInternal {
+ public:
+ static void Initialize();
+
+ static void EnableTracing(const TrackEventCategoryRegistry& registry,
+ const DataSourceConfig& config,
+ uint32_t instance_index);
+ static void DisableTracing(const TrackEventCategoryRegistry& registry,
+ uint32_t instance_index);
+
+ static void WriteEvent(TrackEventTraceContext*,
+ const char* category,
+ const char* name,
+ perfetto::protos::pbzero::TrackEvent::Type);
+};
+
+} // namespace internal
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNAL_H_
diff --git a/include/perfetto/tracing/internal/track_event_macros.h b/include/perfetto/tracing/internal/track_event_macros.h
new file mode 100644
index 0000000..915176c
--- /dev/null
+++ b/include/perfetto/tracing/internal/track_event_macros.h
@@ -0,0 +1,98 @@
+/*
+ * 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_INTERNAL_TRACK_EVENT_MACROS_H_
+#define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_MACROS_H_
+
+// This file contains underlying macros for the trace point track event
+// implementation. Perfetto API users typically don't need to use anything here
+// directly.
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/tracing/internal/track_event_data_source.h"
+#include "perfetto/tracing/track_event_category_registry.h"
+
+// Defines data structures for backing a category registry.
+//
+// Each category has one enabled/disabled bit per possible data source instance.
+// The bits are packed, i.e., each byte holds the state for instances. To
+// improve cache locality, the bits for each instance are stored separately from
+// the names of the categories:
+//
+// byte 0 byte 1
+// (inst0, inst1, ..., inst7), (inst0, inst1, ..., inst7)
+//
+#define PERFETTO_INTERNAL_DECLARE_CATEGORIES(...) \
+ namespace internal { \
+ constexpr ::perfetto::internal::TrackEventCategory kCategories[] = { \
+ __VA_ARGS__}; \
+ constexpr size_t kCategoryCount = \
+ sizeof(kCategories) / sizeof(kCategories[0]); \
+ /* The per-instance enable/disable state per category */ \
+ extern std::atomic<uint8_t> g_category_state_storage[kCategoryCount]; \
+ /* The category registry which mediates access to the above structures. */ \
+ /* The registry is used for two purposes: */ \
+ /**/ \
+ /* 1) For looking up categories at build (constexpr) time. */ \
+ /* 2) For declaring the per-namespace TrackEvent data source. */ \
+ /**/ \
+ /* Because usage #1 requires a constexpr type and usage #2 requires an */ \
+ /* extern type (to avoid declaring a type based on a translation-unit */ \
+ /* variable), we need two separate copies of the registry with different */ \
+ /* storage specifiers. */ \
+ /**/ \
+ /* TODO(skyostil): Unify these using a C++17 inline constexpr variable. */ \
+ constexpr ::perfetto::internal::TrackEventCategoryRegistry \
+ kConstExprCategoryRegistry(kCategoryCount, \
+ &kCategories[0], \
+ &g_category_state_storage[0]); \
+ extern const ::perfetto::internal::TrackEventCategoryRegistry \
+ kCategoryRegistry; \
+ } // namespace internal
+
+// In a .cc file, declares storage for each category's runtime state.
+#define PERFETTO_INTERNAL_CATEGORY_STORAGE() \
+ namespace internal { \
+ std::atomic<uint8_t> g_category_state_storage[kCategoryCount]; \
+ constexpr ::perfetto::internal::TrackEventCategoryRegistry \
+ kCategoryRegistry(kCategoryCount, \
+ &kCategories[0], \
+ &g_category_state_storage[0]); \
+ } // namespace internal
+
+// Defines the TrackEvent data source for the current track event namespace.
+#define PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE() \
+ struct TrackEvent : public ::perfetto::internal::TrackEventDataSource< \
+ TrackEvent, &internal::kCategoryRegistry> {}
+
+// At compile time, turns a category name represented by a static string into an
+// index into the current category registry. A build error will be generated if
+// the category hasn't been registered. See PERFETTO_DEFINE_CATEGORIES.
+#define PERFETTO_GET_CATEGORY_INDEX(category) \
+ ::perfetto::internal::TrackEventCategoryRegistry::Validate< \
+ ::PERFETTO_TRACK_EVENT_NAMESPACE::internal::kConstExprCategoryRegistry \
+ .Find(category)>()
+
+// Efficiently determines whether tracing is enabled for the given category, and
+// if so, emits one trace event with the given arguments.
+#define PERFETTO_INTERNAL_TRACK_EVENT(category, ...) \
+ ::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::CallIfCategoryEnabled< \
+ PERFETTO_GET_CATEGORY_INDEX(category)>([&](uint32_t instances) { \
+ ::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::TraceForCategory< \
+ PERFETTO_GET_CATEGORY_INDEX(category)>(instances, __VA_ARGS__); \
+ })
+
+#endif // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_MACROS_H_
diff --git a/include/perfetto/tracing/track_event.h b/include/perfetto/tracing/track_event.h
index 3c6dee1..e6e9689 100644
--- a/include/perfetto/tracing/track_event.h
+++ b/include/perfetto/tracing/track_event.h
@@ -19,57 +19,98 @@
#include "perfetto/base/time.h"
#include "perfetto/tracing/internal/track_event_data_source.h"
+#include "perfetto/tracing/internal/track_event_macros.h"
+#include "perfetto/tracing/track_event_category_registry.h"
#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
-namespace perfetto {
+// This file contains a set of macros designed for instrumenting applications
+// with track event trace points. While the underlying TrackEvent API can also
+// be used directly, doing so efficiently requires some care (e.g., to avoid
+// evaluating arguments while tracing is disabled). These types of optimizations
+// are abstracted away by the macros below.
+//
+// ================
+// Quickstart guide
+// ================
+//
+// To add track events to your application, first define your categories in,
+// e.g., my_tracing.h:
+//
+// PERFETTO_DEFINE_CATEGORIES(
+// PERFETTO_CATEGORY(base),
+// PERFETTO_CATEGORY(v8),
+// PERFETTO_CATEGORY(cc));
+//
+// Then in a single .cc file, e.g., my_tracing.cc:
+//
+// #include "my_tracing.h"
+// PERFETTO_TRACK_EVENT_STATIC_STORAGE();
+//
+// Finally, register track events at startup, after which you can record
+// events with the TRACE_EVENT macros:
+//
+// #include "my_tracing.h"
+//
+// int main() {
+// perfetto::TrackEvent::Register();
+// TRACK_EVENT_BEGIN("category", "MyEvent");
+// TRACK_EVENT_END("category");
+// ...
+// }
-// Track events are time-based markers that an application can use to construct
-// a timeline of its operation.
-class TrackEvent {
- public:
- // Initializes the track event data source. Must be called before any other
- // method on this class.
- static void Initialize();
+// Each compilation unit can be in exactly one track event namespace,
+// allowing the overall program to use multiple track event data sources and
+// category lists if necessary. Use this macro to select the namespace for the
+// current compilation unit.
+//
+// If the program uses multiple track event namespaces, category & track event
+// registration (see quickstart above) needs to happen for both namespaces
+// separately.
+#ifndef PERFETTO_TRACK_EVENT_NAMESPACE
+#define PERFETTO_TRACK_EVENT_NAMESPACE perfetto
+#endif
- // Returns the current tracing clock in nanoseconds.
- static uint64_t GetTimeNs() {
- // TODO(skyostil): Consider using boot time where available.
- return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
- }
+// A name for a single category. Wrapped in a macro in case we need to introduce
+// more fields in the future.
+#define PERFETTO_CATEGORY(name) \
+ { #name }
- // Begin a slice on the current thread. |category| and |name| are free-form
- // strings that describe the event. Both |category| and |name| must be
- // statically allocated.
- static void Begin(const char* category, const char* name) {
- internal::TrackEventDataSource::WriteEvent(
- category, name, perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN);
- }
+// Register the set of available categories by passing a list of categories to
+// this macro: PERFETTO_CATEGORY(cat1), PERFETTO_CATEGORY(cat2), ...
+#define PERFETTO_DEFINE_CATEGORIES(...) \
+ namespace PERFETTO_TRACK_EVENT_NAMESPACE { \
+ /* The list of category names */ \
+ PERFETTO_INTERNAL_DECLARE_CATEGORIES(__VA_ARGS__); \
+ /* The track event data source for this set of categories */ \
+ PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE(); \
+ } // namespace PERFETTO_TRACK_EVENT_NAMESPACE
- // End a slice on the current thread.
- static void End(const char* category) {
- internal::TrackEventDataSource::WriteEvent(
- category, nullptr,
- perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END);
- }
+// Allocate storage for each category by using this macro once per track event
+// namespace.
+#define PERFETTO_TRACK_EVENT_STATIC_STORAGE() \
+ namespace PERFETTO_TRACK_EVENT_NAMESPACE { \
+ PERFETTO_INTERNAL_CATEGORY_STORAGE(); \
+ } // namespace PERFETTO_TRACK_EVENT_NAMESPACE
- // TODO(skyostil): Add per-category enable/disable.
- // TODO(skyostil): Add arguments.
- // TODO(skyostil): Add scoped events.
- // TODO(skyostil): Add async events.
- // TODO(skyostil): Add flow events.
- // TODO(skyostil): Add instant events.
- // TODO(skyostil): Add counters.
+// Begin a thread-scoped slice under |category| with the title |name|. Both
+// strings must be static constants. The track event is only recorded if
+// |category| is enabled for a tracing session.
+#define TRACE_EVENT_BEGIN(category, name) \
+ PERFETTO_INTERNAL_TRACK_EVENT( \
+ category, name, \
+ ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN)
- static void Flush() {
- internal::TrackEventDataSource::Trace(
- [&](internal::TrackEventDataSource::TraceContext ctx) { ctx.Flush(); });
- }
-};
+// End a thread-scoped slice under |category|.
+#define TRACE_EVENT_END(category) \
+ PERFETTO_INTERNAL_TRACK_EVENT( \
+ category, nullptr, \
+ ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END)
-} // namespace perfetto
-
-PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(
- perfetto::internal::TrackEventDataSource,
- perfetto::internal::TrackEventIncrementalState);
+// TODO(skyostil): Add arguments.
+// TODO(skyostil): Add scoped events.
+// TODO(skyostil): Add async events.
+// TODO(skyostil): Add flow events.
+// TODO(skyostil): Add instant events.
+// TODO(skyostil): Add counters.
#endif // INCLUDE_PERFETTO_TRACING_TRACK_EVENT_H_
diff --git a/include/perfetto/tracing/track_event_category_registry.h b/include/perfetto/tracing/track_event_category_registry.h
new file mode 100644
index 0000000..3e15db6
--- /dev/null
+++ b/include/perfetto/tracing/track_event_category_registry.h
@@ -0,0 +1,107 @@
+/*
+ * 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_TRACK_EVENT_CATEGORY_REGISTRY_H_
+#define INCLUDE_PERFETTO_TRACING_TRACK_EVENT_CATEGORY_REGISTRY_H_
+
+#include "perfetto/tracing/data_source.h"
+
+#include <atomic>
+
+namespace perfetto {
+namespace internal {
+
+// A compile-time representation of a track event category. See
+// PERFETTO_DEFINE_CATEGORIES for registering your own categories.
+struct TrackEventCategory {
+ const char* const name;
+};
+
+// Holds all the registered categories for one category namespace. See
+// PERFETTO_DEFINE_CATEGORIES for building the registry.
+class TrackEventCategoryRegistry {
+ public:
+ constexpr TrackEventCategoryRegistry(size_t category_count,
+ const TrackEventCategory* categories,
+ std::atomic<uint8_t>* state_storage)
+ : categories_(categories),
+ category_count_(category_count),
+ state_storage_(state_storage) {
+ static_assert(
+ sizeof(state_storage[0].load()) * 8 >= kMaxDataSourceInstances,
+ "The category state must have enough bits for all possible data source "
+ "instances");
+ }
+
+ size_t category_count() const { return category_count_; }
+
+ // Returns a category based on its index.
+ const TrackEventCategory* GetCategory(size_t index) const;
+
+ // Turn tracing on or off for the given category in a track event data source
+ // instance.
+ void EnableCategoryForInstance(size_t category_index,
+ uint32_t instance_index) const;
+ void DisableCategoryForInstance(size_t category_index,
+ uint32_t instance_index) const;
+
+ constexpr std::atomic<uint8_t>* GetCategoryState(
+ size_t category_index) const {
+ return &state_storage_[category_index];
+ }
+
+ // --------------------------------------------------------------------------
+ // Trace point support
+ // --------------------------------------------------------------------------
+ //
+ // (The following methods are used by the track event trace point
+ // implementation and typically don't need to be called by other code.)
+
+ // At compile time, turn a category name into an index into the registry.
+ // Returns kInvalidCategoryIndex if the category was not found.
+ static constexpr size_t kInvalidCategoryIndex = static_cast<size_t>(-1);
+ constexpr size_t Find(const char* name, size_t index = 0) const {
+ return (index == category_count_) ? kInvalidCategoryIndex
+ : StringEq(categories_[index].name, name)
+ ? index
+ : Find(name, index + 1);
+ }
+
+ // A helper for validating that a category was registered at compile time.
+ template <size_t CategoryIndex>
+ static constexpr size_t Validate() {
+ static_assert(CategoryIndex != kInvalidCategoryIndex,
+ "A track event used an unknown category. Please add it to "
+ "PERFETTO_DEFINE_CATEGORIES().");
+ return CategoryIndex;
+ }
+
+ private:
+ // TODO(skyostil): Make the compile-time routines nicer with C++14.
+ static constexpr bool StringEq(const char* a, const char* b) {
+ return *a != *b ? false
+ : (!*a || !*b) ? (*a == *b) : StringEq(a + 1, b + 1);
+ }
+
+ const TrackEventCategory* const categories_;
+ const size_t category_count_;
+ std::atomic<uint8_t>* const state_storage_;
+};
+
+} // namespace internal
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_TRACING_TRACK_EVENT_CATEGORY_REGISTRY_H_
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index e426793..aca11d7 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -231,6 +231,8 @@
source_set("client_api") {
deps = [
":common",
+ "../../include/perfetto/tracing/core",
+ "../../protos/perfetto/config:lite",
"../base",
"../tracing",
]
@@ -244,9 +246,10 @@
"internal/in_process_tracing_backend.h",
"internal/tracing_muxer_impl.cc",
"internal/tracing_muxer_impl.h",
+ "internal/track_event_internal.cc",
"platform.cc",
"tracing.cc",
- "track_event.cc",
+ "track_event_category_registry.cc",
"virtual_destructors.cc",
]
@@ -276,6 +279,10 @@
]
sources = [
"api_integrationtest.cc",
+ "test/tracing_module.cc",
+ "test/tracing_module.h",
+ "test/tracing_module2.cc",
+ "test/tracing_module_categories.h",
]
}
}
diff --git a/src/tracing/api_integrationtest.cc b/src/tracing/api_integrationtest.cc
index e093b6b..d11359c 100644
--- a/src/tracing/api_integrationtest.cc
+++ b/src/tracing/api_integrationtest.cc
@@ -32,18 +32,28 @@
// Deliberately not pulling any non-public perfetto header to spot accidental
// header public -> non-public dependency while building this file.
-// This is the only header allowed here, see comments in api_test_support.h.
+// These two are the only headers allowed here, see comments in
+// api_test_support.h.
#include "src/tracing/test/api_test_support.h"
+#include "src/tracing/test/tracing_module.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/trace_config.h"
+// Trace categories used in the tests.
+PERFETTO_DEFINE_CATEGORIES(PERFETTO_CATEGORY(test),
+ PERFETTO_CATEGORY(foo),
+ PERFETTO_CATEGORY(bar));
+PERFETTO_TRACK_EVENT_STATIC_STORAGE();
+
namespace {
using ::testing::_;
+using ::testing::HasSubstr;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::NiceMock;
+using ::testing::Not;
using ::testing::Property;
using ::testing::StrEq;
@@ -109,6 +119,42 @@
void OnStop(const StopArgs&) override {}
};
+// Used to verify that track event data sources in different namespaces register
+// themselves correctly in the muxer.
+class MockTracingMuxer : public perfetto::internal::TracingMuxer {
+ public:
+ struct DataSource {
+ const perfetto::DataSourceDescriptor dsd;
+ perfetto::internal::DataSourceStaticState* static_state;
+ };
+
+ MockTracingMuxer() : TracingMuxer(nullptr), prev_instance_(instance_) {
+ instance_ = this;
+ }
+ ~MockTracingMuxer() override { instance_ = prev_instance_; }
+
+ bool RegisterDataSource(
+ const perfetto::DataSourceDescriptor& dsd,
+ DataSourceFactory,
+ perfetto::internal::DataSourceStaticState* static_state) override {
+ data_sources.emplace_back(DataSource{dsd, static_state});
+ return true;
+ }
+
+ std::unique_ptr<perfetto::TraceWriterBase> CreateTraceWriter(
+ perfetto::internal::DataSourceState*,
+ perfetto::BufferExhaustedPolicy) override {
+ return nullptr;
+ }
+
+ void DestroyStoppedTraceWritersForCurrentThread() override {}
+
+ std::vector<DataSource> data_sources;
+
+ private:
+ TracingMuxer* prev_instance_;
+};
+
struct TestIncrementalState {
TestIncrementalState() { constructed = true; }
// Note: a virtual destructor is not required for incremental state.
@@ -122,9 +168,14 @@
bool TestIncrementalState::constructed;
bool TestIncrementalState::destroyed;
+struct TestIncrementalDataSourceTraits
+ : public perfetto::DefaultDataSourceTraits {
+ using IncrementalStateType = TestIncrementalState;
+};
+
class TestIncrementalDataSource
: public perfetto::DataSource<TestIncrementalDataSource,
- TestIncrementalState> {
+ TestIncrementalDataSourceTraits> {
public:
void OnSetup(const SetupArgs&) override {}
void OnStart(const StartArgs&) override {}
@@ -156,6 +207,7 @@
perfetto::Tracing::Initialize(args);
was_initialized = true;
RegisterDataSource<MockDataSource>("my_data_source");
+ perfetto::TrackEvent::Register();
}
// Make sure our data source always has a valid handle.
data_sources_["my_data_source"];
@@ -229,7 +281,6 @@
// This test used to cause a use after free as the tracing session got
// destroyed. It needed to be run approximately 2000 times to catch it so test
// with --gtest_repeat=3000 (less if running under GDB).
- perfetto::TrackEvent::Initialize(/* TODO(skyostil): Register categories */);
// Setup the trace config.
perfetto::TraceConfig cfg;
@@ -253,7 +304,6 @@
// This test used to cause a deadlock (due to StopBlocking() after the session
// already stopped). This usually occurred within 1 or 2 runs of the test so
// use --gtest_repeat=10
- perfetto::TrackEvent::Initialize(/* TODO(skyostil): Register categories */);
// Setup the trace config.
perfetto::TraceConfig cfg;
@@ -277,8 +327,6 @@
}
TEST_F(PerfettoApiTest, TrackEvent) {
- perfetto::TrackEvent::Initialize(/* TODO(skyostil): Register categories */);
-
// Setup the trace config.
perfetto::TraceConfig cfg;
cfg.set_duration_ms(500);
@@ -292,8 +340,8 @@
tracing_session->get()->StartBlocking();
// Emit one complete track event.
- perfetto::TrackEvent::Begin("test", "TestEvent");
- perfetto::TrackEvent::End("test");
+ TRACE_EVENT_BEGIN("test", "TestEvent");
+ TRACE_EVENT_END("test");
perfetto::TrackEvent::Flush();
tracing_session->on_stop.Wait();
@@ -311,7 +359,7 @@
bool end_found = false;
bool process_descriptor_found = false;
bool thread_descriptor_found = false;
- auto now = perfetto::TrackEvent::GetTimeNs();
+ auto now = perfetto::test::GetWallTimeNs();
uint32_t sequence_id = 0;
int32_t cur_pid = perfetto::test::GetCurrentProcessId();
for (const auto& packet : trace.packet()) {
@@ -381,6 +429,194 @@
EXPECT_TRUE(end_found);
}
+TEST_F(PerfettoApiTest, TrackEventCategories) {
+ // Setup the trace config.
+ perfetto::TraceConfig cfg;
+ cfg.set_duration_ms(500);
+ cfg.add_buffers()->set_size_kb(1024);
+ auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ ds_cfg->set_name("track_event");
+ ds_cfg->set_legacy_config("bar");
+
+ // Create a new trace session.
+ auto* tracing_session = NewTrace(cfg);
+ tracing_session->get()->StartBlocking();
+
+ // Emit some track events.
+ TRACE_EVENT_BEGIN("foo", "NotEnabled");
+ TRACE_EVENT_END("foo");
+ TRACE_EVENT_BEGIN("bar", "Enabled");
+ TRACE_EVENT_END("bar");
+
+ tracing_session->get()->StopBlocking();
+ std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+ std::string trace(raw_trace.data(), raw_trace.size());
+ // TODO(skyostil): Come up with a nicer way to verify trace contents.
+ EXPECT_THAT(trace, HasSubstr("Enabled"));
+ EXPECT_THAT(trace, Not(HasSubstr("NotEnabled")));
+}
+
+TEST_F(PerfettoApiTest, TrackEventRegistrationWithModule) {
+ MockTracingMuxer muxer;
+
+ // Each track event namespace registers its own data source.
+ perfetto::TrackEvent::Register();
+ EXPECT_EQ(1u, muxer.data_sources.size());
+
+ tracing_module::InitializeCategories();
+ EXPECT_EQ(2u, muxer.data_sources.size());
+
+ // Both data sources have the same name but distinct static data (i.e.,
+ // individual instance states).
+ EXPECT_EQ("track_event", muxer.data_sources[0].dsd.name());
+ EXPECT_EQ("track_event", muxer.data_sources[1].dsd.name());
+ EXPECT_NE(muxer.data_sources[0].static_state,
+ muxer.data_sources[1].static_state);
+}
+
+TEST_F(PerfettoApiTest, TrackEventSharedIncrementalState) {
+ tracing_module::InitializeCategories();
+
+ // Setup the trace config.
+ perfetto::TraceConfig cfg;
+ cfg.set_duration_ms(500);
+ cfg.add_buffers()->set_size_kb(1024);
+ auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ ds_cfg->set_name("track_event");
+ auto* tracing_session = NewTrace(cfg);
+ tracing_session->get()->StartBlocking();
+
+ perfetto::internal::TrackEventIncrementalState* main_state = nullptr;
+ perfetto::TrackEvent::Trace(
+ [&main_state](perfetto::TrackEvent::TraceContext ctx) {
+ main_state = ctx.GetIncrementalState();
+ });
+ perfetto::internal::TrackEventIncrementalState* module_state =
+ tracing_module::GetIncrementalState();
+
+ // Both track event data sources should use the same incremental state (thanks
+ // to sharing TLS).
+ EXPECT_NE(nullptr, main_state);
+ EXPECT_EQ(main_state, module_state);
+ tracing_session->get()->StopBlocking();
+}
+
+TEST_F(PerfettoApiTest, TrackEventCategoriesWithModule) {
+ // Check that categories defined in two different category registries are
+ // enabled and disabled correctly.
+ tracing_module::InitializeCategories();
+
+ // Setup the trace config.
+ perfetto::TraceConfig cfg;
+ cfg.set_duration_ms(500);
+ cfg.add_buffers()->set_size_kb(1024);
+ auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ ds_cfg->set_name("track_event");
+
+ // Only the "foo" category is enabled. It also exists both locally and in the
+ // existing module.
+ ds_cfg->set_legacy_config("foo");
+
+ // Create a new trace session.
+ auto* tracing_session = NewTrace(cfg);
+ tracing_session->get()->StartBlocking();
+
+ // Emit some track events locally and from the test module.
+ TRACE_EVENT_BEGIN("foo", "FooEventFromMain");
+ TRACE_EVENT_END("foo");
+ tracing_module::EmitTrackEvents();
+ tracing_module::EmitTrackEvents2();
+ TRACE_EVENT_BEGIN("bar", "DisabledEventFromMain");
+ TRACE_EVENT_END("bar");
+
+ tracing_session->get()->StopBlocking();
+ std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+ std::string trace(raw_trace.data(), raw_trace.size());
+ EXPECT_THAT(trace, HasSubstr("FooEventFromMain"));
+ EXPECT_THAT(trace, Not(HasSubstr("DisabledEventFromMain")));
+ EXPECT_THAT(trace, HasSubstr("FooEventFromModule"));
+ EXPECT_THAT(trace, Not(HasSubstr("DisabledEventFromModule")));
+ EXPECT_THAT(trace, HasSubstr("FooEventFromModule2"));
+ EXPECT_THAT(trace, Not(HasSubstr("DisabledEventFromModule2")));
+
+ perfetto::protos::Trace parsed_trace;
+ ASSERT_TRUE(
+ parsed_trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
+
+ uint32_t sequence_id = 0;
+ for (const auto& packet : parsed_trace.packet()) {
+ if (!packet.has_track_event())
+ continue;
+
+ // Make sure we only see track events on one sequence. This means all track
+ // event modules are sharing the same trace writer (by using the same TLS
+ // index).
+ if (packet.trusted_packet_sequence_id()) {
+ if (!sequence_id)
+ sequence_id = packet.trusted_packet_sequence_id();
+ EXPECT_EQ(sequence_id, packet.trusted_packet_sequence_id());
+ }
+ }
+}
+
+TEST_F(PerfettoApiTest, TrackEventConcurrentSessions) {
+ // Check that categories that are enabled and disabled in two parallel tracing
+ // sessions don't interfere.
+
+ // Setup the trace config.
+ perfetto::TraceConfig cfg;
+ cfg.set_duration_ms(500);
+ cfg.add_buffers()->set_size_kb(1024);
+ auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ ds_cfg->set_name("track_event");
+
+ // Session #1 enables the "foo" category.
+ ds_cfg->set_legacy_config("foo");
+ auto* tracing_session = NewTrace(cfg);
+ tracing_session->get()->StartBlocking();
+
+ // Session #2 enables the "bar" category.
+ ds_cfg->set_legacy_config("bar");
+ auto* tracing_session2 = NewTrace(cfg);
+ tracing_session2->get()->StartBlocking();
+
+ // Emit some track events under both categories.
+ TRACE_EVENT_BEGIN("foo", "Session1_First");
+ TRACE_EVENT_END("foo");
+ TRACE_EVENT_BEGIN("bar", "Session2_First");
+ TRACE_EVENT_END("bar");
+
+ tracing_session->get()->StopBlocking();
+ TRACE_EVENT_BEGIN("foo", "Session1_Second");
+ TRACE_EVENT_END("foo");
+ TRACE_EVENT_BEGIN("bar", "Session2_Second");
+ TRACE_EVENT_END("bar");
+
+ tracing_session2->get()->StopBlocking();
+ TRACE_EVENT_BEGIN("foo", "Session1_Third");
+ TRACE_EVENT_END("foo");
+ TRACE_EVENT_BEGIN("bar", "Session2_Third");
+ TRACE_EVENT_END("bar");
+
+ std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+ std::string trace(raw_trace.data(), raw_trace.size());
+ EXPECT_THAT(trace, HasSubstr("Session1_First"));
+ EXPECT_THAT(trace, Not(HasSubstr("Session1_Second")));
+ EXPECT_THAT(trace, Not(HasSubstr("Session1_Third")));
+ EXPECT_THAT(trace, Not(HasSubstr("Session2_First")));
+ EXPECT_THAT(trace, Not(HasSubstr("Session2_Second")));
+ EXPECT_THAT(trace, Not(HasSubstr("Session2_Third")));
+
+ std::vector<char> raw_trace2 = tracing_session2->get()->ReadTraceBlocking();
+ std::string trace2(raw_trace2.data(), raw_trace2.size());
+ EXPECT_THAT(trace2, Not(HasSubstr("Session1_First")));
+ EXPECT_THAT(trace2, Not(HasSubstr("Session1_Second")));
+ EXPECT_THAT(trace2, Not(HasSubstr("Session1_Third")));
+ EXPECT_THAT(trace2, HasSubstr("Session2_First"));
+ EXPECT_THAT(trace2, HasSubstr("Session2_Second"));
+ EXPECT_THAT(trace2, Not(HasSubstr("Session2_Third")));
+}
+
TEST_F(PerfettoApiTest, OneDataSourceOneEvent) {
auto* data_source = &data_sources_["my_data_source"];
@@ -398,6 +634,9 @@
MockDataSource::Trace([](MockDataSource::TraceContext) {
FAIL() << "Should not be called because the trace was not started";
});
+ MockDataSource::CallIfEnabled([](uint32_t) {
+ FAIL() << "Should not be called because the trace was not started";
+ });
tracing_session->get()->Start();
data_source->on_setup.Wait();
@@ -420,6 +659,12 @@
packet = ctx.NewTracePacket();
});
+ uint32_t active_instances = 0;
+ MockDataSource::CallIfEnabled([&active_instances](uint32_t instances) {
+ active_instances = instances;
+ });
+ EXPECT_EQ(1u, active_instances);
+
data_source->on_stop.Wait();
tracing_session->on_stop.Wait();
EXPECT_EQ(trace_lambda_calls, 1);
@@ -427,6 +672,9 @@
MockDataSource::Trace([](MockDataSource::TraceContext) {
FAIL() << "Should not be called because the trace is now stopped";
});
+ MockDataSource::CallIfEnabled([](uint32_t) {
+ FAIL() << "Should not be called because the trace is now stopped";
+ });
std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
ASSERT_GE(raw_trace.size(), 0u);
@@ -738,4 +986,4 @@
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(MockDataSource);
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(MockDataSource2);
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(TestIncrementalDataSource,
- TestIncrementalState);
+ TestIncrementalDataSourceTraits);
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index 8b269a8..1ce6f93 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -24,6 +24,7 @@
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/hash.h"
#include "perfetto/ext/base/thread_checker.h"
#include "perfetto/ext/base/waitable_event.h"
#include "perfetto/ext/tracing/core/trace_packet.h"
@@ -36,6 +37,7 @@
#include "perfetto/tracing/trace_writer_base.h"
#include "perfetto/tracing/tracing.h"
#include "perfetto/tracing/tracing_backend.h"
+#include "protos/perfetto/config/trace_config.pb.h"
#include "src/tracing/internal/in_process_tracing_backend.h"
#include "src/tracing/internal/system_tracing_backend.h"
@@ -55,6 +57,17 @@
mutable std::function<void()> async_stop_closure;
};
+uint64_t ComputeConfigHash(const DataSourceConfig& config) {
+ base::Hash hasher;
+ perfetto::protos::DataSourceConfig config_proto;
+ config.ToProto(&config_proto);
+ std::string config_bytes;
+ bool serialized = config_proto.SerializeToString(&config_bytes);
+ PERFETTO_DCHECK(serialized);
+ hasher.Update(&config_bytes[0], config_bytes.size());
+ return hasher.digest();
+}
+
} // namespace
// ----- Begin of TracingMuxerImpl::ProducerImpl
@@ -461,7 +474,7 @@
static std::atomic<uint32_t> last_id{};
uint32_t new_index = last_id++;
- if (new_index >= kMaxDataSources - 1) {
+ if (new_index >= kMaxDataSources) {
PERFETTO_DLOG(
"RegisterDataSource failed: too many data sources already registered");
return false;
@@ -493,12 +506,39 @@
PERFETTO_DCHECK_THREAD(thread_checker_);
PERFETTO_DLOG("Setting up data source %" PRIu64 " %s", instance_id,
cfg.name().c_str());
+ uint64_t config_hash = ComputeConfigHash(cfg);
for (const auto& rds : data_sources_) {
if (rds.descriptor.name() != cfg.name())
continue;
-
DataSourceStaticState& static_state = *rds.static_state;
+
+ // If this data source is already active for this exact config, don't start
+ // another instance. This happens when we have several data sources with the
+ // same name, in which case the service sends one SetupDataSource event for
+ // each one. Since we can't map which event maps to which data source, we
+ // ensure each event only starts one data source instance.
+ // TODO(skyostil): Register a unique id with each data source to the service
+ // to disambiguate.
+ bool active_for_config = false;
+ for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
+ if (!static_state.TryGet(i))
+ continue;
+ auto* internal_state =
+ reinterpret_cast<DataSourceState*>(&static_state.instances[i]);
+ if (internal_state->backend_id == backend_id &&
+ internal_state->config_hash == config_hash) {
+ active_for_config = true;
+ break;
+ }
+ }
+ if (active_for_config) {
+ PERFETTO_DLOG(
+ "Data source %s is already active with this config, skipping",
+ cfg.name().c_str());
+ continue;
+ }
+
for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
// Find a free slot.
if (static_state.TryGet(i))
@@ -515,14 +555,16 @@
internal_state->data_source_instance_id = instance_id;
internal_state->buffer_id =
static_cast<internal::BufferId>(cfg.target_buffer());
+ internal_state->config_hash = config_hash;
internal_state->data_source = rds.factory();
// This must be made at the end. See matching acquire-load in
// DataSource::Trace().
- static_state.valid_instances.fetch_or(1 << i, std::memory_order_acq_rel);
+ static_state.valid_instances.fetch_or(1 << i, std::memory_order_release);
DataSourceBase::SetupArgs setup_args;
setup_args.config = &cfg;
+ setup_args.internal_instance_index = i;
internal_state->data_source->OnSetup(setup_args);
return;
}
@@ -530,6 +572,7 @@
"Maximum number of data source instances exhausted. "
"Dropping data source %" PRIu64,
instance_id);
+ break;
}
}
@@ -545,9 +588,12 @@
return;
}
+ DataSourceBase::StartArgs start_args{};
+ start_args.internal_instance_index = ds.instance_idx;
+
std::lock_guard<std::recursive_mutex> guard(ds.internal_state->lock);
ds.internal_state->trace_lambda_enabled = true;
- ds.internal_state->data_source->OnStart(DataSourceBase::StartArgs{});
+ ds.internal_state->data_source->OnStart(start_args);
}
// Called by the service of one of the backends.
@@ -564,6 +610,7 @@
}
StopArgsImpl stop_args{};
+ stop_args.internal_instance_index = ds.instance_idx;
stop_args.async_stop_closure = [this, backend_id, instance_id] {
// TracingMuxerImpl is long lived, capturing |this| is okay.
// The notification closure can be moved out of the StopArgs by the
@@ -631,12 +678,12 @@
// Iterate across all possible data source types.
auto cur_generation = generation_.load(std::memory_order_acquire);
auto* root_tls = GetOrCreateTracingTLS();
- for (size_t ds_idx = 0; ds_idx < kMaxDataSources; ds_idx++) {
+
+ auto destroy_stopped_instances = [](DataSourceThreadLocalState& tls) {
// |tls| has a vector of per-data-source-instance thread-local state.
- DataSourceThreadLocalState& tls = root_tls->data_sources_tls[ds_idx];
DataSourceStaticState* static_state = tls.static_state;
if (!static_state)
- continue; // Slot not used.
+ return; // Slot not used.
// Iterate across all possible instances for this data source.
for (uint32_t inst = 0; inst < kMaxDataSourceInstances; inst++) {
@@ -653,7 +700,14 @@
// The DataSource instance has been destroyed or recycled.
ds_tls.Reset(); // Will also destroy the |ds_tls.trace_writer|.
}
+ };
+
+ for (size_t ds_idx = 0; ds_idx < kMaxDataSources; ds_idx++) {
+ // |tls| has a vector of per-data-source-instance thread-local state.
+ DataSourceThreadLocalState& tls = root_tls->data_sources_tls[ds_idx];
+ destroy_stopped_instances(tls);
}
+ destroy_stopped_instances(root_tls->track_event_tls);
root_tls->generation = cur_generation;
}
diff --git a/src/tracing/track_event.cc b/src/tracing/internal/track_event_internal.cc
similarity index 63%
rename from src/tracing/track_event.cc
rename to src/tracing/internal/track_event_internal.cc
index 88411b9..b797e08 100644
--- a/src/tracing/track_event.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-#include "perfetto/tracing/track_event.h"
+#include "perfetto/tracing/internal/track_event_internal.h"
+#include "perfetto/base/time.h"
#include "perfetto/ext/base/proc_utils.h"
#include "perfetto/ext/base/thread_utils.h"
-#include "perfetto/tracing/core/data_source_descriptor.h"
-#include "perfetto/tracing/data_source.h"
+#include "perfetto/tracing/core/data_source_config.h"
+#include "perfetto/tracing/track_event_category_registry.h"
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
-#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
namespace perfetto {
namespace internal {
@@ -31,6 +31,11 @@
std::atomic<perfetto::base::PlatformThreadID> g_main_thread;
+uint64_t GetTimeNs() {
+ // TODO(skyostil): Consider using boot time where available.
+ return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
+}
+
uint64_t GetNameIidOrZero(std::unordered_map<const char*, uint64_t>& name_map,
const char* name) {
auto it = name_map.find(name);
@@ -39,27 +44,84 @@
return it->second;
}
+// static
+void WriteSequenceDescriptors(TrackEventTraceContext* ctx, uint64_t timestamp) {
+ if (perfetto::base::GetThreadId() == g_main_thread) {
+ auto packet = ctx->NewTracePacket();
+ packet->set_timestamp(timestamp);
+ packet->set_incremental_state_cleared(true);
+ auto pd = packet->set_process_descriptor();
+ pd->set_pid(static_cast<int32_t>(base::GetProcessId()));
+ // TODO(skyostil): Record command line.
+ }
+ {
+ auto packet = ctx->NewTracePacket();
+ packet->set_timestamp(timestamp);
+ auto td = packet->set_thread_descriptor();
+ td->set_pid(static_cast<int32_t>(base::GetProcessId()));
+ td->set_tid(static_cast<int32_t>(perfetto::base::GetThreadId()));
+ }
+}
+
} // namespace
-void TrackEventDataSource::OnSetup(const SetupArgs&) {}
-void TrackEventDataSource::OnStart(const StartArgs&) {}
-void TrackEventDataSource::OnStop(const StopArgs&) {}
+TrackEventTraceContext::TrackEventTraceContext(
+ TrackEventIncrementalState* incremental_state,
+ TracePacketCreator new_trace_packet)
+ : incremental_state_(incremental_state),
+ new_trace_packet_(std::move(new_trace_packet)) {}
+
+TrackEventTraceContext::TracePacketHandle
+TrackEventTraceContext::NewTracePacket() {
+ return new_trace_packet_();
+}
// static
-void TrackEventDataSource::WriteEventImpl(
- internal::TrackEventDataSource::TraceContext ctx,
+void TrackEventInternal::Initialize() {
+ if (!g_main_thread)
+ g_main_thread = perfetto::base::GetThreadId();
+}
+
+// static
+void TrackEventInternal::EnableTracing(
+ const TrackEventCategoryRegistry& registry,
+ const DataSourceConfig& config,
+ uint32_t instance_index) {
+ for (size_t i = 0; i < registry.category_count(); i++) {
+ // TODO(skyostil): Support the full category config syntax instead of
+ // just strict matching.
+ // TODO(skyostil): Support comma-separated categories.
+ if (config.legacy_config().empty() ||
+ config.legacy_config() == registry.GetCategory(i)->name) {
+ registry.EnableCategoryForInstance(i, instance_index);
+ }
+ }
+}
+
+// static
+void TrackEventInternal::DisableTracing(
+ const TrackEventCategoryRegistry& registry,
+ uint32_t instance_index) {
+ for (size_t i = 0; i < registry.category_count(); i++)
+ registry.DisableCategoryForInstance(i, instance_index);
+}
+
+// static
+void TrackEventInternal::WriteEvent(
+ TrackEventTraceContext* ctx,
const char* category,
const char* name,
perfetto::protos::pbzero::TrackEvent::Type type) {
PERFETTO_DCHECK(category);
- auto timestamp = TrackEvent::GetTimeNs();
+ PERFETTO_DCHECK(g_main_thread);
+ auto timestamp = GetTimeNs();
- auto* incr_state = ctx.GetIncrementalState();
+ auto* incr_state = ctx->incremental_state();
if (incr_state->was_cleared) {
incr_state->was_cleared = false;
- WriteSequenceDescriptors(&ctx, timestamp);
+ WriteSequenceDescriptors(ctx, timestamp);
}
- auto packet = ctx.NewTracePacket();
+ auto packet = ctx->NewTracePacket();
packet->set_timestamp(timestamp);
// We assume that |category| and |name| point to strings with static lifetime.
@@ -94,39 +156,5 @@
}
}
-// static
-void TrackEventDataSource::WriteSequenceDescriptors(
- internal::TrackEventDataSource::TraceContext* ctx,
- uint64_t timestamp) {
- if (perfetto::base::GetThreadId() == g_main_thread) {
- auto packet = ctx->NewTracePacket();
- packet->set_timestamp(timestamp);
- packet->set_incremental_state_cleared(true);
- auto pd = packet->set_process_descriptor();
- pd->set_pid(static_cast<int32_t>(base::GetProcessId()));
- // TODO(skyostil): Record command line.
- }
- {
- auto packet = ctx->NewTracePacket();
- packet->set_timestamp(timestamp);
- auto td = packet->set_thread_descriptor();
- td->set_pid(static_cast<int32_t>(base::GetProcessId()));
- td->set_tid(static_cast<int32_t>(perfetto::base::GetThreadId()));
- }
-}
-
} // namespace internal
-
-// static
-void TrackEvent::Initialize() {
- internal::g_main_thread = perfetto::base::GetThreadId();
- DataSourceDescriptor dsd;
- dsd.set_name("track_event");
- internal::TrackEventDataSource::Register(dsd);
-}
-
} // namespace perfetto
-
-PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(
- perfetto::internal::TrackEventDataSource,
- perfetto::internal::TrackEventIncrementalState);
diff --git a/src/tracing/test/api_test_support.cc b/src/tracing/test/api_test_support.cc
index 73f1fae..ce49407 100644
--- a/src/tracing/test/api_test_support.cc
+++ b/src/tracing/test/api_test_support.cc
@@ -16,6 +16,7 @@
#include "src/tracing/test/api_test_support.h"
+#include "perfetto/base/time.h"
#include "perfetto/ext/base/proc_utils.h"
namespace perfetto {
@@ -25,5 +26,9 @@
return static_cast<int32_t>(base::GetProcessId());
}
+uint64_t GetWallTimeNs() {
+ return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
+}
+
} // namespace test
} // namespace perfetto
diff --git a/src/tracing/test/api_test_support.h b/src/tracing/test/api_test_support.h
index 9409caf..d85b9ec 100644
--- a/src/tracing/test/api_test_support.h
+++ b/src/tracing/test/api_test_support.h
@@ -32,6 +32,7 @@
namespace test {
int32_t GetCurrentProcessId();
+uint64_t GetWallTimeNs();
} // namespace test
} // namespace perfetto
diff --git a/src/tracing/test/tracing_module.cc b/src/tracing/test/tracing_module.cc
new file mode 100644
index 0000000..68a31cc
--- /dev/null
+++ b/src/tracing/test/tracing_module.cc
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include "src/tracing/test/tracing_module.h"
+
+#include "src/tracing/test/tracing_module_categories.h"
+
+#include <stdio.h>
+
+// This file is for checking that multiple sets of trace event categories can be
+// combined into the same program.
+
+PERFETTO_TRACK_EVENT_STATIC_STORAGE();
+
+namespace tracing_module {
+
+void InitializeCategories() {
+ TrackEvent::Register();
+}
+
+void EmitTrackEvents() {
+ TRACE_EVENT_BEGIN("cat1", "DisabledEventFromModule");
+ TRACE_EVENT_END("cat1");
+ TRACE_EVENT_BEGIN("cat4", "DisabledEventFromModule");
+ TRACE_EVENT_END("cat4");
+ TRACE_EVENT_BEGIN("cat9", "DisabledEventFromModule");
+ TRACE_EVENT_END("cat9");
+ TRACE_EVENT_BEGIN("foo", "FooEventFromModule");
+ TRACE_EVENT_END("foo");
+}
+
+perfetto::internal::TrackEventIncrementalState* GetIncrementalState() {
+ perfetto::internal::TrackEventIncrementalState* state = nullptr;
+ TrackEvent::Trace([&state](TrackEvent::TraceContext ctx) {
+ state = ctx.GetIncrementalState();
+ });
+ return state;
+}
+
+void FunctionWithOneTrackEvent() {
+ TRACE_EVENT_BEGIN("cat1", "DisabledEventFromModule");
+ // Simulates the non-tracing work of this function, which should take priority
+ // over the above trace event in terms of instruction scheduling.
+ puts("Hello");
+}
+
+} // namespace tracing_module
diff --git a/src/tracing/test/tracing_module.h b/src/tracing/test/tracing_module.h
new file mode 100644
index 0000000..ce9af43
--- /dev/null
+++ b/src/tracing/test/tracing_module.h
@@ -0,0 +1,41 @@
+/*
+ * 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 SRC_TRACING_TEST_TRACING_MODULE_H_
+#define SRC_TRACING_TEST_TRACING_MODULE_H_
+
+// Note: No non-client API header includes are allowed here.
+
+namespace perfetto {
+namespace internal {
+struct TrackEventIncrementalState;
+} // namespace internal
+} // namespace perfetto
+
+namespace tracing_module {
+
+void InitializeCategories();
+void EmitTrackEvents();
+void EmitTrackEvents2();
+perfetto::internal::TrackEventIncrementalState* GetIncrementalState();
+
+// This function is used to check the instruction size overhead of a single
+// track event.
+void FunctionWithOneTrackEvent();
+
+} // namespace tracing_module
+
+#endif // SRC_TRACING_TEST_TRACING_MODULE_H_
diff --git a/src/tracing/test/tracing_module2.cc b/src/tracing/test/tracing_module2.cc
new file mode 100644
index 0000000..e907d71
--- /dev/null
+++ b/src/tracing/test/tracing_module2.cc
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#include "src/tracing/test/tracing_module.h"
+
+#include "src/tracing/test/tracing_module_categories.h"
+
+#include <stdio.h>
+
+// This file checks that one track event category list can be shared by two
+// compilation units.
+
+namespace tracing_module {
+
+void EmitTrackEvents2() {
+ TRACE_EVENT_BEGIN("cat1", "DisabledEventFromModule2");
+ TRACE_EVENT_END("cat1");
+ TRACE_EVENT_BEGIN("cat4", "DisabledEventFromModule2");
+ TRACE_EVENT_END("cat4");
+ TRACE_EVENT_BEGIN("cat9", "DisabledEventFromModule2");
+ TRACE_EVENT_END("cat9");
+ TRACE_EVENT_BEGIN("foo", "FooEventFromModule2");
+ TRACE_EVENT_END("foo");
+}
+
+} // namespace tracing_module
diff --git a/src/tracing/test/tracing_module_categories.h b/src/tracing/test/tracing_module_categories.h
new file mode 100644
index 0000000..f776894
--- /dev/null
+++ b/src/tracing/test/tracing_module_categories.h
@@ -0,0 +1,40 @@
+/*
+ * 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 SRC_TRACING_TEST_TRACING_MODULE_CATEGORIES_H_
+#define SRC_TRACING_TEST_TRACING_MODULE_CATEGORIES_H_
+
+// This header defines the tracing categories (and track event data source) used
+// in the external tracing test module. These categories are distinct from the
+// ones defined in api_integrationtest.cc, but events for both sets of
+// categories can be written to the same trace writer.
+
+#define PERFETTO_TRACK_EVENT_NAMESPACE tracing_module
+
+#include "perfetto/tracing.h"
+
+PERFETTO_DEFINE_CATEGORIES(PERFETTO_CATEGORY(cat1),
+ PERFETTO_CATEGORY(cat2),
+ PERFETTO_CATEGORY(cat3),
+ PERFETTO_CATEGORY(cat4),
+ PERFETTO_CATEGORY(cat5),
+ PERFETTO_CATEGORY(cat6),
+ PERFETTO_CATEGORY(cat7),
+ PERFETTO_CATEGORY(cat8),
+ PERFETTO_CATEGORY(cat9),
+ PERFETTO_CATEGORY(foo));
+
+#endif // SRC_TRACING_TEST_TRACING_MODULE_CATEGORIES_H_
diff --git a/src/tracing/track_event_category_registry.cc b/src/tracing/track_event_category_registry.cc
new file mode 100644
index 0000000..e64fa3b
--- /dev/null
+++ b/src/tracing/track_event_category_registry.cc
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include "perfetto/tracing/track_event_category_registry.h"
+
+namespace perfetto {
+namespace internal {
+
+const TrackEventCategory* TrackEventCategoryRegistry::GetCategory(
+ size_t index) const {
+ PERFETTO_DCHECK(index < category_count_);
+ return &categories_[index];
+}
+
+void TrackEventCategoryRegistry::EnableCategoryForInstance(
+ size_t category_index,
+ uint32_t instance_index) const {
+ PERFETTO_DCHECK(instance_index < kMaxDataSourceInstances);
+ PERFETTO_DCHECK(category_index < category_count_);
+ // Matches the acquire_load in DataSource::Trace().
+ state_storage_[category_index].fetch_or(
+ static_cast<uint8_t>(1u << instance_index), std::memory_order_release);
+}
+
+void TrackEventCategoryRegistry::DisableCategoryForInstance(
+ size_t category_index,
+ uint32_t instance_index) const {
+ PERFETTO_DCHECK(instance_index < kMaxDataSourceInstances);
+ PERFETTO_DCHECK(category_index < category_count_);
+ // Matches the acquire_load in DataSource::Trace().
+ state_storage_[category_index].fetch_and(
+ static_cast<uint8_t>(~(1u << instance_index)), std::memory_order_release);
+}
+
+} // namespace internal
+} // namespace perfetto