1 // Copyright 2020 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_DIAGNOSTICS_SYSTEM_JIT_METADATA_WIN_H_ 6 #define V8_DIAGNOSTICS_SYSTEM_JIT_METADATA_WIN_H_ 7 8 #include <Windows.h> 9 #ifndef VOID 10 #define VOID void 11 #endif 12 #include <TraceLoggingProvider.h> 13 #include <evntprov.h> 14 #include <evntrace.h> // defines TRACE_LEVEL_* and EVENT_TRACE_TYPE_* 15 16 #include <cstdint> 17 #include <string> 18 #include <unordered_set> 19 #include <utility> 20 21 namespace v8 { 22 namespace internal { 23 namespace ETWJITInterface { 24 25 /******************************************************************************* 26 Helper templates to create tightly packed metadata of the format expected by the 27 ETW data structures. 28 *******************************************************************************/ 29 30 // All "manifest-free" events should go to channel 11 by default 31 const uint8_t kManifestFreeChannel = 11; 32 33 // Number of metadescriptors. Use this to find out the index of the field 34 // descriptors in the descriptors_array 35 const uint8_t kMetaDescriptorsCount = 2; 36 37 // Filtering keyword to find JScript stack-walking events 38 constexpr uint64_t kJScriptRuntimeKeyword = 1; 39 40 constexpr uint16_t kSourceLoadEventID = 41; 41 constexpr uint16_t kMethodLoadEventID = 9; 42 43 // Structure to treat a string literal, or char[], as a constexpr byte sequence 44 template <size_t count> 45 struct str_bytes { 46 template <std::size_t... idx> str_bytesstr_bytes47 constexpr str_bytes(char const (&str)[count], std::index_sequence<idx...>) 48 : bytes{str[idx]...}, size(count) {} 49 50 // Concatenate two str_bytes 51 template <std::size_t count1, std::size_t count2, std::size_t... idx1, 52 std::size_t... idx2> str_bytesstr_bytes53 constexpr str_bytes(const str_bytes<count1>& s1, std::index_sequence<idx1...>, 54 const str_bytes<count2>& s2, std::index_sequence<idx2...>) 55 : bytes{s1.bytes[idx1]..., s2.bytes[idx2]...}, size(count) {} 56 57 char bytes[count]; // NOLINT 58 size_t size; 59 }; 60 61 // Specialization for 0 (base case when joining fields) 62 template <> 63 struct str_bytes<0> { 64 constexpr str_bytes() : bytes{}, size(0) {} 65 char bytes[1]; // MSVC doesn't like an array of 0 bytes 66 size_t size; 67 }; 68 69 // Factory function to simplify creating a str_bytes from a string literal 70 template <size_t count, typename idx = std::make_index_sequence<count>> 71 constexpr auto MakeStrBytes(char const (&s)[count]) { 72 return str_bytes<count>{s, idx{}}; 73 } 74 75 // Concatenates two str_bytes into one 76 template <std::size_t size1, std::size_t size2> 77 constexpr auto JoinBytes(const str_bytes<size1>& str1, 78 const str_bytes<size2>& str2) { 79 auto idx1 = std::make_index_sequence<size1>(); 80 auto idx2 = std::make_index_sequence<size2>(); 81 return str_bytes<size1 + size2>{str1, idx1, str2, idx2}; 82 } 83 84 // Creates an str_bytes which is the field name suffixed with the field type 85 template <size_t count> 86 constexpr auto Field(char const (&s)[count], uint8_t type) { 87 auto field_name = MakeStrBytes(s); 88 const char type_arr[1] = {static_cast<char>(type)}; 89 return JoinBytes(field_name, MakeStrBytes(type_arr)); 90 } 91 92 // Creates the ETW event metadata header, which consists of a uint16_t 93 // representing the total size, and a tag byte (always 0x00 currently). 94 constexpr auto Header(size_t size) { 95 size_t total_size = size + 3; // total_size includes the 2 byte size + tag 96 const char header_bytes[3] = {static_cast<char>(total_size & 0xFF), 97 static_cast<char>(total_size >> 8 & 0xFF), 98 '\0'}; 99 return MakeStrBytes(header_bytes); 100 } 101 102 // The JoinFields implementations below are a set of overloads for constructing 103 // a str_bytes representing the concatenated fields from a parameter pack. 104 105 // Empty case needed for events with no fields. 106 constexpr auto JoinFields() { return str_bytes<0>{}; } 107 108 // Only one field, or base case when multiple fields. 109 template <typename T1> 110 constexpr auto JoinFields(T1 field) { 111 return field; 112 } 113 114 // Join two or more fields together. 115 template <typename T1, typename T2, typename... Ts> 116 constexpr auto JoinFields(T1 field1, T2 field2, Ts... args) { 117 auto bytes = JoinBytes(field1, field2); 118 return JoinFields(bytes, args...); 119 } 120 121 // Creates a constexpr char[] representing the fields for an ETW event. 122 // Declare the variable as `constexpr static auto` and provide the event name, 123 // followed by a series of `Field` invocations for each field. 124 // 125 // Example: 126 // constexpr static auto event_fields = EventFields("my1stEvent", 127 // Field("MyIntVal", kTypeInt32), 128 // Field("MyMsg", kTypeAnsiStr), 129 // Field("Address", kTypePointer)); 130 template <std::size_t count, typename... Ts> 131 constexpr auto EventFields(char const (&name)[count], Ts... field_args) { 132 auto name_bytes = MakeStrBytes(name); 133 auto fields = JoinFields(field_args...); 134 auto data = JoinBytes(name_bytes, fields); 135 136 auto header = Header(data.size); 137 return JoinBytes(header, data); 138 } 139 140 constexpr auto EventMetadata(uint16_t id, uint64_t keywords) { 141 return EVENT_DESCRIPTOR{id, 142 0, // Version 143 kManifestFreeChannel, 144 TRACE_LEVEL_INFORMATION, // Level 145 EVENT_TRACE_TYPE_START, // Opcode 146 0, // Task 147 keywords}; 148 } 149 150 void SetMetaDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptor, 151 UINT16 const UNALIGNED* traits, const void* metadata, 152 size_t size) { 153 // The first descriptor is the provider traits (just the name currently) 154 uint16_t traits_size = *reinterpret_cast<const uint16_t*>(traits); 155 EventDataDescCreate(data_descriptor, traits, traits_size); 156 data_descriptor->Type = EVENT_DATA_DESCRIPTOR_TYPE_PROVIDER_METADATA; 157 ++data_descriptor; 158 159 // The second descriptor contains the data to describe the field layout 160 EventDataDescCreate(data_descriptor, metadata, static_cast<ULONG>(size)); 161 data_descriptor->Type = EVENT_DATA_DESCRIPTOR_TYPE_EVENT_METADATA; 162 } 163 164 // Base case, no fields left to set 165 inline void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors) {} 166 167 // Need to declare all the base overloads in advance, as ther bodies may become 168 // a point of reference for any of the overloads, and only overloads that have 169 // been seen will be candidates. 170 template <typename... Ts> 171 void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors, 172 const std::wstring& value, const Ts&... rest); 173 template <typename... Ts> 174 void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors, 175 const std::string& value, const Ts&... rest); 176 template <typename... Ts> 177 void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors, 178 const char* value, const Ts&... rest); 179 180 // One or more fields to set 181 template <typename T, typename... Ts> 182 void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors, 183 const T& value, const Ts&... rest) { 184 EventDataDescCreate(data_descriptors, &value, sizeof(value)); 185 SetFieldDescriptors(++data_descriptors, rest...); 186 } 187 188 // Specialize for strings 189 template <typename... Ts> 190 void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors, 191 const std::wstring& value, const Ts&... rest) { 192 EventDataDescCreate(data_descriptors, value.data(), 193 static_cast<ULONG>(value.size() * 2 + 2)); 194 SetFieldDescriptors(++data_descriptors, rest...); 195 } 196 197 template <typename... Ts> 198 void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors, 199 const std::string& value, const Ts&... rest) { 200 EventDataDescCreate(data_descriptors, value.data(), 201 static_cast<ULONG>(value.size() + 1)); 202 SetFieldDescriptors(++data_descriptors, rest...); 203 } 204 205 template <typename... Ts> 206 void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptors, 207 const char* value, const Ts&... rest) { 208 ULONG size = static_cast<ULONG>(strlen(value) + 1); 209 EventDataDescCreate(data_descriptors, value, size); 210 SetFieldDescriptors(++data_descriptors, rest...); 211 } 212 213 // This function does the actual writing of the event via the Win32 API 214 inline ULONG LogEvent(uint64_t regHandle, 215 const EVENT_DESCRIPTOR* event_descriptor, 216 EVENT_DATA_DESCRIPTOR* data_descriptor, 217 ULONG desc_count) { 218 if (regHandle == 0) return ERROR_SUCCESS; 219 return EventWriteTransfer(regHandle, event_descriptor, NULL /* ActivityId */, 220 NULL /* RelatedActivityId */, desc_count, 221 data_descriptor); 222 } 223 224 // This template is called by the provider implementation 225 template <typename T, typename... Fs> 226 void LogEventData(const TraceLoggingHProvider provider, 227 const EVENT_DESCRIPTOR* event_descriptor, T* meta, 228 const Fs&... fields) { 229 const size_t descriptor_count = sizeof...(fields) + kMetaDescriptorsCount; 230 EVENT_DATA_DESCRIPTOR descriptors[sizeof...(fields) + kMetaDescriptorsCount]; 231 232 SetMetaDescriptors(descriptors, provider->ProviderMetadataPtr, meta->bytes, 233 meta->size); 234 235 EVENT_DATA_DESCRIPTOR* data_descriptors = descriptors + kMetaDescriptorsCount; 236 SetFieldDescriptors(data_descriptors, fields...); 237 238 LogEvent(provider->RegHandle, event_descriptor, descriptors, 239 descriptor_count); 240 } 241 242 } // namespace ETWJITInterface 243 } // namespace internal 244 } // namespace v8 245 246 #endif // V8_DIAGNOSTICS_SYSTEM_JIT_METADATA_WIN_H_ 247