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