| /* |
| * 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_DATA_SOURCE_H_ |
| #define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_ |
| |
| #include "perfetto/base/compiler.h" |
| #include "perfetto/base/template_util.h" |
| #include "perfetto/protozero/message_handle.h" |
| #include "perfetto/tracing/core/data_source_config.h" |
| #include "perfetto/tracing/data_source.h" |
| #include "perfetto/tracing/event_context.h" |
| #include "perfetto/tracing/internal/track_event_internal.h" |
| #include "perfetto/tracing/internal/track_event_legacy.h" |
| #include "perfetto/tracing/internal/write_track_event_args.h" |
| #include "perfetto/tracing/track.h" |
| #include "perfetto/tracing/track_event_category_registry.h" |
| #include "protos/perfetto/common/builtin_clock.pbzero.h" |
| #include "protos/perfetto/config/track_event/track_event_config.gen.h" |
| #include "protos/perfetto/trace/track_event/track_event.pbzero.h" |
| |
| #include <type_traits> |
| |
| namespace perfetto { |
| |
| namespace { |
| |
| class StopArgsImpl : public DataSourceBase::StopArgs { |
| public: |
| // HandleAsynchronously() can optionally be called to defer the tracing |
| // session stop and write track events just before stopping. This function |
| // returns a closure that must be invoked after the last track events have |
| // been emitted. The caller also needs to explicitly call |
| // TrackEvent::Flush() because no other implicit flushes will happen after |
| // the stop signal. |
| // See the comment in include/perfetto/tracing/data_source.h for more info. |
| std::function<void()> HandleStopAsynchronously() const override { |
| auto closure = std::move(async_stop_closure); |
| async_stop_closure = std::function<void()>(); |
| return closure; |
| } |
| |
| mutable std::function<void()> async_stop_closure; |
| }; |
| |
| } // namespace |
| |
| // A function for converting an abstract timestamp into a |
| // perfetto::TraceTimestamp struct. By specialising this template and defining |
| // static ConvertTimestampToTraceTimeNs function in it the user can register |
| // additional timestamp types. The return value should specify the |
| // clock domain used by the timestamp as well as its value. |
| // |
| // The supported clock domains are the ones described in |
| // perfetto.protos.ClockSnapshot. However, custom clock IDs (>=64) are |
| // reserved for internal use by the SDK for the time being. |
| // The timestamp value should be in nanoseconds regardless of the clock domain. |
| template <typename T> |
| struct TraceTimestampTraits; |
| |
| // A pass-through implementation for raw uint64_t nanosecond timestamps. |
| template <> |
| struct TraceTimestampTraits<uint64_t> { |
| static inline TraceTimestamp ConvertTimestampToTraceTimeNs( |
| const uint64_t& timestamp) { |
| return {static_cast<uint32_t>(internal::TrackEventInternal::GetClockId()), timestamp}; |
| } |
| }; |
| |
| // A pass-through implementation for the trace timestamp structure. |
| template <> |
| struct TraceTimestampTraits<TraceTimestamp> { |
| static inline TraceTimestamp ConvertTimestampToTraceTimeNs( |
| const TraceTimestamp& timestamp) { |
| return timestamp; |
| } |
| }; |
| |
| namespace internal { |
| namespace { |
| |
| // Checks if |T| is a valid track. |
| template <typename T> |
| static constexpr bool IsValidTrack() { |
| return std::is_convertible<T, Track>::value; |
| } |
| |
| // Checks if |T| is a valid non-counter track. |
| template <typename T> |
| static constexpr bool IsValidNormalTrack() { |
| return std::is_convertible<T, Track>::value && |
| !std::is_convertible<T, CounterTrack>::value; |
| } |
| |
| // Because the user can use arbitrary timestamp types, we can't compare against |
| // any known base type here. Instead, we check that a track or a trace lambda |
| // isn't being interpreted as a timestamp. |
| template <typename T, |
| typename CanBeConvertedToNsCheck = decltype( |
| ::perfetto::TraceTimestampTraits<typename base::remove_cvref_t< |
| T>>::ConvertTimestampToTraceTimeNs(std::declval<T>())), |
| typename NotTrackCheck = |
| typename std::enable_if<!IsValidNormalTrack<T>()>::type, |
| typename NotLambdaCheck = |
| typename std::enable_if<!IsValidTraceLambda<T>()>::type> |
| static constexpr bool IsValidTimestamp() { |
| return true; |
| } |
| |
| // Taken from C++17 |
| template <typename...> |
| using void_t = void; |
| |
| // Returns true iff `GetStaticString(T)` is defined OR T == DynamicString. |
| template <typename T, typename = void> |
| struct IsValidEventNameType |
| : std::is_same<perfetto::DynamicString, typename std::decay<T>::type> {}; |
| |
| template <typename T> |
| struct IsValidEventNameType< |
| T, |
| void_t<decltype(GetStaticString(std::declval<T>()))>> : std::true_type {}; |
| |
| template <typename T> |
| inline void ValidateEventNameType() { |
| static_assert( |
| IsValidEventNameType<T>::value, |
| "Event names must be static strings. To use dynamic event names, see " |
| "https://perfetto.dev/docs/instrumentation/" |
| "track-events#dynamic-event-names"); |
| } |
| |
| } // namespace |
| |
| inline ::perfetto::DynamicString DecayEventNameType( |
| ::perfetto::DynamicString name) { |
| return name; |
| } |
| |
| inline ::perfetto::StaticString DecayEventNameType( |
| ::perfetto::StaticString name) { |
| return name; |
| } |
| |
| // Convert all static strings of different length to StaticString to avoid |
| // unnecessary template instantiations. |
| inline ::perfetto::StaticString DecayEventNameType(const char* name) { |
| return ::perfetto::StaticString{name}; |
| } |
| |
| // Traits for dynamic categories. |
| template <typename CategoryType> |
| struct CategoryTraits { |
| static constexpr bool kIsDynamic = true; |
| static constexpr const Category* GetStaticCategory( |
| const TrackEventCategoryRegistry*, |
| const CategoryType&) { |
| return nullptr; |
| } |
| static size_t GetStaticIndex(const CategoryType&) { |
| PERFETTO_DCHECK(false); // Not reached. |
| return TrackEventCategoryRegistry::kDynamicCategoryIndex; |
| } |
| static DynamicCategory GetDynamicCategory(const CategoryType& category) { |
| return DynamicCategory{category}; |
| } |
| }; |
| |
| // Traits for static categories. |
| template <> |
| struct CategoryTraits<size_t> { |
| static constexpr bool kIsDynamic = false; |
| static const Category* GetStaticCategory( |
| const TrackEventCategoryRegistry* registry, |
| size_t category_index) { |
| return registry->GetCategory(category_index); |
| } |
| static constexpr size_t GetStaticIndex(size_t category_index) { |
| return category_index; |
| } |
| static DynamicCategory GetDynamicCategory(size_t) { |
| PERFETTO_DCHECK(false); // Not reached. |
| return DynamicCategory(); |
| } |
| }; |
| |
| struct TrackEventDataSourceTraits : public perfetto::DefaultDataSourceTraits { |
| using IncrementalStateType = TrackEventIncrementalState; |
| using TlsStateType = TrackEventTlsState; |
| |
| // 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 DerivedDataSource, |
| const TrackEventCategoryRegistry* Registry> |
| class TrackEventDataSource |
| : public DataSource<DerivedDataSource, TrackEventDataSourceTraits> { |
| using Base = DataSource<DerivedDataSource, TrackEventDataSourceTraits>; |
| |
| public: |
| static constexpr bool kRequiresCallbacksUnderLock = false; |
| |
| // Add or remove a session observer for this track event data source. The |
| // observer will be notified about started and stopped tracing sessions. |
| // Returns |true| if the observer was successfully added (i.e., the maximum |
| // number of observers wasn't exceeded). |
| static bool AddSessionObserver(TrackEventSessionObserver* observer) { |
| return TrackEventInternal::AddSessionObserver(*Registry, observer); |
| } |
| |
| static void RemoveSessionObserver(TrackEventSessionObserver* observer) { |
| TrackEventInternal::RemoveSessionObserver(*Registry, observer); |
| } |
| |
| // DataSource implementation. |
| void OnSetup(const DataSourceBase::SetupArgs& args) override { |
| auto config_raw = args.config->track_event_config_raw(); |
| bool ok = config_.ParseFromArray(config_raw.data(), config_raw.size()); |
| PERFETTO_DCHECK(ok); |
| TrackEventInternal::EnableTracing(*Registry, config_, args); |
| } |
| |
| void OnStart(const DataSourceBase::StartArgs& args) override { |
| TrackEventInternal::OnStart(*Registry, args); |
| } |
| |
| void OnStop(const DataSourceBase::StopArgs& args) override { |
| auto outer_stop_closure = args.HandleStopAsynchronously(); |
| StopArgsImpl inner_stop_args{}; |
| uint32_t internal_instance_index = args.internal_instance_index; |
| inner_stop_args.internal_instance_index = internal_instance_index; |
| inner_stop_args.async_stop_closure = [internal_instance_index, |
| outer_stop_closure] { |
| TrackEventInternal::DisableTracing(*Registry, internal_instance_index); |
| outer_stop_closure(); |
| }; |
| |
| TrackEventInternal::OnStop(*Registry, inner_stop_args); |
| |
| // If inner_stop_args.HandleStopAsynchronously() hasn't been called, |
| // run the async closure here. |
| if (inner_stop_args.async_stop_closure) |
| std::move(inner_stop_args.async_stop_closure)(); |
| } |
| |
| void WillClearIncrementalState( |
| const DataSourceBase::ClearIncrementalStateArgs& args) override { |
| TrackEventInternal::WillClearIncrementalState(*Registry, args); |
| } |
| |
| static void Flush() { |
| Base::template Trace([](typename Base::TraceContext ctx) { ctx.Flush(); }); |
| } |
| |
| // Determine if *any* tracing category is enabled. |
| static bool IsEnabled() { |
| bool enabled = false; |
| Base::template CallIfEnabled( |
| [&](uint32_t /*instances*/) { enabled = true; }); |
| return enabled; |
| } |
| |
| // Determine if tracing for the given static category is enabled. |
| static bool IsCategoryEnabled(size_t category_index) { |
| return Registry->GetCategoryState(category_index) |
| ->load(std::memory_order_relaxed); |
| } |
| |
| // Determine if tracing for the given dynamic category is enabled. |
| static bool IsDynamicCategoryEnabled( |
| const DynamicCategory& dynamic_category) { |
| bool enabled = false; |
| Base::template Trace([&](typename Base::TraceContext ctx) { |
| enabled = enabled || IsDynamicCategoryEnabled(&ctx, dynamic_category); |
| }); |
| return enabled; |
| } |
| |
| // 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 <typename Callback> |
| static void CallIfCategoryEnabled(size_t category_index, |
| Callback callback) PERFETTO_ALWAYS_INLINE { |
| Base::template CallIfEnabled<CategoryTracePointTraits>( |
| [&callback](uint32_t instances) { callback(instances); }, |
| {category_index}); |
| } |
| |
| // Once we've determined tracing to be enabled for this category, actually |
| // write a trace event onto this thread's default track. Outlined to avoid |
| // bloating code (mostly stack depth) at the actual trace point. |
| // |
| // The following combination of parameters is supported (in the given order): |
| // - Zero or one track, |
| // - Zero or one custom timestamp, |
| // - Arbitrary number of debug annotations. |
| // - Zero or one lambda. |
| |
| // Trace point which does not take a track or timestamp. |
| template <typename CategoryType, |
| typename EventNameType, |
| typename... Arguments> |
| static void TraceForCategory(uint32_t instances, |
| const CategoryType& category, |
| const EventNameType& event_name, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| Arguments&&... args) PERFETTO_NO_INLINE { |
| TraceForCategoryImpl(instances, category, event_name, type, |
| TrackEventInternal::kDefaultTrack, |
| TrackEventInternal::GetTraceTime(), |
| std::forward<Arguments>(args)...); |
| } |
| |
| // Trace point which takes a track, but not timestamp. |
| // NOTE: Here track should be captured using universal reference (TrackType&&) |
| // instead of const TrackType& to ensure that the proper overload is selected |
| // (otherwise the compiler will fail to disambiguate between adding const& and |
| // parsing track as a part of Arguments...). |
| template <typename TrackType, |
| typename CategoryType, |
| typename EventNameType, |
| typename... Arguments, |
| typename TrackTypeCheck = typename std::enable_if< |
| std::is_convertible<TrackType, Track>::value>::type> |
| static void TraceForCategory(uint32_t instances, |
| const CategoryType& category, |
| const EventNameType& event_name, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| TrackType&& track, |
| Arguments&&... args) PERFETTO_NO_INLINE { |
| TraceForCategoryImpl( |
| instances, category, event_name, type, std::forward<TrackType>(track), |
| TrackEventInternal::GetTraceTime(), std::forward<Arguments>(args)...); |
| } |
| |
| // Trace point which takes a timestamp, but not track. |
| template <typename CategoryType, |
| typename EventNameType, |
| typename TimestampType = uint64_t, |
| typename... Arguments, |
| typename TimestampTypeCheck = typename std::enable_if< |
| IsValidTimestamp<TimestampType>()>::type> |
| static void TraceForCategory(uint32_t instances, |
| const CategoryType& category, |
| const EventNameType& event_name, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| TimestampType&& timestamp, |
| Arguments&&... args) PERFETTO_NO_INLINE { |
| TraceForCategoryImpl(instances, category, event_name, type, |
| TrackEventInternal::kDefaultTrack, |
| std::forward<TimestampType>(timestamp), |
| std::forward<Arguments>(args)...); |
| } |
| |
| // Trace point which takes a timestamp and a track. |
| template <typename TrackType, |
| typename CategoryType, |
| typename EventNameType, |
| typename TimestampType = uint64_t, |
| typename... Arguments, |
| typename TrackTypeCheck = typename std::enable_if< |
| std::is_convertible<TrackType, Track>::value>::type, |
| typename TimestampTypeCheck = typename std::enable_if< |
| IsValidTimestamp<TimestampType>()>::type> |
| static void TraceForCategory(uint32_t instances, |
| const CategoryType& category, |
| const EventNameType& event_name, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| TrackType&& track, |
| TimestampType&& timestamp, |
| Arguments&&... args) PERFETTO_NO_INLINE { |
| TraceForCategoryImpl(instances, category, event_name, type, |
| std::forward<TrackType>(track), |
| std::forward<TimestampType>(timestamp), |
| std::forward<Arguments>(args)...); |
| } |
| |
| // Trace point with with a counter sample. |
| template <typename CategoryType, typename EventNameType, typename ValueType> |
| static void TraceForCategory(uint32_t instances, |
| const CategoryType& category, |
| const EventNameType&, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| CounterTrack track, |
| ValueType value) PERFETTO_ALWAYS_INLINE { |
| PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER); |
| TraceForCategory(instances, category, /*name=*/nullptr, type, track, |
| TrackEventInternal::GetTraceTime(), value); |
| } |
| |
| // Trace point with with a timestamp and a counter sample. |
| template <typename CategoryType, |
| typename EventNameType, |
| typename TimestampType = uint64_t, |
| typename TimestampTypeCheck = typename std::enable_if< |
| IsValidTimestamp<TimestampType>()>::type, |
| typename ValueType> |
| static void TraceForCategory(uint32_t instances, |
| const CategoryType& category, |
| const EventNameType&, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| CounterTrack track, |
| TimestampType timestamp, |
| ValueType value) PERFETTO_ALWAYS_INLINE { |
| PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER); |
| TraceForCategoryImpl( |
| instances, category, /*name=*/nullptr, type, track, timestamp, |
| [&](EventContext event_ctx) { |
| if (std::is_integral<ValueType>::value) { |
| int64_t value_int64 = static_cast<int64_t>(value); |
| if (track.is_incremental()) { |
| TrackEventIncrementalState* incr_state = |
| event_ctx.GetIncrementalState(); |
| PERFETTO_DCHECK(incr_state != nullptr); |
| auto prv_value = |
| incr_state->last_counter_value_per_track[track.uuid]; |
| event_ctx.event()->set_counter_value(value_int64 - prv_value); |
| prv_value = value_int64; |
| incr_state->last_counter_value_per_track[track.uuid] = prv_value; |
| } else { |
| event_ctx.event()->set_counter_value(value_int64); |
| } |
| } else { |
| event_ctx.event()->set_double_counter_value( |
| static_cast<double>(value)); |
| } |
| }); |
| } |
| |
| // Additional trace points used in legacy macros. |
| // It's possible to implement legacy macros using a common TraceForCategory, |
| // by supplying a lambda that sets all necessary legacy fields. But this |
| // results in a binary size bloat because every trace point generates its own |
| // template instantiation with its own lambda. ICF can't eliminate those as |
| // each lambda captures different variables and so the code is not completely |
| // identical. |
| // What we do instead is define additional TraceForCategoryLegacy templates |
| // that take legacy arguments directly. Their instantiations can have the same |
| // binary code for at least some macro invocations and so can be successfully |
| // folded by the linker. |
| #if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS |
| template <typename TrackType, |
| typename CategoryType, |
| typename EventNameType, |
| typename... Arguments, |
| typename TrackTypeCheck = typename std::enable_if< |
| std::is_convertible<TrackType, Track>::value>::type> |
| static void TraceForCategoryLegacy( |
| uint32_t instances, |
| const CategoryType& category, |
| const EventNameType& event_name, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| TrackType&& track, |
| char phase, |
| uint32_t flags, |
| Arguments&&... args) PERFETTO_NO_INLINE { |
| TraceForCategoryImpl(instances, category, event_name, type, track, |
| TrackEventInternal::GetTraceTime(), |
| [&](perfetto::EventContext ctx) |
| PERFETTO_NO_THREAD_SAFETY_ANALYSIS { |
| using ::perfetto::internal::TrackEventLegacy; |
| TrackEventLegacy::WriteLegacyEvent( |
| std::move(ctx), phase, flags, args...); |
| }); |
| } |
| |
| template <typename TrackType, |
| typename CategoryType, |
| typename EventNameType, |
| typename TimestampType = uint64_t, |
| typename... Arguments, |
| typename TrackTypeCheck = typename std::enable_if< |
| std::is_convertible<TrackType, Track>::value>::type, |
| typename TimestampTypeCheck = typename std::enable_if< |
| IsValidTimestamp<TimestampType>()>::type> |
| static void TraceForCategoryLegacy( |
| uint32_t instances, |
| const CategoryType& category, |
| const EventNameType& event_name, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| TrackType&& track, |
| char phase, |
| uint32_t flags, |
| TimestampType&& timestamp, |
| Arguments&&... args) PERFETTO_NO_INLINE { |
| TraceForCategoryImpl( |
| instances, category, event_name, type, track, timestamp, |
| [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS { |
| using ::perfetto::internal::TrackEventLegacy; |
| TrackEventLegacy::WriteLegacyEvent(std::move(ctx), phase, flags, |
| args...); |
| }); |
| } |
| |
| template <typename TrackType, |
| typename CategoryType, |
| typename EventNameType, |
| typename ThreadIdType, |
| typename LegacyIdType, |
| typename... Arguments, |
| typename TrackTypeCheck = typename std::enable_if< |
| std::is_convertible<TrackType, Track>::value>::type> |
| static void TraceForCategoryLegacyWithId( |
| uint32_t instances, |
| const CategoryType& category, |
| const EventNameType& event_name, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| TrackType&& track, |
| char phase, |
| uint32_t flags, |
| ThreadIdType thread_id, |
| LegacyIdType legacy_id, |
| Arguments&&... args) PERFETTO_NO_INLINE { |
| TraceForCategoryImpl( |
| instances, category, event_name, type, track, |
| TrackEventInternal::GetTraceTime(), |
| [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS { |
| using ::perfetto::internal::TrackEventLegacy; |
| ::perfetto::internal::LegacyTraceId trace_id{legacy_id}; |
| TrackEventLegacy::WriteLegacyEventWithIdAndTid( |
| std::move(ctx), phase, flags, trace_id, thread_id, args...); |
| }); |
| } |
| |
| template <typename TrackType, |
| typename CategoryType, |
| typename EventNameType, |
| typename ThreadIdType, |
| typename LegacyIdType, |
| typename TimestampType = uint64_t, |
| typename... Arguments, |
| typename TrackTypeCheck = typename std::enable_if< |
| std::is_convertible<TrackType, Track>::value>::type, |
| typename TimestampTypeCheck = typename std::enable_if< |
| IsValidTimestamp<TimestampType>()>::type> |
| static void TraceForCategoryLegacyWithId( |
| uint32_t instances, |
| const CategoryType& category, |
| const EventNameType& event_name, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| TrackType&& track, |
| char phase, |
| uint32_t flags, |
| ThreadIdType thread_id, |
| LegacyIdType legacy_id, |
| TimestampType&& timestamp, |
| Arguments&&... args) PERFETTO_NO_INLINE { |
| TraceForCategoryImpl( |
| instances, category, event_name, type, track, timestamp, |
| [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS { |
| using ::perfetto::internal::TrackEventLegacy; |
| ::perfetto::internal::LegacyTraceId trace_id{legacy_id}; |
| TrackEventLegacy::WriteLegacyEventWithIdAndTid( |
| std::move(ctx), phase, flags, trace_id, thread_id, args...); |
| }); |
| } |
| #endif // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS |
| |
| // Initialize the track event library. Should be called before tracing is |
| // enabled. |
| static bool Register() { |
| // Registration is performed out-of-line so users don't need to depend on |
| // DataSourceDescriptor C++ bindings. |
| return TrackEventInternal::Initialize( |
| *Registry, |
| [](const DataSourceDescriptor& dsd) { return Base::Register(dsd); }); |
| } |
| |
| // Record metadata about different types of timeline tracks. See Track. |
| static void SetTrackDescriptor(const Track& track, |
| const protos::gen::TrackDescriptor& desc) { |
| PERFETTO_DCHECK(track.uuid == desc.uuid()); |
| TrackRegistry::Get()->UpdateTrack(track, desc.SerializeAsString()); |
| Base::template Trace([&](typename Base::TraceContext ctx) { |
| TrackEventInternal::WriteTrackDescriptor( |
| track, ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(), |
| *ctx.GetCustomTlsState(), TrackEventInternal::GetTraceTime()); |
| }); |
| } |
| |
| // DEPRECATED. Only kept for backwards compatibility. |
| static void SetTrackDescriptor( |
| const Track& track, |
| std::function<void(protos::pbzero::TrackDescriptor*)> callback) { |
| SetTrackDescriptorImpl(track, std::move(callback)); |
| } |
| |
| // DEPRECATED. Only kept for backwards compatibility. |
| static void SetProcessDescriptor( |
| std::function<void(protos::pbzero::TrackDescriptor*)> callback, |
| const ProcessTrack& track = ProcessTrack::Current()) { |
| SetTrackDescriptorImpl(std::move(track), std::move(callback)); |
| } |
| |
| // DEPRECATED. Only kept for backwards compatibility. |
| static void SetThreadDescriptor( |
| std::function<void(protos::pbzero::TrackDescriptor*)> callback, |
| const ThreadTrack& track = ThreadTrack::Current()) { |
| SetTrackDescriptorImpl(std::move(track), std::move(callback)); |
| } |
| |
| static void EraseTrackDescriptor(const Track& track) { |
| TrackRegistry::Get()->EraseTrack(track); |
| } |
| |
| // Returns the current trace timestamp in nanoseconds. Note the returned |
| // timebase may vary depending on the platform, but will always match the |
| // timestamps recorded by track events (see GetTraceClockId). |
| static uint64_t GetTraceTimeNs() { return TrackEventInternal::GetTimeNs(); } |
| |
| // Returns the type of clock used by GetTraceTimeNs(). |
| static constexpr protos::pbzero::BuiltinClock GetTraceClockId() { |
| return TrackEventInternal::GetClockId(); |
| } |
| |
| const protos::gen::TrackEventConfig& GetConfig() const { return config_; } |
| |
| private: |
| // Each category has its own enabled/disabled state, stored in the category |
| // registry. |
| struct CategoryTracePointTraits { |
| // Each trace point with a static category has an associated category index. |
| struct TracePointData { |
| size_t category_index; |
| }; |
| // Called to get the enabled state bitmap of a given category. |
| // |data| is the trace point data structure given to |
| // DataSource::TraceWithInstances. |
| static constexpr std::atomic<uint8_t>* GetActiveInstances( |
| TracePointData data) { |
| return Registry->GetCategoryState(data.category_index); |
| } |
| }; |
| |
| template <typename CategoryType, |
| typename EventNameType, |
| typename TrackType = Track, |
| typename TimestampType = uint64_t, |
| typename TimestampTypeCheck = typename std::enable_if< |
| IsValidTimestamp<TimestampType>()>::type, |
| typename TrackTypeCheck = |
| typename std::enable_if<IsValidTrack<TrackType>()>::type> |
| static perfetto::EventContext WriteTrackEvent( |
| typename Base::TraceContext& ctx, |
| const CategoryType& category, |
| const EventNameType& event_name, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| const TrackType& track, |
| const TimestampType& timestamp) PERFETTO_NO_INLINE { |
| using CatTraits = CategoryTraits<CategoryType>; |
| const Category* static_category = |
| CatTraits::GetStaticCategory(Registry, category); |
| |
| const TrackEventTlsState& tls_state = *ctx.GetCustomTlsState(); |
| TraceTimestamp trace_timestamp = ::perfetto::TraceTimestampTraits< |
| TimestampType>::ConvertTimestampToTraceTimeNs(timestamp); |
| |
| TraceWriterBase* trace_writer = ctx.tls_inst_->trace_writer.get(); |
| // Make sure incremental state is valid. |
| TrackEventIncrementalState* incr_state = ctx.GetIncrementalState(); |
| TrackEventInternal::ResetIncrementalStateIfRequired( |
| trace_writer, incr_state, tls_state, trace_timestamp); |
| |
| // Write the track descriptor before any event on the track. |
| if (track) { |
| TrackEventInternal::WriteTrackDescriptorIfNeeded( |
| track, trace_writer, incr_state, tls_state, trace_timestamp); |
| } |
| |
| // Write the event itself. |
| bool on_current_thread_track = |
| (&track == &TrackEventInternal::kDefaultTrack); |
| auto event_ctx = TrackEventInternal::WriteEvent( |
| trace_writer, incr_state, tls_state, static_category, type, |
| trace_timestamp, on_current_thread_track); |
| // event name should be emitted with `TRACE_EVENT_BEGIN` macros |
| // but not with `TRACE_EVENT_END`. |
| if (type != protos::pbzero::TrackEvent::TYPE_SLICE_END) { |
| TrackEventInternal::WriteEventName(event_name, event_ctx, tls_state); |
| } |
| // Write dynamic categories (except for events that don't require |
| // categories). For counter events, the counter name (and optional |
| // category) is stored as part of the track descriptor instead being |
| // recorded with individual events. |
| if (CatTraits::kIsDynamic && |
| type != protos::pbzero::TrackEvent::TYPE_SLICE_END && |
| type != protos::pbzero::TrackEvent::TYPE_COUNTER) { |
| DynamicCategory dynamic_category = |
| CatTraits::GetDynamicCategory(category); |
| Category cat = Category::FromDynamicCategory(dynamic_category); |
| cat.ForEachGroupMember([&](const char* member_name, size_t name_size) { |
| event_ctx.event()->add_categories(member_name, name_size); |
| return true; |
| }); |
| } |
| if (type == protos::pbzero::TrackEvent::TYPE_UNSPECIFIED) { |
| // Explicitly clear the track, so that the event is not associated |
| // with the default track, but instead uses the legacy mechanism |
| // based on the phase and pid/tid override. |
| event_ctx.event()->set_track_uuid(0); |
| } else if (!on_current_thread_track) { |
| // We emit these events using TrackDescriptors, and we cannot emit |
| // events on behalf of other processes using the TrackDescriptor |
| // format. Chrome is the only user of events with explicit process |
| // ids and currently only Chrome emits PHASE_MEMORY_DUMP events |
| // with an explicit process id, so we should be fine here. |
| // TODO(mohitms): Get rid of events with explicit process ids |
| // entirely. |
| event_ctx.event()->set_track_uuid(track.uuid); |
| } |
| |
| return event_ctx; |
| } |
| |
| template <typename CategoryType, |
| typename EventNameType, |
| typename TrackType = Track, |
| typename TimestampType = uint64_t, |
| typename TimestampTypeCheck = typename std::enable_if< |
| IsValidTimestamp<TimestampType>()>::type, |
| typename TrackTypeCheck = |
| typename std::enable_if<IsValidTrack<TrackType>()>::type, |
| typename... Arguments> |
| static void TraceForCategoryImpl( |
| uint32_t instances, |
| const CategoryType& category, |
| const EventNameType& event_name, |
| perfetto::protos::pbzero::TrackEvent::Type type, |
| const TrackType& track, |
| const TimestampType& timestamp, |
| Arguments&&... args) PERFETTO_ALWAYS_INLINE { |
| using CatTraits = CategoryTraits<CategoryType>; |
| TraceWithInstances( |
| instances, category, [&](typename Base::TraceContext ctx) { |
| // If this category is dynamic, first check whether it's enabled. |
| if (CatTraits::kIsDynamic && |
| !IsDynamicCategoryEnabled( |
| &ctx, CatTraits::GetDynamicCategory(category))) { |
| return; |
| } |
| |
| auto event_ctx = WriteTrackEvent(ctx, category, event_name, type, |
| track, timestamp); |
| WriteTrackEventArgs(std::move(event_ctx), |
| std::forward<Arguments>(args)...); |
| }); |
| } |
| |
| template <typename CategoryType, typename Lambda> |
| static void TraceWithInstances(uint32_t instances, |
| const CategoryType& category, |
| Lambda lambda) PERFETTO_ALWAYS_INLINE { |
| using CatTraits = CategoryTraits<CategoryType>; |
| if (CatTraits::kIsDynamic) { |
| Base::template TraceWithInstances(instances, std::move(lambda)); |
| } else { |
| Base::template TraceWithInstances<CategoryTracePointTraits>( |
| instances, std::move(lambda), {CatTraits::GetStaticIndex(category)}); |
| } |
| } |
| |
| // Records a track descriptor into the track descriptor registry and, if we |
| // are tracing, also mirrors the descriptor into the trace. |
| template <typename TrackType> |
| static void SetTrackDescriptorImpl( |
| const TrackType& track, |
| std::function<void(protos::pbzero::TrackDescriptor*)> callback) { |
| TrackRegistry::Get()->UpdateTrack(track, std::move(callback)); |
| Base::template Trace([&](typename Base::TraceContext ctx) { |
| TrackEventInternal::WriteTrackDescriptor( |
| track, ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(), |
| *ctx.GetCustomTlsState(), TrackEventInternal::GetTraceTime()); |
| }); |
| } |
| |
| // Determines if the given dynamic category is enabled, first by checking the |
| // per-trace writer cache or by falling back to computing it based on the |
| // trace config for the given session. |
| static bool IsDynamicCategoryEnabled( |
| typename Base::TraceContext* ctx, |
| const DynamicCategory& dynamic_category) { |
| auto incr_state = ctx->GetIncrementalState(); |
| auto it = incr_state->dynamic_categories.find(dynamic_category.name); |
| if (it == incr_state->dynamic_categories.end()) { |
| // We haven't seen this category before. Let's figure out if it's enabled. |
| // This requires grabbing a lock to read the session's trace config. |
| auto ds = ctx->GetDataSourceLocked(); |
| Category category{Category::FromDynamicCategory(dynamic_category)}; |
| bool enabled = TrackEventInternal::IsCategoryEnabled( |
| *Registry, ds->config_, category); |
| // TODO(skyostil): Cap the size of |dynamic_categories|. |
| incr_state->dynamic_categories[dynamic_category.name] = enabled; |
| return enabled; |
| } |
| return it->second; |
| } |
| |
| // Config for the current tracing session. |
| protos::gen::TrackEventConfig config_; |
| }; |
| |
| } // namespace internal |
| } // namespace perfetto |
| |
| #endif // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_ |