| /* |
| * 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_H_ |
| #define INCLUDE_PERFETTO_TRACING_TRACK_EVENT_H_ |
| |
| #include "perfetto/base/time.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/string_helpers.h" |
| #include "perfetto/tracing/track.h" |
| #include "perfetto/tracing/track_event_category_registry.h" |
| #include "protos/perfetto/trace/track_event/track_event.pbzero.h" |
| |
| #include <type_traits> |
| |
| // 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(); |
| // |
| // // A basic track event with just a name. |
| // TRACE_EVENT("category", "MyEvent"); |
| // |
| // // A track event with (up to two) debug annotations. |
| // TRACE_EVENT("category", "MyEvent", "parameter", 42); |
| // |
| // // A track event with a strongly typed parameter. |
| // TRACE_EVENT("category", "MyEvent", [](perfetto::EventContext ctx) { |
| // ctx.event()->set_foo(42); |
| // ctx.event()->set_bar(.5f); |
| // }); |
| // } |
| // |
| // Note that track events must be nested consistently, i.e., the following is |
| // not allowed: |
| // |
| // TRACE_EVENT_BEGIN("a", "bar", ...); |
| // TRACE_EVENT_BEGIN("b", "foo", ...); |
| // TRACE_EVENT_END("a"); // "foo" must be closed before "bar". |
| // TRACE_EVENT_END("b"); |
| // |
| // ==================== |
| // Implementation notes |
| // ==================== |
| // |
| // The track event library consists of the following layers and components. The |
| // classes the internal namespace shouldn't be considered part of the public |
| // API. |
| // .--------------------------------. |
| // .----| TRACE_EVENT |----. |
| // write | | - App instrumentation point | | write |
| // event | '--------------------------------' | arguments |
| // V V |
| // .----------------------------------. .-----------------------------. |
| // | TrackEvent | | EventContext | |
| // | - Registry of event categories | | - One track event instance | |
| // '----------------------------------' '-----------------------------' |
| // | | |
| // | | look up |
| // | is | interning ids |
| // V V |
| // .----------------------------------. .-----------------------------. |
| // | internal::TrackEventDataSource | | TrackEventInternedDataIndex | |
| // | - Perfetto data source | | - Corresponds to a field in | |
| // | - Has TrackEventIncrementalState | | in interned_data.proto | |
| // '----------------------------------' '-----------------------------' |
| // | | ^ |
| // | | owns (1:many) | |
| // | write event '-------------------------' |
| // V |
| // .----------------------------------. |
| // | internal::TrackEventInternal | |
| // | - Outlined code to serialize | |
| // | one track event | |
| // '----------------------------------' |
| // |
| |
| // 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 |
| |
| // Deprecated; see perfetto::Category(). |
| #define PERFETTO_CATEGORY(name) \ |
| ::perfetto::Category { #name } |
| |
| // Internal helpers for determining if a given category is defined at build or |
| // runtime. |
| namespace PERFETTO_TRACK_EVENT_NAMESPACE { |
| namespace internal { |
| |
| // By default no statically defined categories are dynamic, but this can be |
| // overridden with PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES. |
| template <typename... T> |
| constexpr bool IsDynamicCategory(const char*) { |
| return false; |
| } |
| |
| // Explicitly dynamic categories are always dynamic. |
| constexpr bool IsDynamicCategory(const ::perfetto::DynamicCategory&) { |
| return true; |
| } |
| |
| } // namespace internal |
| } // namespace PERFETTO_TRACK_EVENT_NAMESPACE |
| |
| // Normally all categories are defined statically at build-time (see |
| // PERFETTO_DEFINE_CATEGORIES). However, some categories are only used for |
| // testing, and we shouldn't publish them to the tracing service or include them |
| // in a production binary. Use this macro to define a list of prefixes for these |
| // types of categories. Note that trace points using these categories will be |
| // slightly less efficient compared to regular trace points. |
| #define PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES(...) \ |
| namespace PERFETTO_TRACK_EVENT_NAMESPACE { \ |
| namespace internal { \ |
| template <> \ |
| constexpr bool IsDynamicCategory(const char* name) { \ |
| return ::perfetto::internal::IsStringInPrefixList(name, __VA_ARGS__); \ |
| } \ |
| } /* namespace internal */ \ |
| } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */ \ |
| PERFETTO_INTERNAL_SWALLOW_SEMICOLON() |
| |
| // 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 */ \ |
| PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS( \ |
| PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent, \ |
| perfetto::internal::TrackEventDataSourceTraits) |
| |
| // 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() \ |
| PERFETTO_INTERNAL_DEFINE_TRACK_EVENT_DATA_SOURCE() \ |
| } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */ \ |
| PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS( \ |
| PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent, \ |
| perfetto::internal::TrackEventDataSourceTraits) |
| |
| // Ignore GCC warning about a missing argument for a variadic macro parameter. |
| #if defined(__GNUC__) || defined(__clang__) |
| #pragma GCC system_header |
| #endif |
| |
| // Begin a 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. |
| // |
| // The slice is thread-scoped (i.e., written to the default track of the current |
| // thread) unless overridden with a custom track object (see Track). |
| // |
| // |name| must be a string with static lifetime (i.e., the same |
| // address must not be used for a different event name in the future). If you |
| // want to use a dynamically allocated name, do this: |
| // |
| // TRACE_EVENT("category", nullptr, [&](perfetto::EventContext ctx) { |
| // ctx.event()->set_name(dynamic_name); |
| // }); |
| // |
| // The following optional arguments can be passed to `TRACE_EVENT` to add extra |
| // information to events: |
| // |
| // TRACE_EVENT("cat", "name"[, track][, timestamp] |
| // [, "debug_name1", debug_value1] |
| // [, "debug_name2", debug_value2] |
| // ... |
| // [, "debug_nameN", debug_valueN] |
| // [, lambda]); |
| // |
| // Some examples of valid combinations: |
| // |
| // 1. A lambda for writing custom TrackEvent fields: |
| // |
| // TRACE_EVENT("category", "Name", [&](perfetto::EventContext ctx) { |
| // ctx.event()->set_custom_value(...); |
| // }); |
| // |
| // 2. A timestamp and a lambda: |
| // |
| // TRACE_EVENT("category", "Name", time_in_nanoseconds, |
| // [&](perfetto::EventContext ctx) { |
| // ctx.event()->set_custom_value(...); |
| // }); |
| // |
| // |time_in_nanoseconds| should be an uint64_t by default. To support custom |
| // timestamp types, |
| // |perfetto::TraceTimestampTraits<T>::ConvertTimestampToTraceTimeNs| |
| // should be defined. See |ConvertTimestampToTraceTimeNs| for more details. |
| // |
| // 3. Arbitrary number of debug annotations: |
| // |
| // TRACE_EVENT("category", "Name", "arg", value); |
| // TRACE_EVENT("category", "Name", "arg", value, "arg2", value2); |
| // TRACE_EVENT("category", "Name", "arg", value, "arg2", value2, |
| // "arg3", value3); |
| // |
| // See |TracedValue| for recording custom types as debug annotations. |
| // |
| // 4. Arbitrary number of debug annotations and a lambda: |
| // |
| // TRACE_EVENT("category", "Name", "arg", value, |
| // [&](perfetto::EventContext ctx) { |
| // ctx.event()->set_custom_value(...); |
| // }); |
| // |
| // 5. An overridden track: |
| // |
| // TRACE_EVENT("category", "Name", perfetto::Track(1234)); |
| // |
| // See |Track| for other types of tracks which may be used. |
| // |
| // 6. A track and a lambda: |
| // |
| // TRACE_EVENT("category", "Name", perfetto::Track(1234), |
| // [&](perfetto::EventContext ctx) { |
| // ctx.event()->set_custom_value(...); |
| // }); |
| // |
| // 7. A track and a timestamp: |
| // |
| // TRACE_EVENT("category", "Name", perfetto::Track(1234), |
| // time_in_nanoseconds); |
| // |
| // 8. A track, a timestamp and a lambda: |
| // |
| // TRACE_EVENT("category", "Name", perfetto::Track(1234), |
| // time_in_nanoseconds, [&](perfetto::EventContext ctx) { |
| // ctx.event()->set_custom_value(...); |
| // }); |
| // |
| // 9. A track and an arbitrary number of debug annotions: |
| // |
| // TRACE_EVENT("category", "Name", perfetto::Track(1234), |
| // "arg", value); |
| // TRACE_EVENT("category", "Name", perfetto::Track(1234), |
| // "arg", value, "arg2", value2); |
| // |
| #define TRACE_EVENT_BEGIN(category, name, ...) \ |
| PERFETTO_INTERNAL_TRACK_EVENT( \ |
| category, ::perfetto::internal::GetStaticString(name), \ |
| ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN, ##__VA_ARGS__) |
| |
| // End a slice under |category|. |
| #define TRACE_EVENT_END(category, ...) \ |
| PERFETTO_INTERNAL_TRACK_EVENT( \ |
| category, /*name=*/nullptr, \ |
| ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END, ##__VA_ARGS__) |
| |
| // Begin a slice which gets automatically closed when going out of scope. |
| #define TRACE_EVENT(category, name, ...) \ |
| PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(category, name, ##__VA_ARGS__) |
| |
| // Emit a slice which has zero duration. |
| #define TRACE_EVENT_INSTANT(category, name, ...) \ |
| PERFETTO_INTERNAL_TRACK_EVENT( \ |
| category, ::perfetto::internal::GetStaticString(name), \ |
| ::perfetto::protos::pbzero::TrackEvent::TYPE_INSTANT, ##__VA_ARGS__) |
| |
| // Efficiently determine if the given static or dynamic trace category or |
| // category group is enabled for tracing. |
| #define TRACE_EVENT_CATEGORY_ENABLED(category) \ |
| PERFETTO_INTERNAL_CATEGORY_ENABLED(category) |
| |
| // Time-varying numeric data can be recorded with the TRACE_COUNTER macro: |
| // |
| // TRACE_COUNTER("cat", counter_track[, timestamp], value); |
| // |
| // For example, to record a single value for a counter called "MyCounter": |
| // |
| // TRACE_COUNTER("category", "MyCounter", 1234.5); |
| // |
| // This data is displayed as a counter track in the Perfetto UI. |
| // |
| // Both integer and floating point counter values are supported. Counters can |
| // also be annotated with additional information such as units, for example, for |
| // tracking the rendering framerate in terms of frames per second or "fps": |
| // |
| // TRACE_COUNTER("category", perfetto::CounterTrack("Framerate", "fps"), 120); |
| // |
| // As another example, a memory counter that records bytes but accepts samples |
| // as kilobytes (to reduce trace binary size) can be defined like this: |
| // |
| // perfetto::CounterTrack memory_track = perfetto::CounterTrack("Memory") |
| // .set_unit("bytes") |
| // .set_multiplier(1024); |
| // TRACE_COUNTER("category", memory_track, 4 /* = 4096 bytes */); |
| // |
| // See /protos/perfetto/trace/track_event/counter_descriptor.proto |
| // for the full set of attributes for a counter track. |
| // |
| // To record a counter value at a specific point in time (instead of the current |
| // time), you can pass in a custom timestamp: |
| // |
| // // First record the current time and counter value. |
| // uint64_t timestamp = perfetto::TrackEvent::GetTraceTimeNs(); |
| // int64_t value = 1234; |
| // |
| // // Later, emit a sample at that point in time. |
| // TRACE_COUNTER("category", "MyCounter", timestamp, value); |
| // |
| #define TRACE_COUNTER(category, track, ...) \ |
| PERFETTO_INTERNAL_TRACK_EVENT( \ |
| category, /*name=*/nullptr, \ |
| ::perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER, \ |
| ::perfetto::CounterTrack(track), ##__VA_ARGS__) |
| |
| // TODO(skyostil): Add flow events. |
| |
| #endif // INCLUDE_PERFETTO_TRACING_TRACK_EVENT_H_ |