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