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