v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
etw-jit-win.cc
Go to the documentation of this file.
1// Copyright 2010 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.
5
9#include "include/v8-script.h"
10#include "src/api/api-inl.h"
12#include "src/base/logging.h"
19#include "src/logging/log.h"
23
24#if !defined(V8_ENABLE_ETW_STACK_WALKING)
25#error "This file is only compiled if v8_enable_etw_stack_walking"
26#endif
27
28#include <windows.h>
29
30#include <iostream>
31#include <memory>
32#include <string>
33#include <unordered_map>
34#include <unordered_set>
35#include <utility>
36
37namespace v8 {
38namespace internal {
39namespace ETWJITInterface {
40
43
45
47 DCHECK(v8_flags.enable_etw_stack_walking ||
48 v8_flags.enable_etw_by_custom_filter_only);
49 ETWTRACEDBG << "MaybeSetHandlerNow called" << std::endl;
50 // Iterating read-only heap before sealed might not be safe.
52 !EtwIsolateOperations::Instance()->HeapReadOnlySpaceWritable(isolate)) {
53 if (etw_filter_payload_glob.Pointer()->empty()) {
54 DCHECK(v8_flags.enable_etw_stack_walking);
56 isolate, 0, std::weak_ptr<EtwIsolateCaptureStateMonitor>(),
58 } else {
60 isolate, 0, *etw_filter_payload_glob.Pointer(),
61 std::weak_ptr<EtwIsolateCaptureStateMonitor>(), kJitCodeEventDefault);
62 }
63 }
64}
65
66// TODO(v8/11911): UnboundScript::GetLineNumber should be replaced
68 return event->script.IsEmpty() ? Tagged<SharedFunctionInfo>()
70}
71
72std::wstring GetScriptMethodNameFromEvent(const JitCodeEvent* event) {
73 int name_len = static_cast<int>(event->name.len);
74 // Note: event->name.str is not null terminated.
75 std::wstring method_name(name_len + 1, '\0');
76 MultiByteToWideChar(
77 CP_UTF8, 0, event->name.str, name_len,
78 // Const cast needed as building with C++14 (not const in >= C++17)
79 const_cast<LPWSTR>(method_name.data()),
80 static_cast<int>(method_name.size()));
81 return method_name;
82}
83
86 auto sfi_name = sfi->DebugNameCStr();
87 int method_name_length = static_cast<int>(strlen(sfi_name.get()));
88 std::wstring method_name(method_name_length, L'\0');
89 MultiByteToWideChar(CP_UTF8, 0, sfi_name.get(), method_name_length,
90 const_cast<LPWSTR>(method_name.data()),
91 static_cast<int>(method_name.length()));
92 return method_name;
93}
94
95std::wstring GetScriptMethodName(const JitCodeEvent* event) {
96 auto sfi = GetSharedFunctionInfo(event);
97 return sfi.is_null() ? GetScriptMethodNameFromEvent(event)
99}
100
101void UpdateETWEnabled(bool enabled, uint32_t options) {
102 DCHECK(v8_flags.enable_etw_stack_walking ||
103 v8_flags.enable_etw_by_custom_filter_only);
105
107}
108
109// This callback is invoked by Windows every time the ETW tracing status is
110// changed for this application. As such, V8 needs to track its value for
111// knowing if the event requires us to emit JIT runtime events.
113 LPCGUID /* source_id */, ULONG is_enabled, UCHAR level,
114 ULONGLONG match_any_keyword, ULONGLONG match_all_keyword,
115 PEVENT_FILTER_DESCRIPTOR filter_data, PVOID /* callback_context */) {
116 ETWTRACEDBG << "ETWEnableCallback called with is_enabled==" << is_enabled
117 << std::endl;
118
119 bool is_etw_enabled_now =
120 is_enabled && level >= kTraceLevel &&
121 (match_any_keyword & kJScriptRuntimeKeyword) &&
122 ((match_all_keyword & kJScriptRuntimeKeyword) == match_all_keyword);
123
124 uint32_t options = kJitCodeEventDefault;
125 if (is_enabled == kEtwControlCaptureState) {
127 }
128
129 FilterDataType* etw_filter = etw_filter_payload_glob.Pointer();
130
131 if (!is_etw_enabled_now) {
132 // Disable the current tracing session.
133 ETWTRACEDBG << "Disabling" << std::endl;
134 etw_filter->clear();
135 UpdateETWEnabled(false, options);
136 return;
137 }
138
139 DCHECK(is_etw_enabled_now);
140
141 // Ignore the callback if ETW tracing is not fully enabled and we are not
142 // passing a custom filter.
143 if (!v8_flags.enable_etw_stack_walking &&
144 (!filter_data || filter_data->Type != EVENT_FILTER_TYPE_SCHEMATIZED)) {
145 return;
146 }
147
148 if (v8_flags.enable_etw_stack_walking &&
149 (!filter_data || filter_data->Type != EVENT_FILTER_TYPE_SCHEMATIZED)) {
150 etw_filter->clear();
151 ETWTRACEDBG << "Enabling without filter" << std::endl;
152 UpdateETWEnabled(is_etw_enabled_now, options);
153 return;
154 }
155
156 // Ignore the callback if the --enable-etw-by-custom-filter-only flag is not
157 // enabled.
158 if (!v8_flags.enable_etw_by_custom_filter_only) return;
159
160 // Validate custom filter data configured in a WPR profile and passed to the
161 // callback.
162
163 if (filter_data->Size <= sizeof(EVENT_FILTER_DESCRIPTOR)) {
164 return; // Invalid data
165 }
166
167 EVENT_FILTER_HEADER* filter_event_header =
168 reinterpret_cast<EVENT_FILTER_HEADER*>(filter_data->Ptr);
169 if (filter_event_header->Size < sizeof(EVENT_FILTER_HEADER)) {
170 return; // Invalid data
171 }
172
173 const uint8_t* payload_start =
174 reinterpret_cast<uint8_t*>(filter_event_header) +
175 sizeof(EVENT_FILTER_HEADER);
176 const size_t payload_size =
177 filter_event_header->Size - sizeof(EVENT_FILTER_HEADER);
178 etw_filter->assign(payload_start, payload_start + payload_size);
180
181 ETWTRACEDBG << "Enabling with filter data" << std::endl;
183 reinterpret_cast<const uint8_t*>(etw_filter->data()), etw_filter->size(),
184 options);
185}
186
187void Register() {
188 DCHECK(!TraceLoggingProviderEnabled(g_v8Provider, 0, 0));
189 TraceLoggingRegisterEx(g_v8Provider, ETWEnableCallback, nullptr);
190}
191
193 if (g_v8Provider) {
194 TraceLoggingUnregister(g_v8Provider);
195 }
197}
198
199void AddIsolate(Isolate* isolate) {
201}
202
206
207void EventHandler(const JitCodeEvent* event) {
208 v8::Isolate* script_context = event->isolate;
209 Isolate* isolate = reinterpret_cast<Isolate*>(script_context);
210 if (!isolate->IsETWTracingEnabled()) return;
211
213 if (event->type != v8::JitCodeEvent::EventType::CODE_ADDED) return;
214
215 std::wstring method_name = GetScriptMethodName(event);
216
217 // No heap allocations after this point.
219
220 int script_id = 0;
221 uint32_t script_line = -1;
222 uint32_t script_column = -1;
223
225 if (!sfi.is_null() && IsScript(sfi->script())) {
226 Tagged<Script> script = Cast<Script>(sfi->script());
227
228 // if the first time seeing this source file, log the SourceLoad event
229 script_id = script->id();
230 if (IsolateLoadScriptData::MaybeAddLoadedScript(isolate, script_id)) {
231 std::wstring wstr_name(0, L'\0');
232 Tagged<Object> script_name = script->GetNameOrSourceURL();
233 if (IsString(script_name)) {
234 Tagged<String> v8str_name = Cast<String>(script_name);
235 wstr_name.resize(v8str_name->length());
236 // On Windows wchar_t == uint16_t. const_Cast needed for C++14.
237 uint16_t* wstr_data = const_cast<uint16_t*>(
238 reinterpret_cast<const uint16_t*>(wstr_name.data()));
239 String::WriteToFlat(v8str_name, wstr_data, 0, v8str_name->length());
240 }
241
242 if (isolate->ETWIsInRundown()) {
243 constexpr static auto source_dcstart_event_meta =
245 constexpr static auto source_dcstart_event_fields =
246 EventFields("SourceDCStart", Field("SourceID", TlgInUINT64),
247 Field("ScriptContextID", TlgInPOINTER),
248 Field("SourceFlags", TlgInUINT32),
249 Field("Url", TlgInUNICODESTRING));
250 LogEventData(g_v8Provider, &source_dcstart_event_meta,
251 &source_dcstart_event_fields, (uint64_t)script_id,
252 script_context,
253 (uint32_t)0, // SourceFlags
254 wstr_name);
255 } else {
256 constexpr static auto source_load_event_meta =
258 constexpr static auto source_load_event_fields =
259 EventFields("SourceLoad", Field("SourceID", TlgInUINT64),
260 Field("ScriptContextID", TlgInPOINTER),
261 Field("SourceFlags", TlgInUINT32),
262 Field("Url", TlgInUNICODESTRING));
263 LogEventData(g_v8Provider, &source_load_event_meta,
264 &source_load_event_fields, (uint64_t)script_id,
265 script_context,
266 (uint32_t)0, // SourceFlags
267 wstr_name);
268 }
269 }
270
272 script->GetPositionInfo(sfi->StartPosition(), &info);
273 script_line = info.line + 1;
274 script_column = info.column + 1;
275 }
276
277 auto code =
279 isolate, Address(event->code_start));
280 if (code && code.value()->is_builtin()) {
281 bool skip_emitting_builtin = true;
282 // Skip logging functions with code kind BUILTIN as they are already present
283 // in the PDB.
284
285 // We should still emit builtin addresses if they are an interpreter
286 // trampoline.
287 if (code.value()->has_instruction_stream()) {
288 skip_emitting_builtin = false;
289
290 // The only builtin that might have instruction stream is the
291 // InterpreterEntryTrampoline builtin and only when the
292 // v8_flags.interpreted_frames_native_stack flag is enabled.
294 code.value()->is_builtin(),
295 code.value()->builtin_id() == Builtin::kInterpreterEntryTrampoline &&
296 isolate->interpreted_frames_native_stack());
297 } else {
298 DCHECK(code.value()->is_builtin());
299 }
300
301 // If the builtin has been relocated, we still need to emit the address
302 if (skip_emitting_builtin && V8_SHORT_BUILTIN_CALLS_BOOL &&
303 v8_flags.short_builtin_calls) {
304 CodeRange* code_range = isolate->isolate_group()->GetCodeRange();
305 if (code_range && code_range->embedded_blob_code_copy() != nullptr) {
306 skip_emitting_builtin = false;
307 }
308 }
309
310 if (skip_emitting_builtin) {
311 return;
312 }
313 }
314
315 if (isolate->ETWIsInRundown()) {
316 constexpr static auto method_dcstart_event_meta =
318 constexpr static auto method_dcstart_event_fields = EventFields(
319 "MethodDCStart", Field("ScriptContextID", TlgInPOINTER),
320 Field("MethodStartAddress", TlgInPOINTER),
321 Field("MethodSize", TlgInUINT64), Field("MethodID", TlgInUINT32),
322 Field("MethodFlags", TlgInUINT16),
323 Field("MethodAddressRangeID", TlgInUINT16),
324 Field("SourceID", TlgInUINT64), Field("Line", TlgInUINT32),
325 Field("Column", TlgInUINT32), Field("MethodName", TlgInUNICODESTRING));
326 LogEventData(g_v8Provider, &method_dcstart_event_meta,
327 &method_dcstart_event_fields, script_context,
328 event->code_start, (uint64_t)event->code_len,
329 (uint32_t)0, // MethodId
330 (uint16_t)0, // MethodFlags
331 (uint16_t)0, // MethodAddressRangeId
332 (uint64_t)script_id, script_line, script_column, method_name);
333 } else {
334 constexpr static auto method_load_event_meta =
336 constexpr static auto method_load_event_fields = EventFields(
337 "MethodLoad", Field("ScriptContextID", TlgInPOINTER),
338 Field("MethodStartAddress", TlgInPOINTER),
339 Field("MethodSize", TlgInUINT64), Field("MethodID", TlgInUINT32),
340 Field("MethodFlags", TlgInUINT16),
341 Field("MethodAddressRangeID", TlgInUINT16),
342 Field("SourceID", TlgInUINT64), Field("Line", TlgInUINT32),
343 Field("Column", TlgInUINT32), Field("MethodName", TlgInUNICODESTRING));
344 LogEventData(g_v8Provider, &method_load_event_meta,
345 &method_load_event_fields, script_context, event->code_start,
346 (uint64_t)event->code_len,
347 (uint32_t)0, // MethodId
348 (uint16_t)0, // MethodFlags
349 (uint16_t)0, // MethodAddressRangeId
350 (uint64_t)script_id, script_line, script_column, method_name);
351 }
352}
353
354} // namespace ETWJITInterface
355} // namespace internal
356} // namespace v8
static v8::internal::DirectHandle< To > OpenDirectHandle(v8::Local< From > handle)
Definition api.h:279
uint8_t * embedded_blob_code_copy() const
Definition code-range.h:83
virtual std::optional< Tagged< GcSafeCode > > HeapGcSafeTryFindCodeForInnerPointer(Isolate *isolate, Address address)
static void EnableLog(Isolate *isolate, size_t event_id, std::weak_ptr< EtwIsolateCaptureStateMonitor > weak_monitor, uint32_t options)
static void EnableLogWithFilterDataOnAllIsolates(const uint8_t *data, size_t size, uint32_t options)
static void EnableLogWithFilterData(Isolate *isolate, size_t event_id, const std::string &EnableLogWithFilterData, std::weak_ptr< EtwIsolateCaptureStateMonitor > weak_monitor, uint32_t options)
static bool MaybeAddLoadedScript(Isolate *isolate, int script_id)
static void UpdateAllIsolates(bool etw_enabled, uint32_t options)
static void WriteToFlat(Tagged< String > source, SinkCharT *sink, uint32_t start, uint32_t length)
Definition string.cc:772
V8_INLINE constexpr bool is_null() const
Definition tagged.h:502
Handle< SharedFunctionInfo > info
#define ETWTRACEDBG
#define V8_DEFINE_TRACELOGGING_PROVIDER(v8Provider)
#define V8_DECLARE_TRACELOGGING_PROVIDER(v8Provider)
#define V8_SHORT_BUILTIN_CALLS_BOOL
void MaybeSetHandlerNow(Isolate *isolate)
void WINAPI V8_EXPORT_PRIVATE ETWEnableCallback(LPCGUID, ULONG is_enabled, UCHAR level, ULONGLONG match_any_keyword, ULONGLONG match_all_keyword, PEVENT_FILTER_DESCRIPTOR filter_data, PVOID)
constexpr unsigned char kTraceLevel
std::wstring GetScriptMethodName(const JitCodeEvent *event)
void LogEventData(const TraceLoggingHProvider provider, const EVENT_DESCRIPTOR *event_descriptor, T *meta, const Fs &... fields)
constexpr auto Field(char const (&s)[count], uint8_t type)
constexpr uint32_t kEtwRundown
Definition etw-jit-win.h:27
void EventHandler(const JitCodeEvent *event)
void AddIsolate(Isolate *isolate)
std::atomic< bool > has_active_etw_tracing_session_or_custom_filter
void RemoveIsolate(Isolate *isolate)
std::wstring GetScriptMethodNameFromSharedFunctionInfo(Tagged< SharedFunctionInfo > sfi)
void UpdateETWEnabled(bool enabled, uint32_t options)
constexpr uint64_t kJScriptRuntimeKeyword
base::LazyInstance< FilterDataType >::type etw_filter_payload_glob
Tagged< SharedFunctionInfo > GetSharedFunctionInfo(const JitCodeEvent *event)
constexpr auto EventMetadata(uint16_t id, uint64_t keywords)
constexpr uint32_t kEtwControlCaptureState
std::wstring GetScriptMethodNameFromEvent(const JitCodeEvent *event)
constexpr auto EventFields(char const (&name)[count], Ts... field_args)
Tagged(T object) -> Tagged< T >
constexpr int L
V8_EXPORT_PRIVATE FlagValues v8_flags
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
@ kJitCodeEventEnumExisting
@ kJitCodeEventDefault
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK(condition)
Definition logging.h:482
#define V8_EXPORT_PRIVATE
Definition macros.h:460
struct name_t name
Local< UnboundScript > script
void * PVOID
#define WINAPI