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