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_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_ 18 #define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_ 19 20 #include "perfetto/base/compiler.h" 21 #include "perfetto/base/template_util.h" 22 #include "perfetto/protozero/message_handle.h" 23 #include "perfetto/tracing/core/data_source_config.h" 24 #include "perfetto/tracing/data_source.h" 25 #include "perfetto/tracing/event_context.h" 26 #include "perfetto/tracing/internal/track_event_internal.h" 27 #include "perfetto/tracing/internal/write_track_event_args.h" 28 #include "perfetto/tracing/track.h" 29 #include "perfetto/tracing/track_event_category_registry.h" 30 #include "protos/perfetto/common/builtin_clock.pbzero.h" 31 #include "protos/perfetto/config/track_event/track_event_config.gen.h" 32 #include "protos/perfetto/trace/track_event/track_event.pbzero.h" 33 34 #include <type_traits> 35 36 namespace perfetto { 37 38 // A function for converting an abstract timestamp into a 39 // perfetto::TraceTimestamp struct. By specialising this template and defining 40 // static ConvertTimestampToTraceTimeNs function in it the user can register 41 // additional timestamp types. The return value should specify the 42 // clock domain used by the timestamp as well as its value. 43 // 44 // The supported clock domains are the ones described in 45 // perfetto.protos.ClockSnapshot. However, custom clock IDs (>=64) are 46 // reserved for internal use by the SDK for the time being. 47 // The timestamp value should be in nanoseconds regardless of the clock domain. 48 template <typename T> 49 struct TraceTimestampTraits; 50 51 // A pass-through implementation for raw uint64_t nanosecond timestamps. 52 template <> 53 struct TraceTimestampTraits<uint64_t> { 54 static inline TraceTimestamp ConvertTimestampToTraceTimeNs( 55 const uint64_t& timestamp) { 56 return {static_cast<uint32_t>(internal::TrackEventInternal::GetClockId()), timestamp}; 57 } 58 }; 59 60 // A pass-through implementation for the trace timestamp structure. 61 template <> 62 struct TraceTimestampTraits<TraceTimestamp> { 63 static inline TraceTimestamp ConvertTimestampToTraceTimeNs( 64 const TraceTimestamp& timestamp) { 65 return timestamp; 66 } 67 }; 68 69 namespace internal { 70 namespace { 71 72 // Checks if |T| is a valid track. 73 template <typename T> 74 static constexpr bool IsValidTrack() { 75 return std::is_convertible<T, Track>::value; 76 } 77 78 // Checks if |T| is a valid non-counter track. 79 template <typename T> 80 static constexpr bool IsValidNormalTrack() { 81 return std::is_convertible<T, Track>::value && 82 !std::is_convertible<T, CounterTrack>::value; 83 } 84 85 // Because the user can use arbitrary timestamp types, we can't compare against 86 // any known base type here. Instead, we check that a track or a trace lambda 87 // isn't being interpreted as a timestamp. 88 template <typename T, 89 typename CanBeConvertedToNsCheck = decltype( 90 ::perfetto::TraceTimestampTraits<typename base::remove_cvref_t< 91 T>>::ConvertTimestampToTraceTimeNs(std::declval<T>())), 92 typename NotTrackCheck = 93 typename std::enable_if<!IsValidNormalTrack<T>()>::type, 94 typename NotLambdaCheck = 95 typename std::enable_if<!IsValidTraceLambda<T>()>::type> 96 static constexpr bool IsValidTimestamp() { 97 return true; 98 } 99 100 } // namespace 101 102 // Traits for dynamic categories. 103 template <typename CategoryType> 104 struct CategoryTraits { 105 static constexpr bool kIsDynamic = true; 106 static constexpr const Category* GetStaticCategory( 107 const TrackEventCategoryRegistry*, 108 const CategoryType&) { 109 return nullptr; 110 } 111 static size_t GetStaticIndex(const CategoryType&) { 112 PERFETTO_DCHECK(false); // Not reached. 113 return TrackEventCategoryRegistry::kDynamicCategoryIndex; 114 } 115 static DynamicCategory GetDynamicCategory(const CategoryType& category) { 116 return DynamicCategory{category}; 117 } 118 }; 119 120 // Traits for static categories. 121 template <> 122 struct CategoryTraits<size_t> { 123 static constexpr bool kIsDynamic = false; 124 static const Category* GetStaticCategory( 125 const TrackEventCategoryRegistry* registry, 126 size_t category_index) { 127 return registry->GetCategory(category_index); 128 } 129 static constexpr size_t GetStaticIndex(size_t category_index) { 130 return category_index; 131 } 132 static DynamicCategory GetDynamicCategory(size_t) { 133 PERFETTO_DCHECK(false); // Not reached. 134 return DynamicCategory(); 135 } 136 }; 137 138 struct TrackEventDataSourceTraits : public perfetto::DefaultDataSourceTraits { 139 using IncrementalStateType = TrackEventIncrementalState; 140 using TlsStateType = TrackEventTlsState; 141 142 // Use a one shared TLS slot so that all track event data sources write into 143 // the same sequence and share interning dictionaries. 144 static DataSourceThreadLocalState* GetDataSourceTLS(DataSourceStaticState*, 145 TracingTLS* root_tls) { 146 return &root_tls->track_event_tls; 147 } 148 }; 149 150 // A generic track event data source which is instantiated once per track event 151 // category namespace. 152 template <typename DataSourceType, const TrackEventCategoryRegistry* Registry> 153 class TrackEventDataSource 154 : public DataSource<DataSourceType, TrackEventDataSourceTraits> { 155 using Base = DataSource<DataSourceType, TrackEventDataSourceTraits>; 156 157 public: 158 // Add or remove a session observer for this track event data source. The 159 // observer will be notified about started and stopped tracing sessions. 160 // Returns |true| if the observer was successfully added (i.e., the maximum 161 // number of observers wasn't exceeded). 162 static bool AddSessionObserver(TrackEventSessionObserver* observer) { 163 return TrackEventInternal::AddSessionObserver(observer); 164 } 165 166 static void RemoveSessionObserver(TrackEventSessionObserver* observer) { 167 TrackEventInternal::RemoveSessionObserver(observer); 168 } 169 170 // DataSource implementation. 171 void OnSetup(const DataSourceBase::SetupArgs& args) override { 172 auto config_raw = args.config->track_event_config_raw(); 173 bool ok = config_.ParseFromArray(config_raw.data(), config_raw.size()); 174 PERFETTO_DCHECK(ok); 175 TrackEventInternal::EnableTracing(*Registry, config_, args); 176 } 177 178 void OnStart(const DataSourceBase::StartArgs& args) override { 179 TrackEventInternal::OnStart(args); 180 } 181 182 void OnStop(const DataSourceBase::StopArgs& args) override { 183 TrackEventInternal::DisableTracing(*Registry, args); 184 } 185 186 static void Flush() { 187 Base::template Trace([](typename Base::TraceContext ctx) { ctx.Flush(); }); 188 } 189 190 // Determine if *any* tracing category is enabled. 191 static bool IsEnabled() { 192 bool enabled = false; 193 Base::template CallIfEnabled( 194 [&](uint32_t /*instances*/) { enabled = true; }); 195 return enabled; 196 } 197 198 // Determine if tracing for the given static category is enabled. 199 static bool IsCategoryEnabled(size_t category_index) { 200 return Registry->GetCategoryState(category_index) 201 ->load(std::memory_order_relaxed); 202 } 203 204 // Determine if tracing for the given dynamic category is enabled. 205 static bool IsDynamicCategoryEnabled( 206 const DynamicCategory& dynamic_category) { 207 bool enabled = false; 208 Base::template Trace([&](typename Base::TraceContext ctx) { 209 enabled = IsDynamicCategoryEnabled(&ctx, dynamic_category); 210 }); 211 return enabled; 212 } 213 214 // This is the inlined entrypoint for all track event trace points. It tries 215 // to be as lightweight as possible in terms of instructions and aims to 216 // compile down to an unlikely conditional jump to the actual trace writing 217 // function. 218 template <typename Callback> 219 static void CallIfCategoryEnabled(size_t category_index, 220 Callback callback) PERFETTO_ALWAYS_INLINE { 221 Base::template CallIfEnabled<CategoryTracePointTraits>( 222 [&callback](uint32_t instances) { callback(instances); }, 223 {category_index}); 224 } 225 226 // Once we've determined tracing to be enabled for this category, actually 227 // write a trace event onto this thread's default track. Outlined to avoid 228 // bloating code (mostly stack depth) at the actual trace point. 229 // 230 // The following combination of parameters is supported (in the given order): 231 // - Zero or one track, 232 // - Zero or one custom timestamp, 233 // - Arbitrary number of debug annotations. 234 // - Zero or one lambda. 235 236 // Trace point which does not take a track or timestamp. 237 template <typename CategoryType, typename... Arguments> 238 static void TraceForCategory(uint32_t instances, 239 const CategoryType& category, 240 const char* event_name, 241 perfetto::protos::pbzero::TrackEvent::Type type, 242 Arguments&&... args) PERFETTO_NO_INLINE { 243 TraceForCategoryImpl(instances, category, event_name, type, 244 TrackEventInternal::kDefaultTrack, 245 TrackEventInternal::GetTraceTime(), 246 std::forward<Arguments>(args)...); 247 } 248 249 // Trace point which takes a track, but not timestamp. 250 // NOTE: Here track should be captured using universal reference (TrackType&&) 251 // instead of const TrackType& to ensure that the proper overload is selected 252 // (otherwise the compiler will fail to disambiguate between adding const& and 253 // parsing track as a part of Arguments...). 254 template <typename TrackType, 255 typename CategoryType, 256 typename... Arguments, 257 typename TrackTypeCheck = typename std::enable_if< 258 std::is_convertible<TrackType, Track>::value>::type> 259 static void TraceForCategory(uint32_t instances, 260 const CategoryType& category, 261 const char* event_name, 262 perfetto::protos::pbzero::TrackEvent::Type type, 263 TrackType&& track, 264 Arguments&&... args) PERFETTO_NO_INLINE { 265 TraceForCategoryImpl( 266 instances, category, event_name, type, std::forward<TrackType>(track), 267 TrackEventInternal::GetTraceTime(), std::forward<Arguments>(args)...); 268 } 269 270 // Trace point which takes a timestamp, but not track. 271 template <typename CategoryType, 272 typename TimestampType = uint64_t, 273 typename... Arguments, 274 typename TimestampTypeCheck = typename std::enable_if< 275 IsValidTimestamp<TimestampType>()>::type> 276 static void TraceForCategory(uint32_t instances, 277 const CategoryType& category, 278 const char* event_name, 279 perfetto::protos::pbzero::TrackEvent::Type type, 280 TimestampType&& timestamp, 281 Arguments&&... args) PERFETTO_NO_INLINE { 282 TraceForCategoryImpl(instances, category, event_name, type, 283 TrackEventInternal::kDefaultTrack, 284 std::forward<TimestampType>(timestamp), 285 std::forward<Arguments>(args)...); 286 } 287 288 // Trace point which takes a timestamp and a track. 289 template <typename TrackType, 290 typename CategoryType, 291 typename TimestampType = uint64_t, 292 typename... Arguments, 293 typename TrackTypeCheck = typename std::enable_if< 294 std::is_convertible<TrackType, Track>::value>::type, 295 typename TimestampTypeCheck = typename std::enable_if< 296 IsValidTimestamp<TimestampType>()>::type> 297 static void TraceForCategory(uint32_t instances, 298 const CategoryType& category, 299 const char* event_name, 300 perfetto::protos::pbzero::TrackEvent::Type type, 301 TrackType&& track, 302 TimestampType&& timestamp, 303 Arguments&&... args) PERFETTO_NO_INLINE { 304 TraceForCategoryImpl(instances, category, event_name, type, 305 std::forward<TrackType>(track), 306 std::forward<TimestampType>(timestamp), 307 std::forward<Arguments>(args)...); 308 } 309 310 // Trace point with with a counter sample. 311 template <typename CategoryType, typename ValueType> 312 static void TraceForCategory(uint32_t instances, 313 const CategoryType& category, 314 const char*, 315 perfetto::protos::pbzero::TrackEvent::Type type, 316 CounterTrack track, 317 ValueType value) PERFETTO_ALWAYS_INLINE { 318 PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER); 319 TraceForCategory(instances, category, /*name=*/nullptr, type, track, 320 TrackEventInternal::GetTraceTime(), value); 321 } 322 323 // Trace point with with a timestamp and a counter sample. 324 template <typename CategoryType, 325 typename TimestampType = uint64_t, 326 typename TimestampTypeCheck = typename std::enable_if< 327 IsValidTimestamp<TimestampType>()>::type, 328 typename ValueType> 329 static void TraceForCategory(uint32_t instances, 330 const CategoryType& category, 331 const char*, 332 perfetto::protos::pbzero::TrackEvent::Type type, 333 CounterTrack track, 334 TimestampType timestamp, 335 ValueType value) PERFETTO_ALWAYS_INLINE { 336 PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER); 337 TraceForCategoryImpl( 338 instances, category, /*name=*/nullptr, type, track, timestamp, 339 [&](EventContext event_ctx) { 340 if (std::is_integral<ValueType>::value) { 341 int64_t value_int64 = static_cast<int64_t>(value); 342 if (track.is_incremental()) { 343 TrackEventIncrementalState* incr_state = 344 event_ctx.GetIncrementalState(); 345 PERFETTO_DCHECK(incr_state != nullptr); 346 auto prv_value = 347 incr_state->last_counter_value_per_track[track.uuid]; 348 event_ctx.event()->set_counter_value(value_int64 - prv_value); 349 prv_value = value_int64; 350 incr_state->last_counter_value_per_track[track.uuid] = prv_value; 351 } else { 352 event_ctx.event()->set_counter_value(value_int64); 353 } 354 } else { 355 event_ctx.event()->set_double_counter_value( 356 static_cast<double>(value)); 357 } 358 }); 359 } 360 361 // Initialize the track event library. Should be called before tracing is 362 // enabled. 363 static bool Register() { 364 // Registration is performed out-of-line so users don't need to depend on 365 // DataSourceDescriptor C++ bindings. 366 return TrackEventInternal::Initialize( 367 *Registry, 368 [](const DataSourceDescriptor& dsd) { return Base::Register(dsd); }); 369 } 370 371 // Record metadata about different types of timeline tracks. See Track. 372 static void SetTrackDescriptor(const Track& track, 373 const protos::gen::TrackDescriptor& desc) { 374 PERFETTO_DCHECK(track.uuid == desc.uuid()); 375 TrackRegistry::Get()->UpdateTrack(track, desc.SerializeAsString()); 376 Base::template Trace([&](typename Base::TraceContext ctx) { 377 TrackEventInternal::WriteTrackDescriptor( 378 track, ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(), 379 *ctx.GetCustomTlsState(), TrackEventInternal::GetTraceTime()); 380 }); 381 } 382 383 // DEPRECATED. Only kept for backwards compatibility. 384 static void SetTrackDescriptor( 385 const Track& track, 386 std::function<void(protos::pbzero::TrackDescriptor*)> callback) { 387 SetTrackDescriptorImpl(track, std::move(callback)); 388 } 389 390 // DEPRECATED. Only kept for backwards compatibility. 391 static void SetProcessDescriptor( 392 std::function<void(protos::pbzero::TrackDescriptor*)> callback, 393 const ProcessTrack& track = ProcessTrack::Current()) { 394 SetTrackDescriptorImpl(std::move(track), std::move(callback)); 395 } 396 397 // DEPRECATED. Only kept for backwards compatibility. 398 static void SetThreadDescriptor( 399 std::function<void(protos::pbzero::TrackDescriptor*)> callback, 400 const ThreadTrack& track = ThreadTrack::Current()) { 401 SetTrackDescriptorImpl(std::move(track), std::move(callback)); 402 } 403 404 static void EraseTrackDescriptor(const Track& track) { 405 TrackRegistry::Get()->EraseTrack(track); 406 } 407 408 // Returns the current trace timestamp in nanoseconds. Note the returned 409 // timebase may vary depending on the platform, but will always match the 410 // timestamps recorded by track events (see GetTraceClockId). 411 static uint64_t GetTraceTimeNs() { return TrackEventInternal::GetTimeNs(); } 412 413 // Returns the type of clock used by GetTraceTimeNs(). 414 static constexpr protos::pbzero::BuiltinClock GetTraceClockId() { 415 return TrackEventInternal::GetClockId(); 416 } 417 418 const protos::gen::TrackEventConfig& GetConfig() const { return config_; } 419 420 private: 421 // Each category has its own enabled/disabled state, stored in the category 422 // registry. 423 struct CategoryTracePointTraits { 424 // Each trace point with a static category has an associated category index. 425 struct TracePointData { 426 size_t category_index; 427 }; 428 // Called to get the enabled state bitmap of a given category. 429 // |data| is the trace point data structure given to 430 // DataSource::TraceWithInstances. 431 static constexpr std::atomic<uint8_t>* GetActiveInstances( 432 TracePointData data) { 433 return Registry->GetCategoryState(data.category_index); 434 } 435 }; 436 437 template <typename CategoryType, 438 typename TrackType = Track, 439 typename TimestampType = uint64_t, 440 typename TimestampTypeCheck = typename std::enable_if< 441 IsValidTimestamp<TimestampType>()>::type, 442 typename TrackTypeCheck = 443 typename std::enable_if<IsValidTrack<TrackType>()>::type, 444 typename... Arguments> 445 static void TraceForCategoryImpl( 446 uint32_t instances, 447 const CategoryType& category, 448 const char* event_name, 449 perfetto::protos::pbzero::TrackEvent::Type type, 450 const TrackType& track, 451 const TimestampType& timestamp, 452 Arguments&&... args) PERFETTO_ALWAYS_INLINE { 453 using CatTraits = CategoryTraits<CategoryType>; 454 const Category* static_category = 455 CatTraits::GetStaticCategory(Registry, category); 456 TraceWithInstances( 457 instances, category, [&](typename Base::TraceContext ctx) { 458 // If this category is dynamic, first check whether it's enabled. 459 if (CatTraits::kIsDynamic && 460 !IsDynamicCategoryEnabled( 461 &ctx, CatTraits::GetDynamicCategory(category))) { 462 return; 463 } 464 465 const TrackEventTlsState& tls_state = *ctx.GetCustomTlsState(); 466 TraceTimestamp trace_timestamp = ::perfetto::TraceTimestampTraits< 467 TimestampType>::ConvertTimestampToTraceTimeNs(timestamp); 468 469 TraceWriterBase* trace_writer = ctx.tls_inst_->trace_writer.get(); 470 // Make sure incremental state is valid. 471 TrackEventIncrementalState* incr_state = ctx.GetIncrementalState(); 472 TrackEventInternal::ResetIncrementalStateIfRequired( 473 trace_writer, incr_state, tls_state, trace_timestamp); 474 475 // Write the track descriptor before any event on the track. 476 if (track) { 477 TrackEventInternal::WriteTrackDescriptorIfNeeded( 478 track, trace_writer, incr_state, tls_state, trace_timestamp); 479 } 480 481 // Write the event itself. 482 { 483 auto event_ctx = TrackEventInternal::WriteEvent( 484 trace_writer, incr_state, tls_state, static_category, 485 event_name, type, trace_timestamp); 486 // Write dynamic categories (except for events that don't require 487 // categories). For counter events, the counter name (and optional 488 // category) is stored as part of the track descriptor instead being 489 // recorded with individual events. 490 if (CatTraits::kIsDynamic && 491 type != protos::pbzero::TrackEvent::TYPE_SLICE_END && 492 type != protos::pbzero::TrackEvent::TYPE_COUNTER) { 493 DynamicCategory dynamic_category = 494 CatTraits::GetDynamicCategory(category); 495 Category cat = Category::FromDynamicCategory(dynamic_category); 496 cat.ForEachGroupMember( 497 [&](const char* member_name, size_t name_size) { 498 event_ctx.event()->add_categories(member_name, name_size); 499 return true; 500 }); 501 } 502 if (&track != &TrackEventInternal::kDefaultTrack) 503 event_ctx.event()->set_track_uuid(track.uuid); 504 WriteTrackEventArgs(std::move(event_ctx), 505 std::forward<Arguments>(args)...); 506 } // event_ctx 507 }); 508 } 509 510 template <typename CategoryType, typename Lambda> 511 static void TraceWithInstances(uint32_t instances, 512 const CategoryType& category, 513 Lambda lambda) PERFETTO_ALWAYS_INLINE { 514 using CatTraits = CategoryTraits<CategoryType>; 515 if (CatTraits::kIsDynamic) { 516 Base::template TraceWithInstances(instances, std::move(lambda)); 517 } else { 518 Base::template TraceWithInstances<CategoryTracePointTraits>( 519 instances, std::move(lambda), {CatTraits::GetStaticIndex(category)}); 520 } 521 } 522 523 // Records a track descriptor into the track descriptor registry and, if we 524 // are tracing, also mirrors the descriptor into the trace. 525 template <typename TrackType> 526 static void SetTrackDescriptorImpl( 527 const TrackType& track, 528 std::function<void(protos::pbzero::TrackDescriptor*)> callback) { 529 TrackRegistry::Get()->UpdateTrack(track, std::move(callback)); 530 Base::template Trace([&](typename Base::TraceContext ctx) { 531 TrackEventInternal::WriteTrackDescriptor( 532 track, ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(), 533 *ctx.GetCustomTlsState(), TrackEventInternal::GetTraceTime()); 534 }); 535 } 536 537 // Determines if the given dynamic category is enabled, first by checking the 538 // per-trace writer cache or by falling back to computing it based on the 539 // trace config for the given session. 540 static bool IsDynamicCategoryEnabled( 541 typename Base::TraceContext* ctx, 542 const DynamicCategory& dynamic_category) { 543 auto incr_state = ctx->GetIncrementalState(); 544 auto it = incr_state->dynamic_categories.find(dynamic_category.name); 545 if (it == incr_state->dynamic_categories.end()) { 546 // We haven't seen this category before. Let's figure out if it's enabled. 547 // This requires grabbing a lock to read the session's trace config. 548 auto ds = ctx->GetDataSourceLocked(); 549 Category category{Category::FromDynamicCategory(dynamic_category)}; 550 bool enabled = TrackEventInternal::IsCategoryEnabled( 551 *Registry, ds->config_, category); 552 // TODO(skyostil): Cap the size of |dynamic_categories|. 553 incr_state->dynamic_categories[dynamic_category.name] = enabled; 554 return enabled; 555 } 556 return it->second; 557 } 558 559 // Config for the current tracing session. 560 protos::gen::TrackEventConfig config_; 561 }; 562 563 } // namespace internal 564 } // namespace perfetto 565 566 #endif // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_DATA_SOURCE_H_ 567