• 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_TRACK_H_
18 #define INCLUDE_PERFETTO_TRACING_TRACK_H_
19 
20 #include "perfetto/base/export.h"
21 #include "perfetto/base/proc_utils.h"
22 #include "perfetto/base/thread_utils.h"
23 #include "perfetto/protozero/message_handle.h"
24 #include "perfetto/protozero/scattered_heap_buffer.h"
25 #include "perfetto/tracing/internal/fnv1a.h"
26 #include "perfetto/tracing/internal/tracing_muxer.h"
27 #include "perfetto/tracing/platform.h"
28 #include "perfetto/tracing/string_helpers.h"
29 #include "protos/perfetto/trace/trace_packet.pbzero.h"  // IWYU pragma: export
30 #include "protos/perfetto/trace/track_event/counter_descriptor.gen.h"  // IWYU pragma: export
31 #include "protos/perfetto/trace/track_event/counter_descriptor.pbzero.h"  // IWYU pragma: export
32 #include "protos/perfetto/trace/track_event/process_descriptor.gen.h"  // IWYU pragma: export
33 #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"  // IWYU pragma: export
34 #include "protos/perfetto/trace/track_event/thread_descriptor.gen.h"  // IWYU pragma: export
35 #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"  // IWYU pragma: export
36 #include "protos/perfetto/trace/track_event/track_descriptor.gen.h"  // IWYU pragma: export
37 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"  // IWYU pragma: export
38 
39 #include <stdint.h>
40 #include <map>
41 #include <mutex>
42 #include <optional>
43 
44 namespace perfetto {
45 namespace internal {
46 class TrackRegistry;
47 }
48 class Flow;
49 class TerminatingFlow;
50 
51 // Track events are recorded on a timeline track, which maintains the relative
52 // time ordering of all events on that track. Each thread has its own default
53 // track (ThreadTrack), which is by default where all track events are written.
54 // Thread tracks are grouped under their hosting process (ProcessTrack).
55 
56 // Events which aren't strictly scoped to a thread or a process, or don't
57 // correspond to synchronous code execution on a thread can use a custom
58 // track (Track, ThreadTrack or ProcessTrack). A Track object can also
59 // optionally be parented to a thread or a process.
60 //
61 // A track is represented by a uuid, which must be unique across the entire
62 // recorded trace.
63 //
64 // For example, to record an event that begins and ends on different threads,
65 // use a matching id to tie the begin and end events together:
66 //
67 //   TRACE_EVENT_BEGIN("category", "AsyncEvent", perfetto::Track(8086));
68 //   ...
69 //   TRACE_EVENT_END("category", perfetto::Track(8086));
70 //
71 // Tracks can also be annotated with metadata:
72 //
73 //   auto desc = track.Serialize();
74 //   desc.set_name("MyTrack");
75 //   perfetto::TrackEvent::SetTrackDescriptor(track, desc);
76 //
77 // Threads and processes can also be named in a similar way, e.g.:
78 //
79 //   auto desc = perfetto::ProcessTrack::Current().Serialize();
80 //   desc.mutable_process()->set_process_name("MyProcess");
81 //   perfetto::TrackEvent::SetTrackDescriptor(
82 //       perfetto::ProcessTrack::Current(), desc);
83 //
84 // The metadata remains valid between tracing sessions. To free up data for a
85 // track, call EraseTrackDescriptor:
86 //
87 //   perfetto::TrackEvent::EraseTrackDescriptor(track);
88 //
89 struct PERFETTO_EXPORT_COMPONENT Track {
90   const uint64_t uuid;
91   const uint64_t parent_uuid;
TrackTrack92   constexpr Track() : uuid(0), parent_uuid(0) {}
93 
94   // Construct a track with identifier |id|, optionally parented under |parent|.
95   // If no parent is specified, the track's parent is the current process's
96   // track.
97   //
98   // To minimize the chances for accidental id collisions across processes, the
99   // track's effective uuid is generated by xorring |id| with a random,
100   // per-process cookie.
101   explicit constexpr Track(uint64_t id, Track parent = MakeProcessTrack())
102       : uuid(id ^ parent.uuid), parent_uuid(parent.uuid) {}
103 
104   explicit operator bool() const { return uuid; }
105   void Serialize(protos::pbzero::TrackDescriptor*) const;
106   protos::gen::TrackDescriptor Serialize() const;
107 
108   // Construct a global track with identifier |id|.
109   //
110   // Beware: the globally unique |id| should be chosen carefully to avoid
111   // accidental clashes with track identifiers emitted by other producers.
GlobalTrack112   static Track Global(uint64_t id) { return Track(id, Track()); }
113 
114   // Construct a track using |ptr| as identifier.
115   static Track FromPointer(const void* ptr, Track parent = MakeProcessTrack()) {
116     // Using pointers as global TrackIds isn't supported as pointers are
117     // per-proccess and the same pointer value can be used in different
118     // processes. If you hit this check but are providing no |parent| track,
119     // verify that Tracing::Initialize() was called for the current process.
120     PERFETTO_DCHECK(parent.uuid != Track().uuid);
121 
122     return Track(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr)),
123                  parent);
124   }
125 
126   // Construct a track using |ptr| as identifier within thread-scope.
127   // Shorthand for `Track::FromPointer(ptr, ThreadTrack::Current())`
128   // Usage: TRACE_EVENT_BEGIN("...", "...", perfetto::Track::ThreadScoped(this))
129   static Track ThreadScoped(const void* ptr, Track parent = Track());
130 
131  protected:
TrackTrack132   constexpr Track(uint64_t uuid_, uint64_t parent_uuid_)
133       : uuid(uuid_), parent_uuid(parent_uuid_) {}
134 
MakeThreadTrackTrack135   static Track MakeThreadTrack(base::PlatformThreadId tid) {
136     // If tid were 0 here (which is an invalid tid), we would create a thread
137     // track with a uuid that conflicts with the corresponding ProcessTrack.
138     PERFETTO_DCHECK(tid != 0);
139     return Track(static_cast<uint64_t>(tid), MakeProcessTrack());
140   }
141 
MakeProcessTrackTrack142   static Track MakeProcessTrack() { return Track(process_uuid, Track()); }
143 
CompileTimeHashTrack144   static constexpr inline uint64_t CompileTimeHash(const char* string) {
145     return internal::Fnv1a(string);
146   }
147 
148  private:
149   friend class internal::TrackRegistry;
150   friend class Flow;
151   friend class TerminatingFlow;
152   static uint64_t process_uuid;
153 };
154 
155 // A process track represents events that describe the state of the entire
156 // application (e.g., counter events). Currently a ProcessTrack can only
157 // represent the current process.
158 struct PERFETTO_EXPORT_COMPONENT ProcessTrack : public Track {
159   const base::PlatformProcessId pid;
160 
CurrentProcessTrack161   static ProcessTrack Current() { return ProcessTrack(); }
162 
163   void Serialize(protos::pbzero::TrackDescriptor*) const;
164   protos::gen::TrackDescriptor Serialize() const;
165 
166  private:
ProcessTrackProcessTrack167   ProcessTrack()
168       : Track(MakeProcessTrack()), pid(Platform::GetCurrentProcessId()) {}
169 };
170 
171 // A thread track is associated with a specific thread of execution. Currently
172 // only threads in the current process can be referenced.
173 struct PERFETTO_EXPORT_COMPONENT ThreadTrack : public Track {
174   const base::PlatformProcessId pid;
175   const base::PlatformThreadId tid;
176   bool disallow_merging_with_system_tracks = false;
177 
178   static ThreadTrack Current();
179 
180   // Represents a thread in the current process.
181   static ThreadTrack ForThread(base::PlatformThreadId tid_);
182 
183   void Serialize(protos::pbzero::TrackDescriptor*) const;
184   protos::gen::TrackDescriptor Serialize() const;
185 
186  private:
ThreadTrackThreadTrack187   explicit ThreadTrack(base::PlatformThreadId tid_,
188                        bool disallow_merging_with_system_tracks_)
189       : Track(MakeThreadTrack(tid_)),
190         pid(ProcessTrack::Current().pid),
191         tid(tid_),
192         disallow_merging_with_system_tracks(
193             disallow_merging_with_system_tracks_) {}
194 };
195 
196 // A track that's identified by an explcit name, id and its parent.
197 class PERFETTO_EXPORT_COMPONENT NamedTrack : public Track {
198   // A random value mixed into named track uuids to avoid collisions with
199   // other types of tracks.
200   static constexpr uint64_t kNamedTrackMagic = 0xCD571EC5EAD37024ul;
201 
202  public:
203   // `name` is hashed to get a uuid identifying the track. Optionally specify
204   // `id` to differentiate between multiple tracks with the same `name` and
205   // `parent`.
206   NamedTrack(DynamicString name,
207              uint64_t id = 0,
208              Track parent = MakeProcessTrack())
209       : Track(id ^ internal::Fnv1a(name.value, name.length) ^ kNamedTrackMagic,
210               parent),
211         static_name_(nullptr),
212         dynamic_name_(name) {}
213 
214   constexpr NamedTrack(StaticString name,
215                        uint64_t id = 0,
216                        Track parent = MakeProcessTrack())
217       : Track(id ^ internal::Fnv1a(name.value) ^ kNamedTrackMagic, parent),
218         static_name_(name) {}
219 
220   // Construct a track using `name` and `id` as identifier within thread-scope.
221   // Shorthand for `Track::NamedTrack("name", id, ThreadTrack::Current())`
222   // Usage: TRACE_EVENT_BEGIN("...", "...",
223   // perfetto::NamedTrack::ThreadScoped("rendering"))
224   template <class TrackEventName>
225   static NamedTrack ThreadScoped(TrackEventName name,
226                                  uint64_t id = 0,
227                                  Track parent = Track()) {
228     if (parent.uuid == 0)
229       return NamedTrack(std::forward<TrackEventName>(name), id,
230                         ThreadTrack::Current());
231     return NamedTrack(std::forward<TrackEventName>(name), id, parent);
232   }
233 
234   void Serialize(protos::pbzero::TrackDescriptor*) const;
235   protos::gen::TrackDescriptor Serialize() const;
236 
237  private:
238   StaticString static_name_;
239   DynamicString dynamic_name_;
240 };
241 
242 // A track for recording counter values with the TRACE_COUNTER macro. Counter
243 // tracks can optionally be given units and other metadata. See
244 // /protos/perfetto/trace/track_event/counter_descriptor.proto for details.
245 class PERFETTO_EXPORT_COMPONENT CounterTrack : public Track {
246   // A random value mixed into counter track uuids to avoid collisions with
247   // other types of tracks.
248   static constexpr uint64_t kCounterMagic = 0xb1a4a67d7970839eul;
249 
250  public:
251   using Unit = perfetto::protos::pbzero::CounterDescriptor::Unit;
252   using CounterType =
253       perfetto::protos::gen::CounterDescriptor::BuiltinCounterType;
254 
255   // |name| must outlive this object.
256   constexpr explicit CounterTrack(StaticString name,
257                                   Track parent = MakeProcessTrack())
258       : CounterTrack(
259             name,
260             0u,
261             perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,
262             nullptr,
263             parent) {}
264 
265   constexpr explicit CounterTrack(StaticString name,
266                                   uint64_t id,
267                                   Track parent = MakeProcessTrack())
CounterTrack(name,id,perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,nullptr,parent)268       : CounterTrack(
269             name,
270             id,
271             perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,
272             nullptr,
273             parent) {}
274 
275   explicit CounterTrack(DynamicString name, Track parent = MakeProcessTrack())
276       : CounterTrack(
277             name,
278             0u,
279             perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,
280             nullptr,
281             parent) {}
282 
283   explicit CounterTrack(DynamicString name,
284                         uint64_t id,
285                         Track parent = MakeProcessTrack())
CounterTrack(name,id,perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,nullptr,parent)286       : CounterTrack(
287             name,
288             id,
289             perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,
290             nullptr,
291             parent) {}
292 
293   // |unit_name| is a free-form description of the unit used by this counter. It
294   // must outlive this object.
295   template <class TrackEventName>
296   constexpr CounterTrack(TrackEventName&& name,
297                          const char* unit_name,
298                          Track parent = MakeProcessTrack())
299       : CounterTrack(
300             std::forward<TrackEventName>(name),
301             0u,
302             perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,
303             unit_name,
304             parent) {}
305 
306   template <class TrackEventName>
307   constexpr CounterTrack(TrackEventName&& name,
308                          Unit unit,
309                          Track parent = MakeProcessTrack())
310       : CounterTrack(std::forward<TrackEventName>(name),
311                      0u,
312                      unit,
313                      nullptr,
314                      parent) {}
315 
316   template <class TrackEventName>
Global(TrackEventName && name,const char * unit_name)317   static constexpr CounterTrack Global(TrackEventName&& name,
318                                        const char* unit_name) {
319     return CounterTrack(std::forward<TrackEventName>(name), unit_name, Track());
320   }
321 
322   template <class TrackEventName>
Global(TrackEventName && name,Unit unit)323   static constexpr CounterTrack Global(TrackEventName&& name, Unit unit) {
324     return CounterTrack(std::forward<TrackEventName>(name), unit, Track());
325   }
326 
327   template <class TrackEventName>
Global(TrackEventName && name)328   static constexpr CounterTrack Global(TrackEventName&& name) {
329     return Global(std::forward<TrackEventName>(name), nullptr);
330   }
331 
set_unit(Unit unit)332   constexpr CounterTrack set_unit(Unit unit) const {
333     return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
334                         category_, unit, unit_name_, unit_multiplier_,
335                         is_incremental_, type_);
336   }
337 
set_type(CounterType type)338   constexpr CounterTrack set_type(CounterType type) const {
339     return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
340                         category_, unit_, unit_name_, unit_multiplier_,
341                         is_incremental_, type);
342   }
343 
set_unit_name(const char * unit_name)344   constexpr CounterTrack set_unit_name(const char* unit_name) const {
345     return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
346                         category_, unit_, unit_name, unit_multiplier_,
347                         is_incremental_, type_);
348   }
349 
set_unit_multiplier(int64_t unit_multiplier)350   constexpr CounterTrack set_unit_multiplier(int64_t unit_multiplier) const {
351     return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
352                         category_, unit_, unit_name_, unit_multiplier,
353                         is_incremental_, type_);
354   }
355 
set_category(const char * category)356   constexpr CounterTrack set_category(const char* category) const {
357     return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
358                         category, unit_, unit_name_, unit_multiplier_,
359                         is_incremental_, type_);
360   }
361 
362   constexpr CounterTrack set_is_incremental(bool is_incremental = true) const {
363     return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
364                         category_, unit_, unit_name_, unit_multiplier_,
365                         is_incremental, type_);
366   }
367 
is_incremental()368   constexpr bool is_incremental() const { return is_incremental_; }
369 
370   void Serialize(protos::pbzero::TrackDescriptor*) const;
371   protos::gen::TrackDescriptor Serialize() const;
372 
373  private:
CounterTrack(StaticString name,uint64_t id,Unit unit,const char * unit_name,Track parent)374   constexpr CounterTrack(StaticString name,
375                          uint64_t id,
376                          Unit unit,
377                          const char* unit_name,
378                          Track parent)
379       : Track(id ^ internal::Fnv1a(name.value) ^ kCounterMagic, parent),
380         static_name_(name),
381         category_(nullptr),
382         unit_(unit),
383         unit_name_(unit_name) {}
CounterTrack(DynamicString name,uint64_t id,Unit unit,const char * unit_name,Track parent)384   CounterTrack(DynamicString name,
385                uint64_t id,
386                Unit unit,
387                const char* unit_name,
388                Track parent)
389       : Track(id ^ internal::Fnv1a(name.value, name.length) ^ kCounterMagic,
390               parent),
391         static_name_(nullptr),
392         dynamic_name_(name),
393         category_(nullptr),
394         unit_(unit),
395         unit_name_(unit_name) {}
CounterTrack(uint64_t uuid_,uint64_t parent_uuid_,StaticString static_name,DynamicString dynamic_name,const char * category,Unit unit,const char * unit_name,int64_t unit_multiplier,bool is_incremental,CounterType type)396   constexpr CounterTrack(uint64_t uuid_,
397                          uint64_t parent_uuid_,
398                          StaticString static_name,
399                          DynamicString dynamic_name,
400                          const char* category,
401                          Unit unit,
402                          const char* unit_name,
403                          int64_t unit_multiplier,
404                          bool is_incremental,
405                          CounterType type)
406       : Track(uuid_, parent_uuid_),
407         static_name_(static_name),
408         dynamic_name_(dynamic_name),
409         category_(category),
410         unit_(unit),
411         unit_name_(unit_name),
412         unit_multiplier_(unit_multiplier),
413         is_incremental_(is_incremental),
414         type_(type) {}
415 
416   StaticString static_name_;
417   DynamicString dynamic_name_;
418   const char* const category_;
419   Unit unit_ = perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED;
420   const char* const unit_name_ = nullptr;
421   int64_t unit_multiplier_ = 1;
422   const bool is_incremental_ = false;
423   CounterType type_ =
424       perfetto::protos::gen::CounterDescriptor::COUNTER_UNSPECIFIED;
425 };
426 
427 namespace internal {
428 
429 // Keeps a map of uuids to serialized track descriptors and provides a
430 // thread-safe way to read and write them. Each trace writer keeps a TLS set of
431 // the tracks it has seen (see TrackEventIncrementalState). In the common case,
432 // this registry is not consulted (and no locks are taken). However when a new
433 // track is seen, this registry is used to write either 1) the default
434 // descriptor for that track (see *Track::Serialize) or 2) a serialized
435 // descriptor stored in the registry which may have additional metadata (e.g.,
436 // track name).
437 // TODO(eseckler): Remove PERFETTO_EXPORT_COMPONENT once Chromium no longer
438 // calls TrackRegistry::InitializeInstance() directly.
439 class PERFETTO_EXPORT_COMPONENT TrackRegistry {
440  public:
441   using SerializedTrackDescriptor = std::string;
442   struct TrackInfo {
443     SerializedTrackDescriptor desc;
444     uint64_t parent_uuid = 0;
445   };
446 
447   TrackRegistry();
448   ~TrackRegistry();
449 
450   static void InitializeInstance();
451   static void ResetForTesting();
452   static uint64_t ComputeProcessUuid();
Get()453   static TrackRegistry* Get() { return instance_; }
454 
455   void EraseTrack(Track);
456 
457   // This variant lets the user supply a serialized track descriptor directly.
458   void UpdateTrack(Track, const std::string& serialized_desc);
459 
460   // If |track| exists in the registry, write out the serialized track
461   // descriptor for it into |packet|. Otherwise just the ephemeral track object
462   // is serialized without any additional metadata.
463   //
464   // Returns the parent track uuid.
465   template <typename TrackType>
SerializeTrack(const TrackType & track,protozero::MessageHandle<protos::pbzero::TracePacket> packet)466   uint64_t SerializeTrack(
467       const TrackType& track,
468       protozero::MessageHandle<protos::pbzero::TracePacket> packet) {
469     // If the track has extra metadata (recorded with UpdateTrack), it will be
470     // found in the registry. To minimize the time the lock is held, make a copy
471     // of the data held in the registry and write it outside the lock.
472     auto track_info = FindTrackInfo(track.uuid);
473     if (track_info) {
474       WriteTrackDescriptor(std::move(track_info->desc), std::move(packet));
475       return track_info->parent_uuid;
476     } else {
477       // Otherwise we just write the basic descriptor for this type of track
478       // (e.g., just uuid, no name).
479       track.Serialize(packet->set_track_descriptor());
480       return track.parent_uuid;
481     }
482   }
483 
484   // If saved in the registry, returns the serialize track descriptor and parent
485   // uuid for `uuid`.
FindTrackInfo(uint64_t uuid)486   std::optional<TrackInfo> FindTrackInfo(uint64_t uuid) {
487     std::optional<TrackInfo> track_info;
488     {
489       std::lock_guard<std::mutex> lock(mutex_);
490       const auto it = tracks_.find(uuid);
491       if (it != tracks_.end()) {
492         track_info = it->second;
493         PERFETTO_DCHECK(!track_info->desc.empty());
494       }
495     }
496     return track_info;
497   }
498 
499   static void WriteTrackDescriptor(
500       const SerializedTrackDescriptor& desc,
501       protozero::MessageHandle<protos::pbzero::TracePacket> packet);
502 
503  private:
504   std::mutex mutex_;
505   std::map<uint64_t /* uuid */, TrackInfo> tracks_;
506 
507   static TrackRegistry* instance_;
508 };
509 
510 }  // namespace internal
511 }  // namespace perfetto
512 
513 #endif  // INCLUDE_PERFETTO_TRACING_TRACK_H_
514