// Copyright 2020 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifdef UNSAFE_BUFFERS_BUILD // TODO(crbug.com/40284755): Remove this and spanify to fix the errors. #pragma allow_unsafe_buffers #endif #include "base/trace_event/trace_logging_minimal_win.h" #include #include "base/check_op.h" #include "base/logging.h" #include "base/numerics/checked_math.h" TlmProvider::TlmProvider() noexcept = default; TlmProvider::~TlmProvider() { Unregister(); } TlmProvider::TlmProvider(const char* provider_name, const GUID& provider_guid, base::RepeatingCallback on_updated_callback) noexcept { ULONG status = Register(provider_name, provider_guid, std::move(on_updated_callback)); LOG_IF(ERROR, status != ERROR_SUCCESS) << "Provider resistration failure"; } // Appends a nul-terminated string to a metadata block. // Returns new meta_data_index value, or -1 for overflow. uint16_t TlmProvider::AppendNameToMetadata( char* metadata, uint16_t metadata_size, uint16_t metadata_index, std::string_view name) const noexcept { uint16_t index = metadata_index; DCHECK_LE(index, metadata_size); const size_t cch = name.size(); if (cch + 1 > static_cast(metadata_size - index)) { return static_cast(-1); } memcpy(metadata + index, name.data(), cch); metadata[index + cch] = 0; index += static_cast(cch) + 1; return index; } void TlmProvider::Unregister() noexcept { if (reg_handle_ == 0) return; ULONG status = EventUnregister(reg_handle_); LOG_IF(ERROR, status != ERROR_SUCCESS) << "Provider unregistration failure"; reg_handle_ = 0; level_plus1_ = 0; } ULONG TlmProvider::Register(const char* provider_name, const GUID& provider_guid, base::RepeatingCallback on_updated_callback) noexcept { // Calling Register when already registered is a fatal error. CHECK_EQ(reg_handle_, 0ULL); // provider_metadata_ for tracelogging has the following format: // UINT16 metadata_size; // char NullTerminatedUtf8ProviderName[]; // ( + optional extension data, not used here) // Append the provider name starting at offset 2 (skip MetadataSize). provider_metadata_size_ = AppendNameToMetadata( provider_metadata_, kMaxProviderMetadataSize, 2, provider_name); if (provider_metadata_size_ > kMaxProviderMetadataSize) return ERROR_BUFFER_OVERFLOW; // Fill in MetadataSize field at offset 0. *reinterpret_cast(provider_metadata_) = provider_metadata_size_; on_updated_callback_ = std::move(on_updated_callback); ULONG status = EventRegister(&provider_guid, StaticEnableCallback, this, ®_handle_); if (status != ERROR_SUCCESS) return status; // Best-effort, ignore failure. return ::EventSetInformation(reg_handle_, EventProviderSetTraits, provider_metadata_, provider_metadata_size_); } bool TlmProvider::IsEnabled() const noexcept { return 0 < level_plus1_; } bool TlmProvider::IsEnabled(uint8_t level) const noexcept { return level < level_plus1_; } bool TlmProvider::IsEnabled(uint8_t level, uint64_t keyword) const noexcept { return level < level_plus1_ && KeywordEnabled(keyword); } bool TlmProvider::IsEnabled( const EVENT_DESCRIPTOR& event_descriptor) const noexcept { return event_descriptor.Level < level_plus1_ && KeywordEnabled(event_descriptor.Keyword); } void TlmProvider::StaticEnableCallback(const GUID* source_id, ULONG is_enabled, UCHAR level, ULONGLONG match_any_keyword, ULONGLONG match_all_keyword, PEVENT_FILTER_DESCRIPTOR filter_data, PVOID callback_context) { if (!callback_context) return; TlmProvider* provider = static_cast(callback_context); switch (is_enabled) { case EVENT_CONTROL_CODE_DISABLE_PROVIDER: provider->level_plus1_ = 0; break; case EVENT_CONTROL_CODE_ENABLE_PROVIDER: provider->level_plus1_ = level != 0 ? static_cast(level) + 1u : 256u; break; } provider->keyword_any_ = match_any_keyword; provider->keyword_all_ = match_all_keyword; if (provider->on_updated_callback_ && is_enabled <= static_cast(EventControlCode::kHighest)) { provider->on_updated_callback_.Run( static_cast(is_enabled)); } } uint16_t TlmProvider::EventBegin(char* metadata, std::string_view event_name) const noexcept { // EventMetadata for tracelogging has the following format // UINT16 MetadataSize; // BYTE SpecialFlags[]; // Not used, so always size 1. // char NullTerminatedUtf8EventName[]; // ( + field definitions) uint16_t index = 2; // Skip MetadataSize field. metadata[index] = 0; // Set SpecialFlags[0] = 0. index++; // sizeof(SpecialFlags) == 1. index = AppendNameToMetadata(metadata, kMaxEventMetadataSize, index, event_name); return index; } char TlmProvider::EventAddField(char* metadata, uint16_t* metadata_index, uint8_t in_type, uint8_t out_type, std::string_view field_name) const noexcept { DCHECK_LT(in_type, 0x80); DCHECK_LT(out_type, 0x80); // FieldDefinition = // char NullTerminatedUtf8FieldName[]; // BYTE InType; // BYTE OutType; // Only present if high bit set in InType. // ( + optional extension data not used here) if (*metadata_index >= kMaxEventMetadataSize) return 0; *metadata_index = AppendNameToMetadata(metadata, kMaxEventMetadataSize, *metadata_index, field_name); if (*metadata_index >= kMaxEventMetadataSize) return 0; if (out_type == 0) { // 1-byte encoding: inType + TlgOutNULL. if (1 > kMaxEventMetadataSize - *metadata_index) { *metadata_index = static_cast(-1); return 0; } metadata[*metadata_index] = static_cast(in_type); *metadata_index += 1; return 0; } // 2-byte encoding: in_type + out_type. if (kMaxEventMetadataSize - *metadata_index < 2) { *metadata_index = static_cast(-1); return 0; } // Set high bit to indicate presence of OutType. metadata[*metadata_index] = static_cast(in_type | 0x80); *metadata_index += 1; metadata[*metadata_index] = static_cast(out_type); *metadata_index += 1; return 0; } ULONG TlmProvider::EventEnd( char* metadata, uint16_t meta_data_index, EVENT_DATA_DESCRIPTOR* descriptors, uint32_t descriptors_index, const EVENT_DESCRIPTOR& event_descriptor) const noexcept { if (meta_data_index > kMaxEventMetadataSize) { return ERROR_BUFFER_OVERFLOW; } // Fill in EventMetadata's MetadataSize field. *reinterpret_cast(metadata) = meta_data_index; descriptors[0].Ptr = reinterpret_cast(provider_metadata_); descriptors[0].Size = provider_metadata_size_; descriptors[0].Reserved = EVENT_DATA_DESCRIPTOR_TYPE_PROVIDER_METADATA; descriptors[1].Ptr = reinterpret_cast(metadata); descriptors[1].Size = meta_data_index; descriptors[1].Reserved = EVENT_DATA_DESCRIPTOR_TYPE_EVENT_METADATA; return EventWrite(reg_handle_, &event_descriptor, descriptors_index, descriptors); } bool TlmProvider::KeywordEnabled(uint64_t keyword) const noexcept { return keyword == 0 || ((keyword & keyword_any_) && (keyword & keyword_all_) == keyword_all_); } TlmInt64Field::TlmInt64Field(const char* name, const int64_t value) noexcept : TlmFieldWithConstants(name), value_(value) { DCHECK_NE(Name().data(), nullptr); } int64_t TlmInt64Field::Value() const noexcept { return value_; } void TlmInt64Field::FillEventDescriptor( EVENT_DATA_DESCRIPTOR* descriptors) const noexcept { EventDataDescCreate(&descriptors[0], (void*)&value_, sizeof(value_)); } TlmUInt64Field::TlmUInt64Field(const char* name, const uint64_t value) noexcept : TlmFieldWithConstants(name), value_(value) { DCHECK_NE(Name().data(), nullptr); } uint64_t TlmUInt64Field::Value() const noexcept { return value_; } void TlmUInt64Field::FillEventDescriptor( EVENT_DATA_DESCRIPTOR* descriptors) const noexcept { EventDataDescCreate(&descriptors[0], (void*)&value_, sizeof(value_)); } TlmMbcsStringField::TlmMbcsStringField(const char* name, const char* value) noexcept : TlmFieldWithConstants(name), value_(value) { DCHECK_NE(Name().data(), nullptr); DCHECK_NE(value_, nullptr); } const char* TlmMbcsStringField::Value() const noexcept { return value_; } void TlmMbcsStringField::FillEventDescriptor( EVENT_DATA_DESCRIPTOR* descriptors) const noexcept { EventDataDescCreate(&descriptors[0], value_, base::checked_cast(strlen(value_) + 1)); } TlmUtf8StringField::TlmUtf8StringField(const char* name, const char* value) noexcept : TlmFieldWithConstants(name), value_(value) { DCHECK_NE(Name().data(), nullptr); DCHECK_NE(value_, nullptr); } const char* TlmUtf8StringField::Value() const noexcept { return value_; } void TlmUtf8StringField::FillEventDescriptor( EVENT_DATA_DESCRIPTOR* descriptors) const noexcept { EventDataDescCreate(&descriptors[0], value_, base::checked_cast(strlen(value_) + 1)); } TlmFieldBase::TlmFieldBase(const char* name) noexcept : name_(name) {} TlmFieldBase::TlmFieldBase(std::string_view name) noexcept : name_(name) {} TlmFieldBase::~TlmFieldBase() = default; TlmFieldBase::TlmFieldBase(TlmFieldBase&&) noexcept = default; TlmFieldBase& TlmFieldBase::operator=(TlmFieldBase&&) noexcept = default;