%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/diagnostics/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/diagnostics/etw-jit-win.cc |
// 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/etw-jit-win.h"
#include "include/v8-callbacks.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "include/v8-primitive.h"
#include "include/v8-script.h"
#include "src/api/api-inl.h"
#include "src/base/lazy-instance.h"
#include "src/base/logging.h"
#include "src/base/platform/platform.h"
#include "src/diagnostics/etw-jit-metadata-win.h"
#include "src/logging/log.h"
#include "src/objects/shared-function-info.h"
#include "src/tasks/cancelable-task.h"
#include "src/tasks/task-utils.h"
#if !defined(V8_ENABLE_ETW_STACK_WALKING)
#error "This file is only compiled if v8_enable_etw_stack_walking"
#endif
namespace v8 {
namespace internal {
namespace ETWJITInterface {
V8_DECLARE_TRACELOGGING_PROVIDER(g_v8Provider);
V8_DEFINE_TRACELOGGING_PROVIDER(g_v8Provider);
std::atomic<bool> is_etw_enabled = false;
namespace {
class IsolateLoadScriptData {
public:
explicit IsolateLoadScriptData(Isolate* isolate) : isolate_(isolate) {}
explicit IsolateLoadScriptData(IsolateLoadScriptData&& rhs) V8_NOEXCEPT {
isolate_ = rhs.isolate_;
loaded_scripts_ids_ = std::move(rhs.loaded_scripts_ids_);
event_id_ = rhs.event_id_.load();
}
static void AddIsolate(Isolate* isolate);
static void RemoveIsolate(Isolate* isolate);
static void UpdateAllIsolates(bool etw_enabled);
static bool MaybeAddLoadedScript(Isolate* isolate, int script_id);
static void EnableLog(Isolate* isolate, size_t event_id);
static void DisableLog(Isolate* isolate, size_t event_id);
static void EnableLogWithFilterDataOnAllIsolates(const uint8_t* data,
size_t size);
static void EnableLogWithFilterData(
Isolate* isolate, size_t event_id,
const std::string& EnableLogWithFilterData);
private:
static IsolateLoadScriptData& GetData(Isolate* isolate);
void EnqueueEnableLog() {
size_t event_id = event_id_.fetch_add(1);
isolate_->RequestInterrupt(
// Executed in the isolate thread.
[](v8::Isolate* v8_isolate, void* data) {
EnableLog(reinterpret_cast<Isolate*>(v8_isolate),
reinterpret_cast<size_t>(data));
},
reinterpret_cast<void*>(event_id + 1));
}
void EnqueueDisableLog() {
size_t event_id = event_id_.fetch_add(1);
isolate_->RequestInterrupt(
// Executed in the isolate thread.
[](v8::Isolate* v8_isolate, void* data) {
DisableLog(reinterpret_cast<Isolate*>(v8_isolate),
reinterpret_cast<size_t>(data));
},
reinterpret_cast<void*>(event_id + 1));
}
struct EnableWithFilterDataInterruptData {
size_t event_id;
std::string payload;
};
void EnqueueEnableLogWithFilterData(const std::string& etw_filter_payload) {
size_t event_id = event_id_.fetch_add(1);
isolate_->RequestInterrupt(
// Executed in the isolate thread.
[](v8::Isolate* v8_isolate, void* data) {
std::unique_ptr<EnableWithFilterDataInterruptData> interrupt_data(
reinterpret_cast<EnableWithFilterDataInterruptData*>(data));
size_t event_id = interrupt_data->event_id;
std::string etw_filter_payload = interrupt_data->payload;
EnableLogWithFilterData(reinterpret_cast<Isolate*>(v8_isolate),
event_id, etw_filter_payload);
},
new EnableWithFilterDataInterruptData{event_id + 1,
etw_filter_payload});
}
bool IsScriptLoaded(int script_id) const {
return loaded_scripts_ids_.find(script_id) != loaded_scripts_ids_.end();
}
void AddLoadedScript(int script_id) { loaded_scripts_ids_.insert(script_id); }
void RemoveAllLoadedScripts() { loaded_scripts_ids_.clear(); }
size_t CurrentEventId() const { return event_id_.load(); }
Isolate* isolate_ = nullptr;
std::unordered_set<int> loaded_scripts_ids_;
std::atomic<size_t> event_id_ = 0;
};
static base::LazyMutex isolates_mutex = LAZY_MUTEX_INITIALIZER;
using IsolateMapType =
std::unordered_map<v8::internal::Isolate*, IsolateLoadScriptData>;
static base::LazyInstance<IsolateMapType>::type isolate_map =
LAZY_INSTANCE_INITIALIZER;
using FilterDataType = std::string;
// Used when Isolates are created during a ETW tracing session.
static base::LazyInstance<FilterDataType>::type etw_filter_payload =
LAZY_INSTANCE_INITIALIZER;
// static
IsolateLoadScriptData& IsolateLoadScriptData::GetData(Isolate* isolate) {
return isolate_map.Pointer()->at(isolate);
}
// static
void IsolateLoadScriptData::AddIsolate(Isolate* isolate) {
base::MutexGuard guard(isolates_mutex.Pointer());
isolate_map.Pointer()->emplace(isolate, IsolateLoadScriptData(isolate));
}
// static
void IsolateLoadScriptData::RemoveIsolate(Isolate* isolate) {
base::MutexGuard guard(isolates_mutex.Pointer());
isolate_map.Pointer()->erase(isolate);
}
// static
void IsolateLoadScriptData::EnableLog(Isolate* isolate, size_t event_id) {
{
base::MutexGuard guard(isolates_mutex.Pointer());
auto& data = GetData(isolate);
if (event_id > 0 && data.CurrentEventId() != event_id) {
// This interrupt was canceled by a newer interrupt.
return;
}
}
// This cannot be done while isolate_mutex is locked, as it can call
// EventHandler while in the call for all the existing code.
isolate->v8_file_logger()->SetEtwCodeEventHandler(kJitCodeEventDefault);
}
// static
void IsolateLoadScriptData::DisableLog(Isolate* isolate, size_t event_id) {
{
base::MutexGuard guard(isolates_mutex.Pointer());
auto& data = GetData(isolate);
if (event_id > 0 && data.CurrentEventId() != event_id) {
// This interrupt was canceled by a newer interrupt.
return;
}
data.RemoveAllLoadedScripts();
}
isolate->v8_file_logger()->ResetEtwCodeEventHandler();
}
// static
void IsolateLoadScriptData::EnableLogWithFilterData(
Isolate* isolate, size_t event_id, const std::string& etw_filter_payload) {
{
base::MutexGuard guard(isolates_mutex.Pointer());
auto& data = GetData(isolate);
if (event_id > 0 && data.CurrentEventId() != event_id) {
// This interrupt was canceled by a newer interrupt.
return;
}
}
DCHECK(!etw_filter_payload.empty());
// We should not call back into V8 from the RunFilterETWSessionByURLCallback
// callback.
DisallowJavascriptExecution no_js(isolate);
if (isolate->RunFilterETWSessionByURLCallback(etw_filter_payload)) {
isolate->v8_file_logger()->SetEtwCodeEventHandler(kJitCodeEventDefault);
}
}
// static
void IsolateLoadScriptData::EnableLogWithFilterDataOnAllIsolates(
const uint8_t* data, size_t size) {
base::MutexGuard guard(isolates_mutex.Pointer());
std::string etw_filter_payload;
etw_filter_payload.assign(data, data + size);
std::for_each(
isolate_map.Pointer()->begin(), isolate_map.Pointer()->end(),
[&etw_filter_payload](auto& pair) {
auto& isolate_data = pair.second;
isolate_data.EnqueueEnableLogWithFilterData(etw_filter_payload);
});
}
// static
void IsolateLoadScriptData::UpdateAllIsolates(bool etw_enabled) {
base::MutexGuard guard(isolates_mutex.Pointer());
std::for_each(isolate_map.Pointer()->begin(), isolate_map.Pointer()->end(),
[etw_enabled](auto& pair) {
auto& isolate_data = pair.second;
if (etw_enabled) {
isolate_data.EnqueueEnableLog();
} else {
isolate_data.EnqueueDisableLog();
}
});
}
// static
bool IsolateLoadScriptData::MaybeAddLoadedScript(Isolate* isolate,
int script_id) {
base::MutexGuard guard(isolates_mutex.Pointer());
auto& data = GetData(isolate);
if (data.IsScriptLoaded(script_id)) {
return false;
}
data.AddLoadedScript(script_id);
return true;
}
} // namespace
void MaybeSetHandlerNow(Isolate* isolate) {
if (is_etw_enabled) {
if (etw_filter_payload.Pointer()->empty()) {
IsolateLoadScriptData::EnableLog(isolate, 0);
} else {
IsolateLoadScriptData::EnableLogWithFilterData(
isolate, 0, *etw_filter_payload.Pointer());
}
}
}
// TODO(v8/11911): UnboundScript::GetLineNumber should be replaced
Tagged<SharedFunctionInfo> GetSharedFunctionInfo(const JitCodeEvent* event) {
return event->script.IsEmpty() ? Tagged<SharedFunctionInfo>()
: *Utils::OpenHandle(*event->script);
}
std::wstring GetScriptMethodNameFromEvent(const JitCodeEvent* event) {
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()));
return method_name;
}
std::wstring GetScriptMethodNameFromSharedFunctionInfo(
Tagged<SharedFunctionInfo> sfi) {
auto sfi_name = sfi->DebugNameCStr();
int method_name_length = static_cast<int>(strlen(sfi_name.get()));
std::wstring method_name(method_name_length, L'\0');
MultiByteToWideChar(CP_UTF8, 0, sfi_name.get(), method_name_length,
const_cast<LPWSTR>(method_name.data()),
static_cast<int>(method_name.length()));
return method_name;
}
std::wstring GetScriptMethodName(const JitCodeEvent* event) {
auto sfi = GetSharedFunctionInfo(event);
return sfi.is_null() ? GetScriptMethodNameFromEvent(event)
: GetScriptMethodNameFromSharedFunctionInfo(sfi);
}
void UpdateETWEnabled(bool enabled) {
DCHECK(v8_flags.enable_etw_stack_walking);
if (enabled == is_etw_enabled) {
return;
}
is_etw_enabled = enabled;
IsolateLoadScriptData::UpdateAllIsolates(enabled);
}
// This callback is invoked by Windows every time the ETW tracing status is
// changed for this application. As such, V8 needs to track its value for
// knowing if the event requires us to emit JIT runtime events.
void WINAPI ETWEnableCallback(LPCGUID /* source_id */, ULONG is_enabled,
UCHAR level, ULONGLONG match_any_keyword,
ULONGLONG match_all_keyword,
PEVENT_FILTER_DESCRIPTOR filter_data,
PVOID /* callback_context */) {
DCHECK(v8_flags.enable_etw_stack_walking);
bool is_etw_enabled_now =
is_enabled && level >= kTraceLevel &&
(match_any_keyword & kJScriptRuntimeKeyword) &&
((match_all_keyword & kJScriptRuntimeKeyword) == match_all_keyword);
FilterDataType* etw_filter = etw_filter_payload.Pointer();
if (!is_etw_enabled_now || !filter_data ||
filter_data->Type != EVENT_FILTER_TYPE_SCHEMATIZED) {
etw_filter->clear();
UpdateETWEnabled(is_etw_enabled_now);
return;
}
if (is_etw_enabled) return;
if (filter_data->Size <= sizeof(EVENT_FILTER_DESCRIPTOR)) {
return; // Invalid data
}
EVENT_FILTER_HEADER* filter_event_header =
reinterpret_cast<EVENT_FILTER_HEADER*>(filter_data->Ptr);
if (filter_event_header->Size < sizeof(EVENT_FILTER_HEADER)) {
return; // Invalid data
}
const uint8_t* payload_start =
reinterpret_cast<uint8_t*>(filter_event_header) +
sizeof(EVENT_FILTER_HEADER);
const size_t payload_size =
filter_event_header->Size - sizeof(EVENT_FILTER_HEADER);
etw_filter->assign(payload_start, payload_start + payload_size);
is_etw_enabled = is_etw_enabled_now;
IsolateLoadScriptData::EnableLogWithFilterDataOnAllIsolates(
reinterpret_cast<const uint8_t*>(etw_filter->data()), etw_filter->size());
}
void Register() {
DCHECK(!TraceLoggingProviderEnabled(g_v8Provider, 0, 0));
TraceLoggingRegisterEx(g_v8Provider, ETWEnableCallback, nullptr);
}
void Unregister() {
if (g_v8Provider) {
TraceLoggingUnregister(g_v8Provider);
}
UpdateETWEnabled(false);
}
void AddIsolate(Isolate* isolate) {
IsolateLoadScriptData::AddIsolate(isolate);
}
void RemoveIsolate(Isolate* isolate) {
IsolateLoadScriptData::RemoveIsolate(isolate);
}
void EventHandler(const JitCodeEvent* event) {
if (!is_etw_enabled) return;
if (event->code_type != v8::JitCodeEvent::CodeType::JIT_CODE) return;
if (event->type != v8::JitCodeEvent::EventType::CODE_ADDED) return;
std::wstring method_name = GetScriptMethodName(event);
// No heap allocations after this point.
DisallowGarbageCollection no_gc;
v8::Isolate* script_context = event->isolate;
Isolate* isolate = reinterpret_cast<Isolate*>(script_context);
int script_id = 0;
uint32_t script_line = -1;
uint32_t script_column = -1;
Tagged<SharedFunctionInfo> sfi = GetSharedFunctionInfo(event);
if (!sfi.is_null() && IsScript(sfi->script())) {
Tagged<Script> script = Script::cast(sfi->script());
// if the first time seeing this source file, log the SourceLoad event
script_id = script->id();
if (IsolateLoadScriptData::MaybeAddLoadedScript(isolate, script_id)) {
std::wstring wstr_name(0, L'\0');
Tagged<Object> script_name = script->GetNameOrSourceURL();
if (IsString(script_name)) {
Tagged<String> v8str_name = String::cast(script_name);
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()));
String::WriteToFlat(v8str_name, wstr_data, 0, v8str_name->length());
}
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);
}
Script::PositionInfo info;
script->GetPositionInfo(sfi->StartPosition(), &info);
script_line = info.line + 1;
script_column = info.column + 1;
}
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, script_line, script_column, method_name);
}
} // namespace ETWJITInterface
} // namespace internal
} // namespace v8