• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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