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" // IWYU pragma: export 30 #include "protos/perfetto/trace/track_event/counter_descriptor.gen.h" // IWYU pragma: export 31 #include "protos/perfetto/trace/track_event/counter_descriptor.pbzero.h" // IWYU pragma: export 32 #include "protos/perfetto/trace/track_event/process_descriptor.gen.h" // IWYU pragma: export 33 #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h" // IWYU pragma: export 34 #include "protos/perfetto/trace/track_event/thread_descriptor.gen.h" // IWYU pragma: export 35 #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h" // IWYU pragma: export 36 #include "protos/perfetto/trace/track_event/track_descriptor.gen.h" // IWYU pragma: export 37 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h" // IWYU pragma: export 38 39 #include <stdint.h> 40 #include <map> 41 #include <mutex> 42 #include <optional> 43 44 namespace perfetto { 45 namespace internal { 46 class TrackRegistry; 47 } 48 class Flow; 49 class TerminatingFlow; 50 51 // Track events are recorded on a timeline track, which maintains the relative 52 // time ordering of all events on that track. Each thread has its own default 53 // track (ThreadTrack), which is by default where all track events are written. 54 // Thread tracks are grouped under their hosting process (ProcessTrack). 55 56 // Events which aren't strictly scoped to a thread or a process, or don't 57 // correspond to synchronous code execution on a thread can use a custom 58 // track (Track, ThreadTrack or ProcessTrack). A Track object can also 59 // optionally be parented to a thread or a process. 60 // 61 // A track is represented by a uuid, which must be unique across the entire 62 // recorded trace. 63 // 64 // For example, to record an event that begins and ends on different threads, 65 // use a matching id to tie the begin and end events together: 66 // 67 // TRACE_EVENT_BEGIN("category", "AsyncEvent", perfetto::Track(8086)); 68 // ... 69 // TRACE_EVENT_END("category", perfetto::Track(8086)); 70 // 71 // Tracks can also be annotated with metadata: 72 // 73 // auto desc = track.Serialize(); 74 // desc.set_name("MyTrack"); 75 // perfetto::TrackEvent::SetTrackDescriptor(track, desc); 76 // 77 // Threads and processes can also be named in a similar way, e.g.: 78 // 79 // auto desc = perfetto::ProcessTrack::Current().Serialize(); 80 // desc.mutable_process()->set_process_name("MyProcess"); 81 // perfetto::TrackEvent::SetTrackDescriptor( 82 // perfetto::ProcessTrack::Current(), desc); 83 // 84 // The metadata remains valid between tracing sessions. To free up data for a 85 // track, call EraseTrackDescriptor: 86 // 87 // perfetto::TrackEvent::EraseTrackDescriptor(track); 88 // 89 struct PERFETTO_EXPORT_COMPONENT Track { 90 const uint64_t uuid; 91 const uint64_t parent_uuid; TrackTrack92 constexpr Track() : uuid(0), parent_uuid(0) {} 93 94 // Construct a track with identifier |id|, optionally parented under |parent|. 95 // If no parent is specified, the track's parent is the current process's 96 // track. 97 // 98 // To minimize the chances for accidental id collisions across processes, the 99 // track's effective uuid is generated by xorring |id| with a random, 100 // per-process cookie. 101 explicit constexpr Track(uint64_t id, Track parent = MakeProcessTrack()) 102 : uuid(id ^ parent.uuid), parent_uuid(parent.uuid) {} 103 104 explicit operator bool() const { return uuid; } 105 void Serialize(protos::pbzero::TrackDescriptor*) const; 106 protos::gen::TrackDescriptor Serialize() const; 107 108 // Construct a global track with identifier |id|. 109 // 110 // Beware: the globally unique |id| should be chosen carefully to avoid 111 // accidental clashes with track identifiers emitted by other producers. GlobalTrack112 static Track Global(uint64_t id) { return Track(id, Track()); } 113 114 // Construct a track using |ptr| as identifier. 115 static Track FromPointer(const void* ptr, Track parent = MakeProcessTrack()) { 116 // Using pointers as global TrackIds isn't supported as pointers are 117 // per-proccess and the same pointer value can be used in different 118 // processes. If you hit this check but are providing no |parent| track, 119 // verify that Tracing::Initialize() was called for the current process. 120 PERFETTO_DCHECK(parent.uuid != Track().uuid); 121 122 return Track(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr)), 123 parent); 124 } 125 126 // Construct a track using |ptr| as identifier within thread-scope. 127 // Shorthand for `Track::FromPointer(ptr, ThreadTrack::Current())` 128 // Usage: TRACE_EVENT_BEGIN("...", "...", perfetto::Track::ThreadScoped(this)) 129 static Track ThreadScoped(const void* ptr, Track parent = Track()); 130 131 protected: TrackTrack132 constexpr Track(uint64_t uuid_, uint64_t parent_uuid_) 133 : uuid(uuid_), parent_uuid(parent_uuid_) {} 134 MakeThreadTrackTrack135 static Track MakeThreadTrack(base::PlatformThreadId tid) { 136 // If tid were 0 here (which is an invalid tid), we would create a thread 137 // track with a uuid that conflicts with the corresponding ProcessTrack. 138 PERFETTO_DCHECK(tid != 0); 139 return Track(static_cast<uint64_t>(tid), MakeProcessTrack()); 140 } 141 MakeProcessTrackTrack142 static Track MakeProcessTrack() { return Track(process_uuid, Track()); } 143 CompileTimeHashTrack144 static constexpr inline uint64_t CompileTimeHash(const char* string) { 145 return internal::Fnv1a(string); 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 that's identified by an explcit name, id and its parent. 197 class PERFETTO_EXPORT_COMPONENT NamedTrack : public Track { 198 // A random value mixed into named track uuids to avoid collisions with 199 // other types of tracks. 200 static constexpr uint64_t kNamedTrackMagic = 0xCD571EC5EAD37024ul; 201 202 public: 203 // `name` is hashed to get a uuid identifying the track. Optionally specify 204 // `id` to differentiate between multiple tracks with the same `name` and 205 // `parent`. 206 NamedTrack(DynamicString name, 207 uint64_t id = 0, 208 Track parent = MakeProcessTrack()) 209 : Track(id ^ internal::Fnv1a(name.value, name.length) ^ kNamedTrackMagic, 210 parent), 211 static_name_(nullptr), 212 dynamic_name_(name) {} 213 214 constexpr NamedTrack(StaticString name, 215 uint64_t id = 0, 216 Track parent = MakeProcessTrack()) 217 : Track(id ^ internal::Fnv1a(name.value) ^ kNamedTrackMagic, parent), 218 static_name_(name) {} 219 220 // Construct a track using `name` and `id` as identifier within thread-scope. 221 // Shorthand for `Track::NamedTrack("name", id, ThreadTrack::Current())` 222 // Usage: TRACE_EVENT_BEGIN("...", "...", 223 // perfetto::NamedTrack::ThreadScoped("rendering")) 224 template <class TrackEventName> 225 static NamedTrack ThreadScoped(TrackEventName name, 226 uint64_t id = 0, 227 Track parent = Track()) { 228 if (parent.uuid == 0) 229 return NamedTrack(std::forward<TrackEventName>(name), id, 230 ThreadTrack::Current()); 231 return NamedTrack(std::forward<TrackEventName>(name), id, parent); 232 } 233 234 void Serialize(protos::pbzero::TrackDescriptor*) const; 235 protos::gen::TrackDescriptor Serialize() const; 236 237 private: 238 StaticString static_name_; 239 DynamicString dynamic_name_; 240 }; 241 242 // A track for recording counter values with the TRACE_COUNTER macro. Counter 243 // tracks can optionally be given units and other metadata. See 244 // /protos/perfetto/trace/track_event/counter_descriptor.proto for details. 245 class PERFETTO_EXPORT_COMPONENT CounterTrack : public Track { 246 // A random value mixed into counter track uuids to avoid collisions with 247 // other types of tracks. 248 static constexpr uint64_t kCounterMagic = 0xb1a4a67d7970839eul; 249 250 public: 251 using Unit = perfetto::protos::pbzero::CounterDescriptor::Unit; 252 using CounterType = 253 perfetto::protos::gen::CounterDescriptor::BuiltinCounterType; 254 255 // |name| must outlive this object. 256 constexpr explicit CounterTrack(StaticString name, 257 Track parent = MakeProcessTrack()) 258 : CounterTrack( 259 name, 260 0u, 261 perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED, 262 nullptr, 263 parent) {} 264 265 constexpr explicit CounterTrack(StaticString name, 266 uint64_t id, 267 Track parent = MakeProcessTrack()) CounterTrack(name,id,perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,nullptr,parent)268 : CounterTrack( 269 name, 270 id, 271 perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED, 272 nullptr, 273 parent) {} 274 275 explicit CounterTrack(DynamicString name, Track parent = MakeProcessTrack()) 276 : CounterTrack( 277 name, 278 0u, 279 perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED, 280 nullptr, 281 parent) {} 282 283 explicit CounterTrack(DynamicString name, 284 uint64_t id, 285 Track parent = MakeProcessTrack()) CounterTrack(name,id,perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,nullptr,parent)286 : CounterTrack( 287 name, 288 id, 289 perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED, 290 nullptr, 291 parent) {} 292 293 // |unit_name| is a free-form description of the unit used by this counter. It 294 // must outlive this object. 295 template <class TrackEventName> 296 constexpr CounterTrack(TrackEventName&& name, 297 const char* unit_name, 298 Track parent = MakeProcessTrack()) 299 : CounterTrack( 300 std::forward<TrackEventName>(name), 301 0u, 302 perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED, 303 unit_name, 304 parent) {} 305 306 template <class TrackEventName> 307 constexpr CounterTrack(TrackEventName&& name, 308 Unit unit, 309 Track parent = MakeProcessTrack()) 310 : CounterTrack(std::forward<TrackEventName>(name), 311 0u, 312 unit, 313 nullptr, 314 parent) {} 315 316 template <class TrackEventName> Global(TrackEventName && name,const char * unit_name)317 static constexpr CounterTrack Global(TrackEventName&& name, 318 const char* unit_name) { 319 return CounterTrack(std::forward<TrackEventName>(name), unit_name, Track()); 320 } 321 322 template <class TrackEventName> Global(TrackEventName && name,Unit unit)323 static constexpr CounterTrack Global(TrackEventName&& name, Unit unit) { 324 return CounterTrack(std::forward<TrackEventName>(name), unit, Track()); 325 } 326 327 template <class TrackEventName> Global(TrackEventName && name)328 static constexpr CounterTrack Global(TrackEventName&& name) { 329 return Global(std::forward<TrackEventName>(name), nullptr); 330 } 331 set_unit(Unit unit)332 constexpr CounterTrack set_unit(Unit unit) const { 333 return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_, 334 category_, unit, unit_name_, unit_multiplier_, 335 is_incremental_, type_); 336 } 337 set_type(CounterType type)338 constexpr CounterTrack set_type(CounterType type) const { 339 return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_, 340 category_, unit_, unit_name_, unit_multiplier_, 341 is_incremental_, type); 342 } 343 set_unit_name(const char * unit_name)344 constexpr CounterTrack set_unit_name(const char* unit_name) const { 345 return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_, 346 category_, unit_, unit_name, unit_multiplier_, 347 is_incremental_, type_); 348 } 349 set_unit_multiplier(int64_t unit_multiplier)350 constexpr CounterTrack set_unit_multiplier(int64_t unit_multiplier) const { 351 return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_, 352 category_, unit_, unit_name_, unit_multiplier, 353 is_incremental_, type_); 354 } 355 set_category(const char * category)356 constexpr CounterTrack set_category(const char* category) const { 357 return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_, 358 category, unit_, unit_name_, unit_multiplier_, 359 is_incremental_, type_); 360 } 361 362 constexpr CounterTrack set_is_incremental(bool is_incremental = true) const { 363 return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_, 364 category_, unit_, unit_name_, unit_multiplier_, 365 is_incremental, type_); 366 } 367 is_incremental()368 constexpr bool is_incremental() const { return is_incremental_; } 369 370 void Serialize(protos::pbzero::TrackDescriptor*) const; 371 protos::gen::TrackDescriptor Serialize() const; 372 373 private: CounterTrack(StaticString name,uint64_t id,Unit unit,const char * unit_name,Track parent)374 constexpr CounterTrack(StaticString name, 375 uint64_t id, 376 Unit unit, 377 const char* unit_name, 378 Track parent) 379 : Track(id ^ internal::Fnv1a(name.value) ^ kCounterMagic, parent), 380 static_name_(name), 381 category_(nullptr), 382 unit_(unit), 383 unit_name_(unit_name) {} CounterTrack(DynamicString name,uint64_t id,Unit unit,const char * unit_name,Track parent)384 CounterTrack(DynamicString name, 385 uint64_t id, 386 Unit unit, 387 const char* unit_name, 388 Track parent) 389 : Track(id ^ internal::Fnv1a(name.value, name.length) ^ kCounterMagic, 390 parent), 391 static_name_(nullptr), 392 dynamic_name_(name), 393 category_(nullptr), 394 unit_(unit), 395 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)396 constexpr CounterTrack(uint64_t uuid_, 397 uint64_t parent_uuid_, 398 StaticString static_name, 399 DynamicString dynamic_name, 400 const char* category, 401 Unit unit, 402 const char* unit_name, 403 int64_t unit_multiplier, 404 bool is_incremental, 405 CounterType type) 406 : Track(uuid_, parent_uuid_), 407 static_name_(static_name), 408 dynamic_name_(dynamic_name), 409 category_(category), 410 unit_(unit), 411 unit_name_(unit_name), 412 unit_multiplier_(unit_multiplier), 413 is_incremental_(is_incremental), 414 type_(type) {} 415 416 StaticString static_name_; 417 DynamicString dynamic_name_; 418 const char* const category_; 419 Unit unit_ = perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED; 420 const char* const unit_name_ = nullptr; 421 int64_t unit_multiplier_ = 1; 422 const bool is_incremental_ = false; 423 CounterType type_ = 424 perfetto::protos::gen::CounterDescriptor::COUNTER_UNSPECIFIED; 425 }; 426 427 namespace internal { 428 429 // Keeps a map of uuids to serialized track descriptors and provides a 430 // thread-safe way to read and write them. Each trace writer keeps a TLS set of 431 // the tracks it has seen (see TrackEventIncrementalState). In the common case, 432 // this registry is not consulted (and no locks are taken). However when a new 433 // track is seen, this registry is used to write either 1) the default 434 // descriptor for that track (see *Track::Serialize) or 2) a serialized 435 // descriptor stored in the registry which may have additional metadata (e.g., 436 // track name). 437 // TODO(eseckler): Remove PERFETTO_EXPORT_COMPONENT once Chromium no longer 438 // calls TrackRegistry::InitializeInstance() directly. 439 class PERFETTO_EXPORT_COMPONENT TrackRegistry { 440 public: 441 using SerializedTrackDescriptor = std::string; 442 struct TrackInfo { 443 SerializedTrackDescriptor desc; 444 uint64_t parent_uuid = 0; 445 }; 446 447 TrackRegistry(); 448 ~TrackRegistry(); 449 450 static void InitializeInstance(); 451 static void ResetForTesting(); 452 static uint64_t ComputeProcessUuid(); Get()453 static TrackRegistry* Get() { return instance_; } 454 455 void EraseTrack(Track); 456 457 // This variant lets the user supply a serialized track descriptor directly. 458 void UpdateTrack(Track, const std::string& serialized_desc); 459 460 // If |track| exists in the registry, write out the serialized track 461 // descriptor for it into |packet|. Otherwise just the ephemeral track object 462 // is serialized without any additional metadata. 463 // 464 // Returns the parent track uuid. 465 template <typename TrackType> SerializeTrack(const TrackType & track,protozero::MessageHandle<protos::pbzero::TracePacket> packet)466 uint64_t SerializeTrack( 467 const TrackType& track, 468 protozero::MessageHandle<protos::pbzero::TracePacket> packet) { 469 // If the track has extra metadata (recorded with UpdateTrack), it will be 470 // found in the registry. To minimize the time the lock is held, make a copy 471 // of the data held in the registry and write it outside the lock. 472 auto track_info = FindTrackInfo(track.uuid); 473 if (track_info) { 474 WriteTrackDescriptor(std::move(track_info->desc), std::move(packet)); 475 return track_info->parent_uuid; 476 } else { 477 // Otherwise we just write the basic descriptor for this type of track 478 // (e.g., just uuid, no name). 479 track.Serialize(packet->set_track_descriptor()); 480 return track.parent_uuid; 481 } 482 } 483 484 // If saved in the registry, returns the serialize track descriptor and parent 485 // uuid for `uuid`. FindTrackInfo(uint64_t uuid)486 std::optional<TrackInfo> FindTrackInfo(uint64_t uuid) { 487 std::optional<TrackInfo> track_info; 488 { 489 std::lock_guard<std::mutex> lock(mutex_); 490 const auto it = tracks_.find(uuid); 491 if (it != tracks_.end()) { 492 track_info = it->second; 493 PERFETTO_DCHECK(!track_info->desc.empty()); 494 } 495 } 496 return track_info; 497 } 498 499 static void WriteTrackDescriptor( 500 const SerializedTrackDescriptor& desc, 501 protozero::MessageHandle<protos::pbzero::TracePacket> packet); 502 503 private: 504 std::mutex mutex_; 505 std::map<uint64_t /* uuid */, TrackInfo> tracks_; 506 507 static TrackRegistry* instance_; 508 }; 509 510 } // namespace internal 511 } // namespace perfetto 512 513 #endif // INCLUDE_PERFETTO_TRACING_TRACK_H_ 514