| // 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. |
| |
| #include "base/trace_event/trace_logging_minimal_win.h" |
| |
| #include <evntrace.h> |
| |
| #include "base/check_op.h" |
| #include "base/logging.h" |
| #include "base/numerics/checked_math.h" |
| |
| TlmProvider::~TlmProvider() { |
| Unregister(); |
| } |
| |
| TlmProvider::TlmProvider(const char* provider_name, |
| const GUID& provider_guid, |
| PENABLECALLBACK enable_callback, |
| void* enable_callback_context) noexcept { |
| ULONG status = Register(provider_name, provider_guid, enable_callback, |
| enable_callback_context); |
| 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, |
| const char* name) const noexcept { |
| uint16_t index = metadata_index; |
| DCHECK_LE(index, metadata_size); |
| |
| const size_t cch = strlen(name) + 1; |
| if (cch > static_cast<unsigned>(metadata_size - index)) |
| return static_cast<uint16_t>(-1); |
| |
| memcpy(metadata + index, name, cch); |
| index += static_cast<uint16_t>(cch); |
| 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, |
| PENABLECALLBACK enable_callback, |
| void* enable_callback_context) 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<uint16_t*>(provider_metadata_) = provider_metadata_size_; |
| |
| enable_callback_ = enable_callback; |
| enable_callback_context_ = enable_callback_context; |
| 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* pProvider = static_cast<TlmProvider*>(callback_context); |
| switch (is_enabled) { |
| case EVENT_CONTROL_CODE_DISABLE_PROVIDER: |
| pProvider->level_plus1_ = 0; |
| break; |
| case EVENT_CONTROL_CODE_ENABLE_PROVIDER: |
| pProvider->level_plus1_ = |
| level != 0 ? static_cast<unsigned>(level) + 1u : 256u; |
| pProvider->keyword_any_ = match_any_keyword; |
| pProvider->keyword_all_ = match_all_keyword; |
| break; |
| } |
| |
| if (pProvider->enable_callback_) { |
| pProvider->enable_callback_(source_id, is_enabled, level, match_any_keyword, |
| match_all_keyword, filter_data, |
| pProvider->enable_callback_context_); |
| } |
| } |
| |
| uint16_t TlmProvider::EventBegin(char* metadata, |
| const char* 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, |
| const char* 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<uint16_t>(-1); |
| return 0; |
| } |
| |
| metadata[*metadata_index] = static_cast<char>(in_type); |
| *metadata_index += 1; |
| return 0; |
| } |
| // 2-byte encoding: in_type + out_type. |
| if (kMaxEventMetadataSize - *metadata_index < 2) { |
| *metadata_index = static_cast<uint16_t>(-1); |
| return 0; |
| } |
| |
| // Set high bit to indicate presence of OutType. |
| metadata[*metadata_index] = static_cast<char>(in_type | 0x80); |
| *metadata_index += 1; |
| metadata[*metadata_index] = static_cast<char>(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<uint16_t*>(metadata) = meta_data_index; |
| |
| descriptors[0].Ptr = reinterpret_cast<ULONG_PTR>(provider_metadata_); |
| descriptors[0].Size = provider_metadata_size_; |
| descriptors[0].Reserved = EVENT_DATA_DESCRIPTOR_TYPE_PROVIDER_METADATA; |
| |
| descriptors[1].Ptr = reinterpret_cast<ULONG_PTR>(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_); |
| } |
| |
| TlmMbcsStringField::TlmMbcsStringField(const char* name, |
| const char* value) noexcept |
| : TlmFieldBase(name), value_(value) { |
| DCHECK_NE(Name(), 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<ULONG>(strlen(value_) + 1)); |
| } |
| |
| TlmUtf8StringField::TlmUtf8StringField(const char* name, |
| const char* value) noexcept |
| : TlmFieldBase(name), value_(value) { |
| DCHECK_NE(Name(), 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<ULONG>(strlen(value_) + 1)); |
| } |