• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_
18 #define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_
19 
20 #include "perfetto/base/compiler.h"
21 #include "perfetto/base/template_util.h"
22 #include "perfetto/protozero/message_handle.h"
23 #include "perfetto/tracing/core/data_source_config.h"
24 #include "perfetto/tracing/data_source.h"
25 #include "perfetto/tracing/event_context.h"
26 #include "perfetto/tracing/internal/track_event_internal.h"
27 #include "perfetto/tracing/internal/write_track_event_args.h"
28 #include "perfetto/tracing/track.h"
29 #include "perfetto/tracing/track_event_category_registry.h"
30 #include "protos/perfetto/common/builtin_clock.pbzero.h"
31 #include "protos/perfetto/config/track_event/track_event_config.gen.h"
32 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
33 
34 #include <type_traits>
35 
36 namespace perfetto {
37 
38 // A function for converting an abstract timestamp into a
39 // perfetto::TraceTimestamp struct. By specialising this template and defining
40 // static ConvertTimestampToTraceTimeNs function in it the user can register
41 // additional timestamp types. The return value should specify the
42 // clock domain used by the timestamp as well as its value.
43 //
44 // The supported clock domains are the ones described in
45 // perfetto.protos.ClockSnapshot. However, custom clock IDs (>=64) are
46 // reserved for internal use by the SDK for the time being.
47 // The timestamp value should be in nanoseconds regardless of the clock domain.
48 template <typename T>
49 struct TraceTimestampTraits;
50 
51 // A pass-through implementation for raw uint64_t nanosecond timestamps.
52 template <>
53 struct TraceTimestampTraits<uint64_t> {
54   static inline TraceTimestamp ConvertTimestampToTraceTimeNs(
55       const uint64_t& timestamp) {
56     return {static_cast<uint32_t>(internal::TrackEventInternal::GetClockId()), timestamp};
57   }
58 };
59 
60 // A pass-through implementation for the trace timestamp structure.
61 template <>
62 struct TraceTimestampTraits<TraceTimestamp> {
63   static inline TraceTimestamp ConvertTimestampToTraceTimeNs(
64       const TraceTimestamp& timestamp) {
65     return timestamp;
66   }
67 };
68 
69 namespace internal {
70 namespace {
71 
72 // Checks if |T| is a valid track.
73 template <typename T>
74 static constexpr bool IsValidTrack() {
75   return std::is_convertible<T, Track>::value;
76 }
77 
78 // Checks if |T| is a valid non-counter track.
79 template <typename T>
80 static constexpr bool IsValidNormalTrack() {
81   return std::is_convertible<T, Track>::value &&
82          !std::is_convertible<T, CounterTrack>::value;
83 }
84 
85 // Because the user can use arbitrary timestamp types, we can't compare against
86 // any known base type here. Instead, we check that a track or a trace lambda
87 // isn't being interpreted as a timestamp.
88 template <typename T,
89           typename CanBeConvertedToNsCheck = decltype(
90               ::perfetto::TraceTimestampTraits<typename base::remove_cvref_t<
91                   T>>::ConvertTimestampToTraceTimeNs(std::declval<T>())),
92           typename NotTrackCheck =
93               typename std::enable_if<!IsValidNormalTrack<T>()>::type,
94           typename NotLambdaCheck =
95               typename std::enable_if<!IsValidTraceLambda<T>()>::type>
96 static constexpr bool IsValidTimestamp() {
97   return true;
98 }
99 
100 }  // namespace
101 
102 // Traits for dynamic categories.
103 template <typename CategoryType>
104 struct CategoryTraits {
105   static constexpr bool kIsDynamic = true;
106   static constexpr const Category* GetStaticCategory(
107       const TrackEventCategoryRegistry*,
108       const CategoryType&) {
109     return nullptr;
110   }
111   static size_t GetStaticIndex(const CategoryType&) {
112     PERFETTO_DCHECK(false);  // Not reached.
113     return TrackEventCategoryRegistry::kDynamicCategoryIndex;
114   }
115   static DynamicCategory GetDynamicCategory(const CategoryType& category) {
116     return DynamicCategory{category};
117   }
118 };
119 
120 // Traits for static categories.
121 template <>
122 struct CategoryTraits<size_t> {
123   static constexpr bool kIsDynamic = false;
124   static const Category* GetStaticCategory(
125       const TrackEventCategoryRegistry* registry,
126       size_t category_index) {
127     return registry->GetCategory(category_index);
128   }
129   static constexpr size_t GetStaticIndex(size_t category_index) {
130     return category_index;
131   }
132   static DynamicCategory GetDynamicCategory(size_t) {
133     PERFETTO_DCHECK(false);  // Not reached.
134     return DynamicCategory();
135   }
136 };
137 
138 struct TrackEventDataSourceTraits : public perfetto::DefaultDataSourceTraits {
139   using IncrementalStateType = TrackEventIncrementalState;
140   using TlsStateType = TrackEventTlsState;
141 
142   // Use a one shared TLS slot so that all track event data sources write into
143   // the same sequence and share interning dictionaries.
144   static DataSourceThreadLocalState* GetDataSourceTLS(DataSourceStaticState*,
145                                                       TracingTLS* root_tls) {
146     return &root_tls->track_event_tls;
147   }
148 };
149 
150 // A generic track event data source which is instantiated once per track event
151 // category namespace.
152 template <typename DataSourceType, const TrackEventCategoryRegistry* Registry>
153 class TrackEventDataSource
154     : public DataSource<DataSourceType, TrackEventDataSourceTraits> {
155   using Base = DataSource<DataSourceType, TrackEventDataSourceTraits>;
156 
157  public:
158   // Add or remove a session observer for this track event data source. The
159   // observer will be notified about started and stopped tracing sessions.
160   // Returns |true| if the observer was successfully added (i.e., the maximum
161   // number of observers wasn't exceeded).
162   static bool AddSessionObserver(TrackEventSessionObserver* observer) {
163     return TrackEventInternal::AddSessionObserver(observer);
164   }
165 
166   static void RemoveSessionObserver(TrackEventSessionObserver* observer) {
167     TrackEventInternal::RemoveSessionObserver(observer);
168   }
169 
170   // DataSource implementation.
171   void OnSetup(const DataSourceBase::SetupArgs& args) override {
172     auto config_raw = args.config->track_event_config_raw();
173     bool ok = config_.ParseFromArray(config_raw.data(), config_raw.size());
174     PERFETTO_DCHECK(ok);
175     TrackEventInternal::EnableTracing(*Registry, config_, args);
176   }
177 
178   void OnStart(const DataSourceBase::StartArgs& args) override {
179     TrackEventInternal::OnStart(args);
180   }
181 
182   void OnStop(const DataSourceBase::StopArgs& args) override {
183     TrackEventInternal::DisableTracing(*Registry, args);
184   }
185 
186   static void Flush() {
187     Base::template Trace([](typename Base::TraceContext ctx) { ctx.Flush(); });
188   }
189 
190   // Determine if *any* tracing category is enabled.
191   static bool IsEnabled() {
192     bool enabled = false;
193     Base::template CallIfEnabled(
194         [&](uint32_t /*instances*/) { enabled = true; });
195     return enabled;
196   }
197 
198   // Determine if tracing for the given static category is enabled.
199   static bool IsCategoryEnabled(size_t category_index) {
200     return Registry->GetCategoryState(category_index)
201         ->load(std::memory_order_relaxed);
202   }
203 
204   // Determine if tracing for the given dynamic category is enabled.
205   static bool IsDynamicCategoryEnabled(
206       const DynamicCategory& dynamic_category) {
207     bool enabled = false;
208     Base::template Trace([&](typename Base::TraceContext ctx) {
209       enabled = IsDynamicCategoryEnabled(&ctx, dynamic_category);
210     });
211     return enabled;
212   }
213 
214   // This is the inlined entrypoint for all track event trace points. It tries
215   // to be as lightweight as possible in terms of instructions and aims to
216   // compile down to an unlikely conditional jump to the actual trace writing
217   // function.
218   template <typename Callback>
219   static void CallIfCategoryEnabled(size_t category_index,
220                                     Callback callback) PERFETTO_ALWAYS_INLINE {
221     Base::template CallIfEnabled<CategoryTracePointTraits>(
222         [&callback](uint32_t instances) { callback(instances); },
223         {category_index});
224   }
225 
226   // Once we've determined tracing to be enabled for this category, actually
227   // write a trace event onto this thread's default track. Outlined to avoid
228   // bloating code (mostly stack depth) at the actual trace point.
229   //
230   // The following combination of parameters is supported (in the given order):
231   // - Zero or one track,
232   // - Zero or one custom timestamp,
233   // - Arbitrary number of debug annotations.
234   // - Zero or one lambda.
235 
236   // Trace point which does not take a track or timestamp.
237   template <typename CategoryType, typename... Arguments>
238   static void TraceForCategory(uint32_t instances,
239                                const CategoryType& category,
240                                const char* event_name,
241                                perfetto::protos::pbzero::TrackEvent::Type type,
242                                Arguments&&... args) PERFETTO_NO_INLINE {
243     TraceForCategoryImpl(instances, category, event_name, type,
244                          TrackEventInternal::kDefaultTrack,
245                          TrackEventInternal::GetTraceTime(),
246                          std::forward<Arguments>(args)...);
247   }
248 
249   // Trace point which takes a track, but not timestamp.
250   // NOTE: Here track should be captured using universal reference (TrackType&&)
251   // instead of const TrackType& to ensure that the proper overload is selected
252   // (otherwise the compiler will fail to disambiguate between adding const& and
253   // parsing track as a part of Arguments...).
254   template <typename TrackType,
255             typename CategoryType,
256             typename... Arguments,
257             typename TrackTypeCheck = typename std::enable_if<
258                 std::is_convertible<TrackType, Track>::value>::type>
259   static void TraceForCategory(uint32_t instances,
260                                const CategoryType& category,
261                                const char* event_name,
262                                perfetto::protos::pbzero::TrackEvent::Type type,
263                                TrackType&& track,
264                                Arguments&&... args) PERFETTO_NO_INLINE {
265     TraceForCategoryImpl(
266         instances, category, event_name, type, std::forward<TrackType>(track),
267         TrackEventInternal::GetTraceTime(), std::forward<Arguments>(args)...);
268   }
269 
270   // Trace point which takes a timestamp, but not track.
271   template <typename CategoryType,
272             typename TimestampType = uint64_t,
273             typename... Arguments,
274             typename TimestampTypeCheck = typename std::enable_if<
275                 IsValidTimestamp<TimestampType>()>::type>
276   static void TraceForCategory(uint32_t instances,
277                                const CategoryType& category,
278                                const char* event_name,
279                                perfetto::protos::pbzero::TrackEvent::Type type,
280                                TimestampType&& timestamp,
281                                Arguments&&... args) PERFETTO_NO_INLINE {
282     TraceForCategoryImpl(instances, category, event_name, type,
283                          TrackEventInternal::kDefaultTrack,
284                          std::forward<TimestampType>(timestamp),
285                          std::forward<Arguments>(args)...);
286   }
287 
288   // Trace point which takes a timestamp and a track.
289   template <typename TrackType,
290             typename CategoryType,
291             typename TimestampType = uint64_t,
292             typename... Arguments,
293             typename TrackTypeCheck = typename std::enable_if<
294                 std::is_convertible<TrackType, Track>::value>::type,
295             typename TimestampTypeCheck = typename std::enable_if<
296                 IsValidTimestamp<TimestampType>()>::type>
297   static void TraceForCategory(uint32_t instances,
298                                const CategoryType& category,
299                                const char* event_name,
300                                perfetto::protos::pbzero::TrackEvent::Type type,
301                                TrackType&& track,
302                                TimestampType&& timestamp,
303                                Arguments&&... args) PERFETTO_NO_INLINE {
304     TraceForCategoryImpl(instances, category, event_name, type,
305                          std::forward<TrackType>(track),
306                          std::forward<TimestampType>(timestamp),
307                          std::forward<Arguments>(args)...);
308   }
309 
310   // Trace point with with a counter sample.
311   template <typename CategoryType, typename ValueType>
312   static void TraceForCategory(uint32_t instances,
313                                const CategoryType& category,
314                                const char*,
315                                perfetto::protos::pbzero::TrackEvent::Type type,
316                                CounterTrack track,
317                                ValueType value) PERFETTO_ALWAYS_INLINE {
318     PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
319     TraceForCategory(instances, category, /*name=*/nullptr, type, track,
320                      TrackEventInternal::GetTraceTime(), value);
321   }
322 
323   // Trace point with with a timestamp and a counter sample.
324   template <typename CategoryType,
325             typename TimestampType = uint64_t,
326             typename TimestampTypeCheck = typename std::enable_if<
327                 IsValidTimestamp<TimestampType>()>::type,
328             typename ValueType>
329   static void TraceForCategory(uint32_t instances,
330                                const CategoryType& category,
331                                const char*,
332                                perfetto::protos::pbzero::TrackEvent::Type type,
333                                CounterTrack track,
334                                TimestampType timestamp,
335                                ValueType value) PERFETTO_ALWAYS_INLINE {
336     PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
337     TraceForCategoryImpl(
338         instances, category, /*name=*/nullptr, type, track, timestamp,
339         [&](EventContext event_ctx) {
340           if (std::is_integral<ValueType>::value) {
341             int64_t value_int64 = static_cast<int64_t>(value);
342             if (track.is_incremental()) {
343               TrackEventIncrementalState* incr_state =
344                   event_ctx.GetIncrementalState();
345               PERFETTO_DCHECK(incr_state != nullptr);
346               auto prv_value =
347                   incr_state->last_counter_value_per_track[track.uuid];
348               event_ctx.event()->set_counter_value(value_int64 - prv_value);
349               prv_value = value_int64;
350               incr_state->last_counter_value_per_track[track.uuid] = prv_value;
351             } else {
352               event_ctx.event()->set_counter_value(value_int64);
353             }
354           } else {
355             event_ctx.event()->set_double_counter_value(
356                 static_cast<double>(value));
357           }
358         });
359   }
360 
361   // Initialize the track event library. Should be called before tracing is
362   // enabled.
363   static bool Register() {
364     // Registration is performed out-of-line so users don't need to depend on
365     // DataSourceDescriptor C++ bindings.
366     return TrackEventInternal::Initialize(
367         *Registry,
368         [](const DataSourceDescriptor& dsd) { return Base::Register(dsd); });
369   }
370 
371   // Record metadata about different types of timeline tracks. See Track.
372   static void SetTrackDescriptor(const Track& track,
373                                  const protos::gen::TrackDescriptor& desc) {
374     PERFETTO_DCHECK(track.uuid == desc.uuid());
375     TrackRegistry::Get()->UpdateTrack(track, desc.SerializeAsString());
376     Base::template Trace([&](typename Base::TraceContext ctx) {
377       TrackEventInternal::WriteTrackDescriptor(
378           track, ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
379           *ctx.GetCustomTlsState(), TrackEventInternal::GetTraceTime());
380     });
381   }
382 
383   // DEPRECATED. Only kept for backwards compatibility.
384   static void SetTrackDescriptor(
385       const Track& track,
386       std::function<void(protos::pbzero::TrackDescriptor*)> callback) {
387     SetTrackDescriptorImpl(track, std::move(callback));
388   }
389 
390   // DEPRECATED. Only kept for backwards compatibility.
391   static void SetProcessDescriptor(
392       std::function<void(protos::pbzero::TrackDescriptor*)> callback,
393       const ProcessTrack& track = ProcessTrack::Current()) {
394     SetTrackDescriptorImpl(std::move(track), std::move(callback));
395   }
396 
397   // DEPRECATED. Only kept for backwards compatibility.
398   static void SetThreadDescriptor(
399       std::function<void(protos::pbzero::TrackDescriptor*)> callback,
400       const ThreadTrack& track = ThreadTrack::Current()) {
401     SetTrackDescriptorImpl(std::move(track), std::move(callback));
402   }
403 
404   static void EraseTrackDescriptor(const Track& track) {
405     TrackRegistry::Get()->EraseTrack(track);
406   }
407 
408   // Returns the current trace timestamp in nanoseconds. Note the returned
409   // timebase may vary depending on the platform, but will always match the
410   // timestamps recorded by track events (see GetTraceClockId).
411   static uint64_t GetTraceTimeNs() { return TrackEventInternal::GetTimeNs(); }
412 
413   // Returns the type of clock used by GetTraceTimeNs().
414   static constexpr protos::pbzero::BuiltinClock GetTraceClockId() {
415     return TrackEventInternal::GetClockId();
416   }
417 
418   const protos::gen::TrackEventConfig& GetConfig() const { return config_; }
419 
420  private:
421   // Each category has its own enabled/disabled state, stored in the category
422   // registry.
423   struct CategoryTracePointTraits {
424     // Each trace point with a static category has an associated category index.
425     struct TracePointData {
426       size_t category_index;
427     };
428     // Called to get the enabled state bitmap of a given category.
429     // |data| is the trace point data structure given to
430     // DataSource::TraceWithInstances.
431     static constexpr std::atomic<uint8_t>* GetActiveInstances(
432         TracePointData data) {
433       return Registry->GetCategoryState(data.category_index);
434     }
435   };
436 
437   template <typename CategoryType,
438             typename TrackType = Track,
439             typename TimestampType = uint64_t,
440             typename TimestampTypeCheck = typename std::enable_if<
441                 IsValidTimestamp<TimestampType>()>::type,
442             typename TrackTypeCheck =
443                 typename std::enable_if<IsValidTrack<TrackType>()>::type,
444             typename... Arguments>
445   static void TraceForCategoryImpl(
446       uint32_t instances,
447       const CategoryType& category,
448       const char* event_name,
449       perfetto::protos::pbzero::TrackEvent::Type type,
450       const TrackType& track,
451       const TimestampType& timestamp,
452       Arguments&&... args) PERFETTO_ALWAYS_INLINE {
453     using CatTraits = CategoryTraits<CategoryType>;
454     const Category* static_category =
455         CatTraits::GetStaticCategory(Registry, category);
456     TraceWithInstances(
457         instances, category, [&](typename Base::TraceContext ctx) {
458           // If this category is dynamic, first check whether it's enabled.
459           if (CatTraits::kIsDynamic &&
460               !IsDynamicCategoryEnabled(
461                   &ctx, CatTraits::GetDynamicCategory(category))) {
462             return;
463           }
464 
465           const TrackEventTlsState& tls_state = *ctx.GetCustomTlsState();
466           TraceTimestamp trace_timestamp = ::perfetto::TraceTimestampTraits<
467               TimestampType>::ConvertTimestampToTraceTimeNs(timestamp);
468 
469           TraceWriterBase* trace_writer = ctx.tls_inst_->trace_writer.get();
470           // Make sure incremental state is valid.
471           TrackEventIncrementalState* incr_state = ctx.GetIncrementalState();
472           TrackEventInternal::ResetIncrementalStateIfRequired(
473               trace_writer, incr_state, tls_state, trace_timestamp);
474 
475           // Write the track descriptor before any event on the track.
476           if (track) {
477             TrackEventInternal::WriteTrackDescriptorIfNeeded(
478                 track, trace_writer, incr_state, tls_state, trace_timestamp);
479           }
480 
481           // Write the event itself.
482           {
483             auto event_ctx = TrackEventInternal::WriteEvent(
484                 trace_writer, incr_state, tls_state, static_category,
485                 event_name, type, trace_timestamp);
486             // Write dynamic categories (except for events that don't require
487             // categories). For counter events, the counter name (and optional
488             // category) is stored as part of the track descriptor instead being
489             // recorded with individual events.
490             if (CatTraits::kIsDynamic &&
491                 type != protos::pbzero::TrackEvent::TYPE_SLICE_END &&
492                 type != protos::pbzero::TrackEvent::TYPE_COUNTER) {
493               DynamicCategory dynamic_category =
494                   CatTraits::GetDynamicCategory(category);
495               Category cat = Category::FromDynamicCategory(dynamic_category);
496               cat.ForEachGroupMember(
497                   [&](const char* member_name, size_t name_size) {
498                     event_ctx.event()->add_categories(member_name, name_size);
499                     return true;
500                   });
501             }
502             if (&track != &TrackEventInternal::kDefaultTrack)
503               event_ctx.event()->set_track_uuid(track.uuid);
504             WriteTrackEventArgs(std::move(event_ctx),
505                                 std::forward<Arguments>(args)...);
506           }  // event_ctx
507         });
508   }
509 
510   template <typename CategoryType, typename Lambda>
511   static void TraceWithInstances(uint32_t instances,
512                                  const CategoryType& category,
513                                  Lambda lambda) PERFETTO_ALWAYS_INLINE {
514     using CatTraits = CategoryTraits<CategoryType>;
515     if (CatTraits::kIsDynamic) {
516       Base::template TraceWithInstances(instances, std::move(lambda));
517     } else {
518       Base::template TraceWithInstances<CategoryTracePointTraits>(
519           instances, std::move(lambda), {CatTraits::GetStaticIndex(category)});
520     }
521   }
522 
523   // Records a track descriptor into the track descriptor registry and, if we
524   // are tracing, also mirrors the descriptor into the trace.
525   template <typename TrackType>
526   static void SetTrackDescriptorImpl(
527       const TrackType& track,
528       std::function<void(protos::pbzero::TrackDescriptor*)> callback) {
529     TrackRegistry::Get()->UpdateTrack(track, std::move(callback));
530     Base::template Trace([&](typename Base::TraceContext ctx) {
531       TrackEventInternal::WriteTrackDescriptor(
532           track, ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
533           *ctx.GetCustomTlsState(), TrackEventInternal::GetTraceTime());
534     });
535   }
536 
537   // Determines if the given dynamic category is enabled, first by checking the
538   // per-trace writer cache or by falling back to computing it based on the
539   // trace config for the given session.
540   static bool IsDynamicCategoryEnabled(
541       typename Base::TraceContext* ctx,
542       const DynamicCategory& dynamic_category) {
543     auto incr_state = ctx->GetIncrementalState();
544     auto it = incr_state->dynamic_categories.find(dynamic_category.name);
545     if (it == incr_state->dynamic_categories.end()) {
546       // We haven't seen this category before. Let's figure out if it's enabled.
547       // This requires grabbing a lock to read the session's trace config.
548       auto ds = ctx->GetDataSourceLocked();
549       Category category{Category::FromDynamicCategory(dynamic_category)};
550       bool enabled = TrackEventInternal::IsCategoryEnabled(
551           *Registry, ds->config_, category);
552       // TODO(skyostil): Cap the size of |dynamic_categories|.
553       incr_state->dynamic_categories[dynamic_category.name] = enabled;
554       return enabled;
555     }
556     return it->second;
557   }
558 
559   // Config for the current tracing session.
560   protos::gen::TrackEventConfig config_;
561 };
562 
563 }  // namespace internal
564 }  // namespace perfetto
565 
566 #endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_
567