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