Commit 88926769 authored by Sara Tang's avatar Sara Tang Committed by Commit Bot

Step 2 (of 2) for ETW integration into V8

Design doc:
https://docs.google.com/document/d/1xkXj94iExFgLWc_OszTNyNGi523ARaKMWPZTeomhI4U

This is the second (and hopefully final!) change list needed to
integrate ETW into V8. In particular, we added stack-walking
functionality for JIT-ted functions!

Some notes on instrumentation:
  - The gist of getting stack-walking in ETW is we need to emit events
    with specific event IDs. These events get stitched into a pseudo-PDB
    that is recognizable by ETW.
  - Unfortunately, we cannot rely on the TraceLogging API from the first
    CL, as it does not support specifying event IDs. Instead, Bill
    Ticehurst wrote an API that peels back the TraceLogging API just
    enough so that we can specify event IDs. This API is the entirety of
    etw-metdata.h
  - We attach a CodeEventHandler that logs a stack-walking event whenever
    code movement is triggered.

Bug: v8:11043
Change-Id: I1bf57c985b7375f045089027855b1c03878abb78
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2616221Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Sara Tang <sartang@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#73145}
parent 476582bf
......@@ -4186,6 +4186,17 @@ v8_source_set("v8_base_without_compiler") {
]
}
# Architecture independent but platform-specific sources
if (is_win) {
if (v8_enable_system_instrumentation) {
sources += [
"src/diagnostics/system-jit-metadata-win.h",
"src/diagnostics/system-jit-win.cc",
"src/diagnostics/system-jit-win.h",
]
}
}
configs = [
":internal_config",
":cppgc_base_config",
......@@ -4669,6 +4680,7 @@ v8_component("v8_libplatform") {
sources -= [
"//base/trace_event/common/trace_event_common.h",
"src/libplatform/tracing/recorder-default.cc",
"src/libplatform/tracing/recorder.h",
"src/libplatform/tracing/trace-buffer.cc",
"src/libplatform/tracing/trace-buffer.h",
"src/libplatform/tracing/trace-object.cc",
......
......@@ -70,4 +70,7 @@ specific_include_rules = {
"builtins-trace\.cc": [
"+protos/perfetto",
],
"system-jit-win\.cc": [
"+src/libplatform/tracing/recorder.h",
],
}
......@@ -128,12 +128,16 @@
#if V8_OS_WIN
#include <versionhelpers.h>
#include <windows.h>
#include "include/v8-wasm-trap-handler-win.h"
#include "src/trap-handler/handler-inside-win.h"
#if defined(V8_OS_WIN64)
#include "src/base/platform/wrappers.h"
#include "src/diagnostics/unwinding-info-win64.h"
#endif // V8_OS_WIN64
#if defined(V8_ENABLE_SYSTEM_INSTRUMENTATION)
#include "src/diagnostics/system-jit-win.h"
#endif
#endif // V8_OS_WIN
// Has to be the last include (doesn't have include guards):
......@@ -8412,6 +8416,12 @@ void Isolate::Initialize(Isolate* isolate,
code_event_handler = i::GDBJITInterface::EventHandler;
}
#endif // ENABLE_GDB_JIT_INTERFACE
#if defined(V8_TARGET_OS_WIN) && defined(V8_ENABLE_SYSTEM_INSTRUMENTATION)
if (code_event_handler == nullptr) {
code_event_handler = i::ETWJITInterface::EventHandler;
}
#endif // defined(V8_TARGET_OS_WIN)
if (code_event_handler) {
i_isolate->InitializeLoggingAndCounters();
i_isolate->logger()->SetCodeEventHandler(kJitCodeEventDefault,
......
// 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>
#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_
// Copyright 2010 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.
#include "src/diagnostics/system-jit-win.h"
#include "src/base/logging.h"
#include "src/diagnostics/system-jit-metadata-win.h"
#include "src/libplatform/tracing/recorder.h"
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wc++98-compat-extra-semi"
#endif
namespace v8 {
namespace internal {
namespace ETWJITInterface {
TRACELOGGING_DECLARE_PROVIDER(g_v8Provider);
TRACELOGGING_DEFINE_PROVIDER(g_v8Provider, "V8.js", (V8_ETW_GUID));
using ScriptMapType = std::unordered_set<int>;
ScriptMapType* script_map = new ScriptMapType();
void Register() {
DCHECK(!TraceLoggingProviderEnabled(g_v8Provider, 0, 0));
TraceLoggingRegister(g_v8Provider);
}
void Unregister() {
if (g_v8Provider) {
TraceLoggingUnregister(g_v8Provider);
}
}
void EventHandler(const JitCodeEvent* event) {
if (event->code_type != v8::JitCodeEvent::CodeType::JIT_CODE) return;
if (event->type != v8::JitCodeEvent::EventType::CODE_ADDED) return;
int name_len = static_cast<int>(event->name.len);
// Note: event->name.str is not null terminated.
std::wstring method_name(name_len + 1, '\0');
MultiByteToWideChar(
CP_UTF8, 0, event->name.str, name_len,
// Const cast needed as building with C++14 (not const in >= C++17)
const_cast<LPWSTR>(method_name.data()),
static_cast<int>(method_name.size()));
v8::Isolate* script_context = event->isolate;
auto script = event->script;
int script_id = 0;
if (!script.IsEmpty()) {
// if the first time seeing this source file, log the SourceLoad event
script_id = script->GetId();
if (script_map->find(script_id) == script_map->end()) {
script_map->insert(script_id);
auto script_name = script->GetScriptName();
std::wstring wstr_name(0, L'\0');
if (script_name->IsString()) {
auto v8str_name = script_name.As<v8::String>();
wstr_name.resize(v8str_name->Length());
// On Windows wchar_t == uint16_t. const_Cast needed for C++14.
uint16_t* wstr_data = const_cast<uint16_t*>(
reinterpret_cast<const uint16_t*>(wstr_name.data()));
v8str_name->Write(event->isolate, wstr_data);
}
constexpr static auto source_load_event_meta =
EventMetadata(kSourceLoadEventID, kJScriptRuntimeKeyword);
constexpr static auto source_load_event_fields = EventFields(
"SourceLoad", Field("SourceID", TlgInUINT64),
Field("ScriptContextID", TlgInPOINTER),
Field("SourceFlags", TlgInUINT32), Field("Url", TlgInUNICODESTRING));
LogEventData(g_v8Provider, &source_load_event_meta,
&source_load_event_fields, (uint64_t)script_id,
script_context,
(uint32_t)0, // SourceFlags
wstr_name);
}
}
constexpr static auto method_load_event_meta =
EventMetadata(kMethodLoadEventID, kJScriptRuntimeKeyword);
constexpr static auto method_load_event_fields = EventFields(
"MethodLoad", Field("ScriptContextID", TlgInPOINTER),
Field("MethodStartAddress", TlgInPOINTER),
Field("MethodSize", TlgInUINT64), Field("MethodID", TlgInUINT32),
Field("MethodFlags", TlgInUINT16),
Field("MethodAddressRangeID", TlgInUINT16),
Field("SourceID", TlgInUINT64), Field("Line", TlgInUINT32),
Field("Column", TlgInUINT32), Field("MethodName", TlgInUNICODESTRING));
LogEventData(g_v8Provider, &method_load_event_meta, &method_load_event_fields,
script_context, event->code_start, (uint64_t)event->code_len,
(uint32_t)0, // MethodId
(uint16_t)0, // MethodFlags
(uint16_t)0, // MethodAddressRangeId
(uint64_t)script_id, (uint32_t)0, (uint32_t)0, // Line & Column
method_name);
}
} // namespace ETWJITInterface
} // namespace internal
} // namespace v8
// Copyright 2010 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_WIN_H_
#define V8_DIAGNOSTICS_SYSTEM_JIT_WIN_H_
#include "include/v8.h"
namespace v8 {
namespace internal {
namespace ETWJITInterface {
void Register();
void Unregister();
void EventHandler(const v8::JitCodeEvent* event);
} // namespace ETWJITInterface
} // namespace internal
} // namespace v8
#endif // V8_DIAGNOSTICS_SYSTEM_JIT_WIN_H_
......@@ -28,6 +28,10 @@
#include "src/tracing/tracing-category-observer.h"
#include "src/wasm/wasm-engine.h"
#if defined(V8_TARGET_OS_WIN) && defined(V8_ENABLE_SYSTEM_INSTRUMENTATION)
#include "src/diagnostics/system-jit-win.h"
#endif
namespace v8 {
namespace internal {
......@@ -166,10 +170,17 @@ void V8::InitializePlatform(v8::Platform* platform) {
platform_ = platform;
v8::base::SetPrintStackTrace(platform_->GetStackTracePrinter());
v8::tracing::TracingCategoryObserver::SetUp();
#if defined(V8_TARGET_OS_WIN) && defined(V8_ENABLE_SYSTEM_INSTRUMENTATION)
// TODO(sartang@microsoft.com): Move to platform specific diagnostics object
v8::internal::ETWJITInterface::Register();
#endif
}
void V8::ShutdownPlatform() {
CHECK(platform_);
#if defined(V8_TARGET_OS_WIN) && defined(V8_ENABLE_SYSTEM_INSTRUMENTATION)
v8::internal::ETWJITInterface::Unregister();
#endif
v8::tracing::TracingCategoryObserver::TearDown();
v8::base::SetPrintStackTrace(nullptr);
platform_ = nullptr;
......
......@@ -4,7 +4,7 @@
#ifndef V8_LIBPLATFORM_TRACING_RECORDER_WIN_H_
#define V8_LIBPLATFORM_TRACING_RECORDER_WIN_H_
#include <windows.h>
#include <Windows.h>
#include <TraceLoggingProvider.h>
#include "src/libplatform/tracing/recorder.h"
......@@ -13,11 +13,6 @@
#pragma clang diagnostic ignored "-Wc++98-compat-extra-semi"
#endif
#ifndef V8_ETW_GUID
#define V8_ETW_GUID \
0x57277741, 0x3638, 0x4A4B, 0xBD, 0xBA, 0x0A, 0xC6, 0xE4, 0x5D, 0xA5, 0x6C
#endif
namespace v8 {
namespace platform {
namespace tracing {
......
......@@ -9,6 +9,13 @@
#include "include/libplatform/v8-tracing.h"
#if V8_OS_WIN
#ifndef V8_ETW_GUID
#define V8_ETW_GUID \
0x57277741, 0x3638, 0x4A4B, 0xBD, 0xBA, 0x0A, 0xC6, 0xE4, 0x5D, 0xA5, 0x6C
#endif
#endif
namespace v8 {
namespace platform {
namespace tracing {
......@@ -19,13 +26,13 @@ namespace tracing {
// the --enable-system-instrumentation command line flag. When enabled, it is
// called from within SystemInstrumentationTraceWriter and replaces the
// JSONTraceWriter for event-tracing.
class Recorder {
class V8_PLATFORM_EXPORT Recorder {
public:
Recorder();
~Recorder();
bool IsEnabled();
bool IsEnabled(const uint8_t level);
static bool IsEnabled();
static bool IsEnabled(const uint8_t level);
void AddEvent(TraceObject* trace_event);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment