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