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