// 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_