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