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