// 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 <evntrace.h>
#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<void(EventControlCode)>
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<unsigned>(metadata_size - index)) {
return static_cast<uint16_t>(-1);
}
memcpy(metadata + index, name.data(), cch);
metadata[index + cch] = 0;
index += static_cast<uint16_t>(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<void(EventControlCode)>
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<uint16_t*>(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<TlmProvider*>(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<unsigned>(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<size_t>(EventControlCode::kHighest)) {
provider->on_updated_callback_.Run(
static_cast<EventControlCode>(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<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_);
}
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<ULONG>(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<ULONG>(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;