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