blob: 84cf184eda3f86e74555810283b758ddae6a7b4f [file] [log] [blame]
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_DIAGNOSTICS_SYSTEM_JIT_METADATA_WIN_H_
#define V8_DIAGNOSTICS_SYSTEM_JIT_METADATA_WIN_H_
#include <Windows.h>
#ifndef VOID
#define VOID void
#endif
#include <TraceLoggingProvider.h>
#include <evntprov.h>
#include <evntrace.h> // defines TRACE_LEVEL_* and EVENT_TRACE_TYPE_*
#include <cstdint>
#include <string>
#include <unordered_set>
#include <utility>
namespace v8 {
namespace internal {
namespace ETWJITInterface {
/*******************************************************************************
Helper templates to create tightly packed metadata of the format expected by the
ETW data structures.
*******************************************************************************/
// All "manifest-free" events should go to channel 11 by default
const uint8_t kManifestFreeChannel = 11;
// Number of metadescriptors. Use this to find out the index of the field
// descriptors in the descriptors_array
const uint8_t kMetaDescriptorsCount = 2;
// Filtering keyword to find JScript stack-walking events
constexpr uint64_t kJScriptRuntimeKeyword = 1;
constexpr uint16_t kSourceLoadEventID = 41;
constexpr uint16_t kMethodLoadEventID = 9;
// Structure to treat a string literal, or char[], as a constexpr byte sequence
template <size_t count>
struct str_bytes {
template <std::size_t... idx>
constexpr str_bytes(char const (&str)[count], std::index_sequence<idx...>)
: bytes{str[idx]...}, size(count) {}
// Concatenate two str_bytes
template <std::size_t count1, std::size_t count2, std::size_t... idx1,
std::size_t... idx2>
constexpr str_bytes(const str_bytes<count1>& s1, std::index_sequence<idx1...>,
const str_bytes<count2>& s2, std::index_sequence<idx2...>)
: bytes{s1.bytes[idx1]..., s2.bytes[idx2]...}, size(count) {}
char bytes[count]; // NOLINT
size_t size;
};
// Specialization for 0 (base case when joining fields)
template <>
struct str_bytes<0> {
constexpr str_bytes() : bytes{}, size(0) {}
char bytes[1]; // MSVC doesn't like an array of 0 bytes
size_t size;
};
// Factory function to simplify creating a str_bytes from a string literal
template <size_t count, typename idx = std::make_index_sequence<count>>
constexpr auto MakeStrBytes(char const (&s)[count]) {
return str_bytes<count>{s, idx{}};
}
// Concatenates two str_bytes into one
template <std::size_t size1, std::size_t size2>
constexpr auto JoinBytes(const str_bytes<size1>& str1,
const str_bytes<size2>& str2) {
auto idx1 = std::make_index_sequence<size1>();
auto idx2 = std::make_index_sequence<size2>();
return str_bytes<size1 + size2>{str1, idx1, str2, idx2};
}
// Creates an str_bytes which is the field name suffixed with the field type
template <size_t count>
constexpr auto Field(char const (&s)[count], uint8_t type) {
auto field_name = MakeStrBytes(s);
const char type_arr[1] = {static_cast<char>(type)};
return JoinBytes(field_name, MakeStrBytes(type_arr));
}
// Creates the ETW event metadata header, which consists of a uint16_t
// representing the total size, and a tag byte (always 0x00 currently).
constexpr auto Header(size_t size) {
size_t total_size = size + 3; // total_size includes the 2 byte size + tag
const char header_bytes[3] = {static_cast<char>(total_size & 0xFF),
static_cast<char>(total_size >> 8 & 0xFF),
'\0'};
return MakeStrBytes(header_bytes);
}
// The JoinFields implementations below are a set of overloads for constructing
// a str_bytes representing the concatenated fields from a parameter pack.
// Empty case needed for events with no fields.
constexpr auto JoinFields() { return str_bytes<0>{}; }
// Only one field, or base case when multiple fields.
template <typename T1>
constexpr auto JoinFields(T1 field) {
return field;
}
// Join two or more fields together.
template <typename T1, typename T2, typename... Ts>
constexpr auto JoinFields(T1 field1, T2 field2, Ts... args) {
auto bytes = JoinBytes(field1, field2);
return JoinFields(bytes, args...);
}
// Creates a constexpr char[] representing the fields for an ETW event.
// Declare the variable as `constexpr static auto` and provide the event name,
// followed by a series of `Field` invocations for each field.
//
// Example:
// constexpr static auto event_fields = EventFields("my1stEvent",
// Field("MyIntVal", kTypeInt32),
// Field("MyMsg", kTypeAnsiStr),
// Field("Address", kTypePointer));
template <std::size_t count, typename... Ts>
constexpr auto EventFields(char const (&name)[count], Ts... field_args) {
auto name_bytes = MakeStrBytes(name);
auto fields = JoinFields(field_args...);
auto data = JoinBytes(name_bytes, fields);
auto header = Header(data.size);
return JoinBytes(header, data);
}
constexpr auto EventMetadata(uint16_t id, uint64_t keywords) {
return EVENT_DESCRIPTOR{id,
0, // Version
kManifestFreeChannel,
TRACE_LEVEL_INFORMATION, // Level
EVENT_TRACE_TYPE_START, // Opcode
0, // Task
keywords};
}
void SetMetaDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptor,
UINT16 const UNALIGNED* traits, const void* metadata,
size_t size) {
// The first descriptor is the provider traits (just the name currently)
uint16_t traits_size = *reinterpret_cast<const uint16_t*>(traits);
EventDataDescCreate(data_descriptor, traits, traits_size);
data_descriptor->Type = EVENT_DATA_DESCRIPTOR_TYPE_PROVIDER_METADATA;
++data_descriptor;
// The second descriptor contains the data to describe the field layout
EventDataDescCreate(data_descriptor, metadata, static_cast<ULONG>(size));
data_descriptor->Type = EVENT_DATA_DESCRIPTOR_TYPE_EVENT_METADATA;
}
// Base case, no fields left to set
inline void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors) {}
// Need to declare all the base overloads in advance, as ther bodies may become
// a point of reference for any of the overloads, and only overloads that have
// been seen will be candidates.
template <typename... Ts>
void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors,
const std::wstring& value, const Ts&... rest);
template <typename... Ts>
void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors,
const std::string& value, const Ts&... rest);
template <typename... Ts>
void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors,
const char* value, const Ts&... rest);
// One or more fields to set
template <typename T, typename... Ts>
void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors,
const T& value, const Ts&... rest) {
EventDataDescCreate(data_descriptors, &value, sizeof(value));
SetFieldDescriptors(++data_descriptors, rest...);
}
// Specialize for strings
template <typename... Ts>
void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors,
const std::wstring& value, const Ts&... rest) {
EventDataDescCreate(data_descriptors, value.data(),
static_cast<ULONG>(value.size() * 2 + 2));
SetFieldDescriptors(++data_descriptors, rest...);
}
template <typename... Ts>
void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors,
const std::string& value, const Ts&... rest) {
EventDataDescCreate(data_descriptors, value.data(),
static_cast<ULONG>(value.size() + 1));
SetFieldDescriptors(++data_descriptors, rest...);
}
template <typename... Ts>
void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors,
const char* value, const Ts&... rest) {
ULONG size = static_cast<ULONG>(strlen(value) + 1);
EventDataDescCreate(data_descriptors, value, size);
SetFieldDescriptors(++data_descriptors, rest...);
}
// This function does the actual writing of the event via the Win32 API
inline ULONG LogEvent(uint64_t regHandle,
const EVENT_DESCRIPTOR* event_descriptor,
EVENT_DATA_DESCRIPTOR* data_descriptor,
ULONG desc_count) {
if (regHandle == 0) return ERROR_SUCCESS;
return EventWriteTransfer(regHandle, event_descriptor, NULL /* ActivityId */,
NULL /* RelatedActivityId */, desc_count,
data_descriptor);
}
// This template is called by the provider implementation
template <typename T, typename... Fs>
void LogEventData(const TraceLoggingHProvider provider,
const EVENT_DESCRIPTOR* event_descriptor, T* meta,
const Fs&... fields) {
const size_t descriptor_count = sizeof...(fields) + kMetaDescriptorsCount;
EVENT_DATA_DESCRIPTOR descriptors[sizeof...(fields) + kMetaDescriptorsCount];
SetMetaDescriptors(descriptors, provider->ProviderMetadataPtr, meta->bytes,
meta->size);
EVENT_DATA_DESCRIPTOR* data_descriptors = descriptors + kMetaDescriptorsCount;
SetFieldDescriptors(data_descriptors, fields...);
LogEvent(provider->RegHandle, event_descriptor, descriptors,
descriptor_count);
}
} // namespace ETWJITInterface
} // namespace internal
} // namespace v8
#endif // V8_DIAGNOSTICS_SYSTEM_JIT_METADATA_WIN_H_