mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
4 Commits
fix-audio-
...
robo/enabl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22c8b7cf17 | ||
|
|
26f169d9d1 | ||
|
|
15416ef8e4 | ||
|
|
2d4e638722 |
@@ -151,3 +151,4 @@ fix_pulseaudio_stream_and_icon_names.patch
|
||||
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
|
||||
feat_allow_enabling_extensions_on_custom_protocols.patch
|
||||
fix_initialize_com_on_desktopmedialistcapturethread_on_windows.patch
|
||||
chore_register_node_as_a_dynamic_trace_category_prefix.patch
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Tue, 31 Mar 2026 09:03:39 +0900
|
||||
Subject: chore: register node as a dynamic trace category prefix
|
||||
|
||||
This allows Node.js trace categories to be treated as dynamic Perfetto
|
||||
categories in the Chromium build. Without this, the categories
|
||||
must be registered in the static registry base/trace_event/builtin_categories.h
|
||||
which is backed by constexpr function ValidateCategories() that
|
||||
recursively validates to a depth of index + longest_category_name_length,
|
||||
adding the node categories exceeds the current constexpr recursion depth
|
||||
of 512 and requires additional patching to add `-fconstexpr-depth` to //base
|
||||
target. Given neither the static nor the dynamic registration can be
|
||||
upstreamed, the minimal of the two changes is chosen here.
|
||||
|
||||
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
|
||||
index 85c6f973788938b6a48a7a89e9fa803dc1030580..ae25a8188d57ff4c15e9a20e91629d585314db87 100644
|
||||
--- a/base/trace_event/builtin_categories.h
|
||||
+++ b/base/trace_event/builtin_categories.h
|
||||
@@ -14,6 +14,7 @@ PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES("cat",
|
||||
"foo",
|
||||
"test",
|
||||
"kTest",
|
||||
+ "node",
|
||||
"noise",
|
||||
"Testing",
|
||||
"NotTesting",
|
||||
@@ -34,48 +34,6 @@ index 7ea6daec53a497bf867d799e041bf6ae7191ef7b..15940624940d5c629c40319f45c59282
|
||||
agent_group_scheduler_compositor_task_runner =
|
||||
execution_context->GetScheduler()
|
||||
->ToFrameScheduler()
|
||||
diff --git a/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc b/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc
|
||||
index 936f5ebe28caa993ed5de0f7de3613fa338e263f..961ac8091aa82128e1cfb8800a7efcb80d100a05 100644
|
||||
--- a/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc
|
||||
+++ b/third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.cc
|
||||
@@ -13,10 +13,12 @@
|
||||
#include "third_party/blink/public/platform/task_type.h"
|
||||
#include "third_party/blink/renderer/core/dom/document.h"
|
||||
#include "third_party/blink/renderer/core/execution_context/security_context.h"
|
||||
+#include "third_party/blink/renderer/core/exported/web_view_impl.h"
|
||||
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
|
||||
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
|
||||
#include "third_party/blink/renderer/core/frame/local_frame.h"
|
||||
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
|
||||
+#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
|
||||
#include "third_party/blink/renderer/core/inspector/thread_debugger_common_impl.h"
|
||||
#include "third_party/blink/renderer/core/loader/worker_fetch_context.h"
|
||||
#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
|
||||
@@ -135,6 +137,14 @@ void ThreadedWorkletMessagingProxy::Initialize(
|
||||
DCHECK(csp);
|
||||
|
||||
LocalFrameClient* frame_client = window->GetFrame()->Client();
|
||||
+ auto worklet_settings =
|
||||
+ std::make_unique<WorkerSettings>(window->GetFrame()->GetSettings());
|
||||
+ if (auto* web_local_frame = WebLocalFrameImpl::FromFrame(window->GetFrame())) {
|
||||
+ if (auto* web_view = web_local_frame->ViewImpl()) {
|
||||
+ worklet_settings->SetNodeIntegrationInWorker(
|
||||
+ web_view->GetWebPreferences().node_integration_in_worker);
|
||||
+ }
|
||||
+ }
|
||||
auto global_scope_creation_params =
|
||||
std::make_unique<GlobalScopeCreationParams>(
|
||||
window->Url(), mojom::blink::ScriptType::kModule, global_scope_name,
|
||||
@@ -147,8 +157,7 @@ void ThreadedWorkletMessagingProxy::Initialize(
|
||||
window->GetHttpsState(), worker_clients,
|
||||
frame_client->CreateWorkerContentSettingsClient(),
|
||||
OriginTrialContext::GetInheritedTrialFeatures(window).get(),
|
||||
- base::UnguessableToken::Create(),
|
||||
- std::make_unique<WorkerSettings>(window->GetFrame()->GetSettings()),
|
||||
+ base::UnguessableToken::Create(), std::move(worklet_settings),
|
||||
mojom::blink::V8CacheOptions::kDefault, module_responses_map,
|
||||
mojo::NullRemote() /* browser_interface_broker */,
|
||||
window->GetFrame()->Loader().CreateWorkerCodeCacheHost(),
|
||||
diff --git a/third_party/blink/renderer/core/workers/worker_settings.cc b/third_party/blink/renderer/core/workers/worker_settings.cc
|
||||
index 45680c5f6ea0c7e89ccf43eb88f8a11e3318c02e..3fa3af62f4e7ba8186441c5e3184b1c04fe32d12 100644
|
||||
--- a/third_party/blink/renderer/core/workers/worker_settings.cc
|
||||
@@ -113,56 +71,3 @@ index 45c60dd2c44b05fdd279f759069383479823c7f2..33a2a0337efb9a46293e11d0d09b3fc1
|
||||
|
||||
GenericFontFamilySettings generic_font_family_settings_;
|
||||
};
|
||||
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
|
||||
index b5300dea97f20d72a807543a6da0baf61d21955f..a7030c1ba6851b26c765c7b05cd26e1453866719 100644
|
||||
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.cc
|
||||
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "third_party/blink/renderer/core/script/modulator.h"
|
||||
#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
|
||||
#include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
|
||||
+#include "third_party/blink/renderer/core/workers/worker_settings.h"
|
||||
#include "third_party/blink/renderer/core/workers/worker_thread.h"
|
||||
#include "third_party/blink/renderer/core/workers/worklet_module_responses_map.h"
|
||||
#include "third_party/blink/renderer/core/workers/worklet_module_tree_client.h"
|
||||
@@ -110,6 +111,10 @@ WorkletGlobalScope::WorkletGlobalScope(
|
||||
parent_cross_origin_isolated_capability_(
|
||||
creation_params->cross_origin_isolated_capability),
|
||||
parent_is_isolated_context_(creation_params->parent_is_isolated_context),
|
||||
+ node_integration_in_worker_(
|
||||
+ creation_params->worker_settings
|
||||
+ ? creation_params->worker_settings->NodeIntegrationInWorker()
|
||||
+ : false),
|
||||
browser_interface_broker_proxy_(this) {
|
||||
DCHECK((thread_type_ == ThreadType::kMainThread && frame_) ||
|
||||
(thread_type_ == ThreadType::kOffMainThread && worker_thread_));
|
||||
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.h b/third_party/blink/renderer/core/workers/worklet_global_scope.h
|
||||
index c7dd62900f0de48ab992a7c99058f5b6d98212cf..47ceea11ec9db6b67cef6945d165f46c868f4ca5 100644
|
||||
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.h
|
||||
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.h
|
||||
@@ -140,6 +140,13 @@ class CORE_EXPORT WorkletGlobalScope : public WorkerOrWorkletGlobalScope {
|
||||
// Returns the WorkletToken that uniquely identifies this worklet.
|
||||
virtual WorkletToken GetWorkletToken() const = 0;
|
||||
|
||||
+ // Electron: returns whether the creator frame had the
|
||||
+ // `nodeIntegrationInWorker` web preference enabled. Copied from
|
||||
+ // GlobalScopeCreationParams::worker_settings at construction time so the
|
||||
+ // value is readable on the worker thread without crossing back to the
|
||||
+ // main thread.
|
||||
+ bool NodeIntegrationInWorker() const { return node_integration_in_worker_; }
|
||||
+
|
||||
// Returns the ExecutionContextToken that uniquely identifies the parent
|
||||
// context that created this worklet. Note that this will always be a
|
||||
// LocalFrameToken.
|
||||
@@ -207,6 +214,11 @@ class CORE_EXPORT WorkletGlobalScope : public WorkerOrWorkletGlobalScope {
|
||||
// TODO(crbug.com/1206150): We need a spec for this capability.
|
||||
const bool parent_is_isolated_context_;
|
||||
|
||||
+ // Electron: snapshot of the creator frame's nodeIntegrationInWorker
|
||||
+ // WebPreference, copied out of GlobalScopeCreationParams::worker_settings
|
||||
+ // at construction time.
|
||||
+ const bool node_integration_in_worker_;
|
||||
+
|
||||
// This is the interface that handles generated code cache
|
||||
// requests both to fetch code cache when loading resources
|
||||
// and to store generated code cache to disk.
|
||||
|
||||
@@ -133,10 +133,10 @@ index 6fe4f0492dc1f3eaf576c8ff7866080a54cb81c1..41e8e052ff81df78ece87163b0499966
|
||||
// Recreate the buffer in the constructor.
|
||||
InternalFieldInfo* casted_info = static_cast<InternalFieldInfo*>(info);
|
||||
diff --git a/src/env.cc b/src/env.cc
|
||||
index b5cf58cc953590493beb52abf249e33e486ffc46..347ec5c42e098186ff489dff199ac5989961f6e3 100644
|
||||
index 57a46c8be2e052b298ed841eed6f291d62711750..e4ffaa465a4ffe21334496c52334fcb1404f67a9 100644
|
||||
--- a/src/env.cc
|
||||
+++ b/src/env.cc
|
||||
@@ -1765,10 +1765,10 @@ void AsyncHooks::Deserialize(Local<Context> context) {
|
||||
@@ -1764,10 +1764,10 @@ void AsyncHooks::Deserialize(Local<Context> context) {
|
||||
context->GetDataFromSnapshotOnce<Array>(
|
||||
info_->js_execution_async_resources).ToLocalChecked();
|
||||
} else {
|
||||
@@ -149,7 +149,7 @@ index b5cf58cc953590493beb52abf249e33e486ffc46..347ec5c42e098186ff489dff199ac598
|
||||
|
||||
// The native_execution_async_resources_ field requires v8::Local<> instances
|
||||
// for async calls whose resources were on the stack as JS objects when they
|
||||
@@ -1808,7 +1808,7 @@ AsyncHooks::SerializeInfo AsyncHooks::Serialize(Local<Context> context,
|
||||
@@ -1807,7 +1807,7 @@ AsyncHooks::SerializeInfo AsyncHooks::Serialize(Local<Context> context,
|
||||
info.async_id_fields = async_id_fields_.Serialize(context, creator);
|
||||
if (!js_execution_async_resources_.IsEmpty()) {
|
||||
info.js_execution_async_resources = creator->AddData(
|
||||
@@ -458,7 +458,7 @@ index fea0426496978c0003fe1481afcf93fc9c23edca..c9588880d05435ab9f4e23fcff74c933
|
||||
|
||||
CHECK(
|
||||
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
|
||||
index 3c234205e89be7e976dae5c3fcc73ca67953e034..e66d4fcb0c064f96cdb819c783027d864fe88d12 100644
|
||||
index 986a2d8da7fd04b5d4060d9c8d44c61a231dcce6..9f11d32c70366524cf3b7c1cfdfd24f31e438e7b 100644
|
||||
--- a/src/node_contextify.cc
|
||||
+++ b/src/node_contextify.cc
|
||||
@@ -113,7 +113,7 @@ namespace {
|
||||
@@ -479,7 +479,7 @@ index 3c234205e89be7e976dae5c3fcc73ca67953e034..e66d4fcb0c064f96cdb819c783027d86
|
||||
|
||||
PropertyAttribute attributes = PropertyAttribute::None;
|
||||
bool is_declared =
|
||||
@@ -1666,7 +1666,7 @@ static MaybeLocal<Function> CompileFunctionForCJSLoader(
|
||||
@@ -1665,7 +1665,7 @@ static MaybeLocal<Function> CompileFunctionForCJSLoader(
|
||||
bool* cache_rejected,
|
||||
bool is_cjs_scope,
|
||||
ScriptCompiler::CachedData* cached_data) {
|
||||
@@ -533,10 +533,10 @@ index 55a0c986c5b6989ee9ce277bb6a9778abb2ad2ee..809d88f21e5572807e38132d40ee7587
|
||||
READONLY_PROPERTY(target, "exitCodes", exit_codes);
|
||||
|
||||
diff --git a/src/node_file.cc b/src/node_file.cc
|
||||
index 96aac2d86695732bf6805f2ad2168a62241b5045..547455bb5011677719a8de1f98cb447561bce6aa 100644
|
||||
index c7a9648b0f83e910190dc620f4b72577ffde6c44..46cd16b535d9bd651ef733ca52ea58db7d39b09f 100644
|
||||
--- a/src/node_file.cc
|
||||
+++ b/src/node_file.cc
|
||||
@@ -3850,7 +3850,7 @@ void BindingData::Deserialize(Local<Context> context,
|
||||
@@ -3857,7 +3857,7 @@ void BindingData::Deserialize(Local<Context> context,
|
||||
int index,
|
||||
InternalFieldInfoBase* info) {
|
||||
DCHECK_IS_SNAPSHOT_SLOT(index);
|
||||
|
||||
@@ -14,10 +14,10 @@ We don't need to do this for zlib, as the existing gn workflow uses the same
|
||||
Upstreamed at https://github.com/nodejs/node/pull/55903
|
||||
|
||||
diff --git a/unofficial.gni b/unofficial.gni
|
||||
index bff7b0650cfe8578a044e45d0f9e352859909695..4ab316e45bd84e43a53335df60f847b17fe6c2fa 100644
|
||||
index a773152813376bef1fa227c331241a1d944c9317..43f09d1e68c88d3ba3b862a1a74769f73c370894 100644
|
||||
--- a/unofficial.gni
|
||||
+++ b/unofficial.gni
|
||||
@@ -199,7 +199,17 @@ template("node_gn_build") {
|
||||
@@ -203,7 +203,17 @@ template("node_gn_build") {
|
||||
configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
|
||||
configs += [ "//build/config/gcc:symbol_visibility_default" ]
|
||||
}
|
||||
@@ -36,7 +36,7 @@ index bff7b0650cfe8578a044e45d0f9e352859909695..4ab316e45bd84e43a53335df60f847b1
|
||||
if (v8_enable_i18n_support) {
|
||||
deps += [ "//third_party/icu" ]
|
||||
}
|
||||
@@ -232,6 +242,19 @@ template("node_gn_build") {
|
||||
@@ -236,6 +246,19 @@ template("node_gn_build") {
|
||||
sources += node_inspector.node_inspector_sources +
|
||||
node_inspector.node_inspector_generated_sources
|
||||
}
|
||||
|
||||
@@ -3,20 +3,14 @@ From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Wed, 17 Apr 2024 08:17:49 -0400
|
||||
Subject: build: enable perfetto
|
||||
|
||||
Enable perfetto by default in Node.js. Node.js disables perfetto by
|
||||
default but is broken on build - they don't currently add guards for
|
||||
`V8_USE_PERFETTO` and upstream only defines certain functions
|
||||
on `v8::TracingController` if perfetto is disabled. Electron already
|
||||
had minimal to no support for Node.js trace events, so the impact of
|
||||
adding associated guards there should be relatively small.
|
||||
|
||||
We should upstream this as it will eventually impact Node.js as well.
|
||||
Enable perfetto by default in Node.js and wire track events
|
||||
through legacy shim.
|
||||
|
||||
diff --git a/lib/internal/constants.js b/lib/internal/constants.js
|
||||
index 8d7204f6cb48f783adc4d1c1eb2de0c83b7fffe2..a154559a56bf383d3c26af523c9bb07b564ef600 100644
|
||||
index 8d7204f6cb48f783adc4d1c1eb2de0c83b7fffe2..8061013fcfe3c3b02aabaca0447069423ac853b2 100644
|
||||
--- a/lib/internal/constants.js
|
||||
+++ b/lib/internal/constants.js
|
||||
@@ -5,12 +5,15 @@ const isWindows = process.platform === 'win32';
|
||||
@@ -5,12 +5,16 @@ const isWindows = process.platform === 'win32';
|
||||
module.exports = {
|
||||
// Alphabet chars.
|
||||
CHAR_UPPERCASE_A: 65, /* A */
|
||||
@@ -28,61 +22,216 @@ index 8d7204f6cb48f783adc4d1c1eb2de0c83b7fffe2..a154559a56bf383d3c26af523c9bb07b
|
||||
CHAR_LOWERCASE_B: 98, /* b */
|
||||
+ CHAR_UPPERCASE_E: 69, /* E */
|
||||
CHAR_LOWERCASE_E: 101, /* e */
|
||||
+ CHAR_UPPERCASE_I: 73, /* I */
|
||||
+
|
||||
CHAR_LOWERCASE_N: 110, /* n */
|
||||
|
||||
// Non-alphabetic chars.
|
||||
diff --git a/lib/internal/http.js b/lib/internal/http.js
|
||||
index f8b4fd7c4ca5a0907806c7e804de8c951675a36a..209e3bcf8be5a23ac528dcd673bed82cbad709ca 100644
|
||||
index f8b4fd7c4ca5a0907806c7e804de8c951675a36a..0f924a5cc6718415226ffef5f8bc40a51043be04 100644
|
||||
--- a/lib/internal/http.js
|
||||
+++ b/lib/internal/http.js
|
||||
@@ -11,8 +11,8 @@ const {
|
||||
const { setUnrefTimeout } = require('internal/timers');
|
||||
const { getCategoryEnabledBuffer, trace } = internalBinding('trace_events');
|
||||
const {
|
||||
- CHAR_LOWERCASE_B,
|
||||
- CHAR_LOWERCASE_E,
|
||||
+ CHAR_UPPERCASE_B,
|
||||
+ CHAR_UPPERCASE_E,
|
||||
} = require('internal/constants');
|
||||
@@ -9,7 +9,7 @@ const {
|
||||
} = primordials;
|
||||
|
||||
const { URL } = require('internal/url');
|
||||
@@ -51,11 +51,13 @@ function isTraceHTTPEnabled() {
|
||||
const traceEventCategory = 'node,node.http';
|
||||
const { setUnrefTimeout } = require('internal/timers');
|
||||
-const { getCategoryEnabledBuffer, trace } = internalBinding('trace_events');
|
||||
+const { isTraceCategoryEnabled, trace } = internalBinding('trace_events');
|
||||
const {
|
||||
CHAR_LOWERCASE_B,
|
||||
CHAR_LOWERCASE_E,
|
||||
@@ -42,13 +42,11 @@ function getNextTraceEventId() {
|
||||
return ++traceEventId;
|
||||
}
|
||||
|
||||
-const httpEnabled = getCategoryEnabledBuffer('node.http');
|
||||
-
|
||||
function isTraceHTTPEnabled() {
|
||||
- return httpEnabled[0] > 0;
|
||||
+ return isTraceCategoryEnabled('node.http');
|
||||
}
|
||||
|
||||
-const traceEventCategory = 'node,node.http';
|
||||
+const traceEventCategory = 'node.http';
|
||||
|
||||
function traceBegin(...args) {
|
||||
- trace(CHAR_LOWERCASE_B, traceEventCategory, ...args);
|
||||
+ // See v8/src/builtins/builtins-trace.cc - must be uppercase for perfetto
|
||||
+ trace(CHAR_UPPERCASE_B, traceEventCategory, ...args);
|
||||
trace(CHAR_LOWERCASE_B, traceEventCategory, ...args);
|
||||
diff --git a/lib/internal/perf/usertiming.js b/lib/internal/perf/usertiming.js
|
||||
index 88bb63ead2d3d620dea6b54db043a404b484ead9..a37386bbf51b55b26a140cd81fbaf26d78015f21 100644
|
||||
--- a/lib/internal/perf/usertiming.js
|
||||
+++ b/lib/internal/perf/usertiming.js
|
||||
@@ -13,6 +13,12 @@ const { PerformanceEntry, kSkipThrow } = require('internal/perf/performance_entr
|
||||
const { now } = require('internal/perf/utils');
|
||||
const { enqueue, bufferUserTiming } = require('internal/perf/observe');
|
||||
const nodeTiming = require('internal/perf/nodetiming');
|
||||
+const { isTraceCategoryEnabled, trace } = internalBinding('trace_events');
|
||||
+const {
|
||||
+ CHAR_LOWERCASE_B,
|
||||
+ CHAR_LOWERCASE_E,
|
||||
+ CHAR_UPPERCASE_I
|
||||
+} = require('internal/constants');
|
||||
|
||||
const {
|
||||
validateNumber,
|
||||
@@ -41,6 +47,9 @@ const kDetail = Symbol('kDetail');
|
||||
|
||||
const markTimings = new SafeMap();
|
||||
|
||||
+const traceEventCategory = 'node.perf.usertiming';
|
||||
+let traceEventId = 0;
|
||||
+
|
||||
const nodeTimingReadOnlyAttributes = new SafeSet(new SafeArrayIterator([
|
||||
'nodeStart',
|
||||
'v8Start',
|
||||
@@ -168,6 +177,10 @@ function mark(name, options) {
|
||||
const mark = new PerformanceMark(name, options);
|
||||
enqueue(mark);
|
||||
bufferUserTiming(mark);
|
||||
+ if (isTraceCategoryEnabled(traceEventCategory)) {
|
||||
+ trace(CHAR_UPPERCASE_I, traceEventCategory, name, undefined,
|
||||
+ { startTime: mark.startTime });
|
||||
+ }
|
||||
return mark;
|
||||
}
|
||||
|
||||
function traceEnd(...args) {
|
||||
- trace(CHAR_LOWERCASE_E, traceEventCategory, ...args);
|
||||
+ // See v8/src/builtins/builtins-trace.cc - must be uppercase for perfetto
|
||||
+ trace(CHAR_UPPERCASE_E, traceEventCategory, ...args);
|
||||
@@ -233,6 +246,13 @@ function measure(name, startOrMeasureOptions, endMark) {
|
||||
const measure = createPerformanceMeasure(name, start, duration, detail);
|
||||
enqueue(measure);
|
||||
bufferUserTiming(measure);
|
||||
+ if (isTraceCategoryEnabled(traceEventCategory)) {
|
||||
+ const id = ++traceEventId;
|
||||
+ trace(CHAR_LOWERCASE_B, traceEventCategory, name, id,
|
||||
+ { startTime: start });
|
||||
+ trace(CHAR_LOWERCASE_E, traceEventCategory, name, id,
|
||||
+ { startTime: start, duration });
|
||||
+ }
|
||||
return measure;
|
||||
}
|
||||
|
||||
function ipToInt(ip) {
|
||||
diff --git a/lib/internal/trace_events_async_hooks.js b/lib/internal/trace_events_async_hooks.js
|
||||
index a9f517ffc9e4eea5bc68997ffadc85d43dde2a52..e85bcd2f500ff3f5bbd2b25922c13cb29de50993 100644
|
||||
--- a/lib/internal/trace_events_async_hooks.js
|
||||
+++ b/lib/internal/trace_events_async_hooks.js
|
||||
@@ -20,7 +20,7 @@ const {
|
||||
// the specific C++ macros.
|
||||
const kBeforeEvent = CHAR_LOWERCASE_B;
|
||||
const kEndEvent = CHAR_LOWERCASE_E;
|
||||
-const kTraceEventCategory = 'node,node.async_hooks';
|
||||
+const kTraceEventCategory = 'node.async_hooks';
|
||||
|
||||
const kEnabled = Symbol('enabled');
|
||||
|
||||
diff --git a/lib/internal/util/debuglog.js b/lib/internal/util/debuglog.js
|
||||
index 06a4f8a239855571dcc67cd81e7da7a255a9ebfd..1fa9e314ad796cdf74f718f0eb2a15530f5833d3 100644
|
||||
--- a/lib/internal/util/debuglog.js
|
||||
+++ b/lib/internal/util/debuglog.js
|
||||
@@ -20,7 +20,7 @@ const {
|
||||
CHAR_LOWERCASE_N: kTraceInstant,
|
||||
} = require('internal/constants');
|
||||
const { inspect, format, formatWithOptions } = require('internal/util/inspect');
|
||||
-const { getCategoryEnabledBuffer, trace } = internalBinding('trace_events');
|
||||
+const { isTraceCategoryEnabled, trace } = internalBinding('trace_events');
|
||||
|
||||
// `debugImpls` and `testEnabled` are deliberately not initialized so any call
|
||||
// to `debuglog()` before `initializeDebugEnv()` is called will throw.
|
||||
@@ -386,14 +386,13 @@ function debugWithTimer(set, cb) {
|
||||
}
|
||||
|
||||
const traceCategory = `node,node.${StringPrototypeToLowerCase(set)}`;
|
||||
- let traceCategoryBuffer;
|
||||
let debugLogCategoryEnabled = false;
|
||||
let timerFlags = kNone;
|
||||
|
||||
function ensureTimerFlagsAreUpdated() {
|
||||
timerFlags &= ~kSkipTrace;
|
||||
|
||||
- if (traceCategoryBuffer[0] === 0) {
|
||||
+ if (!isTraceCategoryEnabled(traceCategory)) {
|
||||
timerFlags |= kSkipTrace;
|
||||
}
|
||||
}
|
||||
@@ -467,7 +466,6 @@ function debugWithTimer(set, cb) {
|
||||
}
|
||||
emitWarningIfNeeded(set);
|
||||
debugLogCategoryEnabled = testEnabled(set);
|
||||
- traceCategoryBuffer = getCategoryEnabledBuffer(traceCategory);
|
||||
|
||||
timerFlags = kNone;
|
||||
|
||||
@@ -475,7 +473,7 @@ function debugWithTimer(set, cb) {
|
||||
timerFlags |= kSkipLog;
|
||||
}
|
||||
|
||||
- if (traceCategoryBuffer[0] === 0) {
|
||||
+ if (!isTraceCategoryEnabled(traceCategory)) {
|
||||
timerFlags |= kSkipTrace;
|
||||
}
|
||||
|
||||
diff --git a/node.gyp b/node.gyp
|
||||
index f5cd416b5fe7a51084bc4af9a4427a8e62599fd8..5eb70ce3820f2b82121bc102c5182ab768cbef36 100644
|
||||
index f5cd416b5fe7a51084bc4af9a4427a8e62599fd8..b7072ce74354495bec49357f962f4ef2999bf727 100644
|
||||
--- a/node.gyp
|
||||
+++ b/node.gyp
|
||||
@@ -182,7 +182,6 @@
|
||||
@@ -182,9 +182,9 @@
|
||||
'src/timers.cc',
|
||||
'src/timer_wrap.cc',
|
||||
'src/tracing/agent.cc',
|
||||
- 'src/tracing/node_trace_buffer.cc',
|
||||
'src/tracing/node_trace_writer.cc',
|
||||
'src/tracing/trace_event.cc',
|
||||
+ 'src/tracing/trace_categories.cc',
|
||||
'src/tracing/traced_value.cc',
|
||||
@@ -314,7 +313,6 @@
|
||||
'src/tty_wrap.cc',
|
||||
'src/udp_wrap.cc',
|
||||
@@ -314,9 +314,9 @@
|
||||
'src/tcp_wrap.h',
|
||||
'src/timers.h',
|
||||
'src/tracing/agent.h',
|
||||
- 'src/tracing/node_trace_buffer.h',
|
||||
'src/tracing/node_trace_writer.h',
|
||||
'src/tracing/trace_event.h',
|
||||
+ 'src/tracing/trace_categories.h',
|
||||
'src/tracing/trace_event_common.h',
|
||||
'src/tracing/traced_value.h',
|
||||
'src/timer_wrap.h',
|
||||
diff --git a/src/async_wrap.cc b/src/async_wrap.cc
|
||||
index 301f77c419f178c4eea258e0896327f69389dda7..d5068a18392a6128ceee7f0146f8f9c77f9924bb 100644
|
||||
--- a/src/async_wrap.cc
|
||||
+++ b/src/async_wrap.cc
|
||||
@@ -110,8 +110,7 @@ void AsyncWrap::EmitPromiseResolve(Environment* env, double async_id) {
|
||||
}
|
||||
|
||||
void AsyncWrap::EmitTraceAsyncStart() const {
|
||||
- if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
|
||||
- TRACING_CATEGORY_NODE1(async_hooks))) {
|
||||
+ if (NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE1(async_hooks))) {
|
||||
tracing::AsyncWrapArgs data(env()->execution_async_id(),
|
||||
get_trigger_async_id());
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(async_hooks),
|
||||
diff --git a/src/env.cc b/src/env.cc
|
||||
index fdabe48dd7776c59298f7d972286d0d2ed062752..c185d822b29c0b691bbf5f724f71f59638c6184d 100644
|
||||
--- a/src/env.cc
|
||||
+++ b/src/env.cc
|
||||
@@ -650,8 +650,8 @@ void TrackingTraceStateObserver::UpdateTraceCategoryState() {
|
||||
return;
|
||||
}
|
||||
|
||||
- bool async_hooks_enabled = (*(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
|
||||
- TRACING_CATEGORY_NODE1(async_hooks)))) != 0;
|
||||
+ bool async_hooks_enabled =
|
||||
+ NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE1(async_hooks));
|
||||
|
||||
Isolate* isolate = env_->isolate();
|
||||
HandleScope handle_scope(isolate);
|
||||
@@ -893,8 +893,7 @@ Environment::Environment(IsolateData* isolate_data,
|
||||
time_origin_timestamp_,
|
||||
MAYBE_FIELD_PTR(env_info, performance_state));
|
||||
|
||||
- if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
|
||||
- TRACING_CATEGORY_NODE1(environment)) != 0) {
|
||||
+ if (NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE1(environment))) {
|
||||
tracing::EnvironmentArgs traced_value(args, exec_args);
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(environment),
|
||||
"Environment",
|
||||
diff --git a/src/inspector/tracing_agent.cc b/src/inspector/tracing_agent.cc
|
||||
index 40c8aea35c931c46fc62b717c978eab0659645fd..348cdfb0b42aa18f352c220cea0b896c09f67753 100644
|
||||
--- a/src/inspector/tracing_agent.cc
|
||||
@@ -104,6 +253,156 @@ index 40c8aea35c931c46fc62b717c978eab0659645fd..348cdfb0b42aa18f352c220cea0b896c
|
||||
void Flush(bool) override {
|
||||
if (!json_writer_)
|
||||
return;
|
||||
diff --git a/src/node.cc b/src/node.cc
|
||||
index 0bc086ccd1ff449c0f3fb08a972a0c45d3178f1c..ca74e83ef6f7b0e8b8496457af3813f07f52eb37 100644
|
||||
--- a/src/node.cc
|
||||
+++ b/src/node.cc
|
||||
@@ -78,6 +78,11 @@
|
||||
|
||||
#include "large_pages/node_large_page.h"
|
||||
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+#include "perfetto/tracing/tracing.h"
|
||||
+#include "tracing/trace_categories.h"
|
||||
+#endif
|
||||
+
|
||||
#if defined(__APPLE__) || defined(__linux__) || defined(_WIN32)
|
||||
#define NODE_USE_V8_WASM_TRAP_HANDLER 1
|
||||
#else
|
||||
@@ -1261,6 +1266,14 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
|
||||
absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kIgnore);
|
||||
}
|
||||
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+ // Register Node's Perfetto TrackEvent data source so that trace
|
||||
+ // categories are available.
|
||||
+ if (perfetto::Tracing::IsInitialized()) {
|
||||
+ node::perfetto_track_event::TrackEvent::Register();
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
#if NODE_USE_V8_WASM_TRAP_HANDLER
|
||||
bool use_wasm_trap_handler =
|
||||
!per_process::cli_options->disable_wasm_trap_handler;
|
||||
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
|
||||
index 3c234205e89be7e976dae5c3fcc73ca67953e034..986a2d8da7fd04b5d4060d9c8d44c61a231dcce6 100644
|
||||
--- a/src/node_contextify.cc
|
||||
+++ b/src/node_contextify.cc
|
||||
@@ -1026,8 +1026,7 @@ void ContextifyScript::New(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
ContextifyScript* contextify_script = New(env, args.This());
|
||||
|
||||
- if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
|
||||
- TRACING_CATEGORY_NODE2(vm, script)) != 0) {
|
||||
+ if (NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE2(vm, script))) {
|
||||
Utf8Value fn(isolate, filename);
|
||||
TRACE_EVENT_BEGIN1(TRACING_CATEGORY_NODE2(vm, script),
|
||||
"ContextifyScript::New",
|
||||
diff --git a/src/node_dir.cc b/src/node_dir.cc
|
||||
index c9173d404c79a69743fc75ddb6bba0ac9579c1ef..8ffac047a69b3900f37d712334c504a1c65c83fd 100644
|
||||
--- a/src/node_dir.cc
|
||||
+++ b/src/node_dir.cc
|
||||
@@ -61,18 +61,25 @@ static const char* get_dir_func_name_by_type(uv_fs_type req_type) {
|
||||
|
||||
#define TRACE_NAME(name) "fs_dir.sync." #name
|
||||
#define GET_TRACE_ENABLED \
|
||||
- (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \
|
||||
- TRACING_CATEGORY_NODE2(fs_dir, sync)) != 0)
|
||||
+ NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE2(fs_dir, sync))
|
||||
#define FS_DIR_SYNC_TRACE_BEGIN(syscall, ...) \
|
||||
if (GET_TRACE_ENABLED) \
|
||||
TRACE_EVENT_BEGIN(TRACING_CATEGORY_NODE2(fs_dir, sync), \
|
||||
TRACE_NAME(syscall), \
|
||||
##__VA_ARGS__);
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+// Perfetto's TRACE_EVENT_END does not accept a name; it matches the prior
|
||||
+// TRACE_EVENT_BEGIN on the same thread.
|
||||
+#define FS_DIR_SYNC_TRACE_END(syscall, ...) \
|
||||
+ if (GET_TRACE_ENABLED) \
|
||||
+ TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs_dir, sync), ##__VA_ARGS__);
|
||||
+#else
|
||||
#define FS_DIR_SYNC_TRACE_END(syscall, ...) \
|
||||
if (GET_TRACE_ENABLED) \
|
||||
TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs_dir, sync), \
|
||||
TRACE_NAME(syscall), \
|
||||
##__VA_ARGS__);
|
||||
+#endif
|
||||
|
||||
#define FS_DIR_ASYNC_TRACE_BEGIN0(fs_type, id) \
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(TRACING_CATEGORY_NODE2(fs_dir, async), \
|
||||
diff --git a/src/node_file.cc b/src/node_file.cc
|
||||
index 96aac2d86695732bf6805f2ad2168a62241b5045..c7a9648b0f83e910190dc620f4b72577ffde6c44 100644
|
||||
--- a/src/node_file.cc
|
||||
+++ b/src/node_file.cc
|
||||
@@ -147,16 +147,23 @@ static const char* get_fs_func_name_by_type(uv_fs_type req_type) {
|
||||
|
||||
#define TRACE_NAME(name) "fs.sync." #name
|
||||
#define GET_TRACE_ENABLED \
|
||||
- (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \
|
||||
- TRACING_CATEGORY_NODE2(fs, sync)) != 0)
|
||||
+ NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE2(fs, sync))
|
||||
#define FS_SYNC_TRACE_BEGIN(syscall, ...) \
|
||||
if (GET_TRACE_ENABLED) \
|
||||
TRACE_EVENT_BEGIN( \
|
||||
TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), ##__VA_ARGS__);
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+// Perfetto's TRACE_EVENT_END does not accept a name; it matches the prior
|
||||
+// TRACE_EVENT_BEGIN on the same thread.
|
||||
+#define FS_SYNC_TRACE_END(syscall, ...) \
|
||||
+ if (GET_TRACE_ENABLED) \
|
||||
+ TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs, sync), ##__VA_ARGS__);
|
||||
+#else
|
||||
#define FS_SYNC_TRACE_END(syscall, ...) \
|
||||
if (GET_TRACE_ENABLED) \
|
||||
TRACE_EVENT_END( \
|
||||
TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), ##__VA_ARGS__);
|
||||
+#endif
|
||||
|
||||
#define FS_ASYNC_TRACE_BEGIN0(fs_type, id) \
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(TRACING_CATEGORY_NODE2(fs, async), \
|
||||
diff --git a/src/node_internals.h b/src/node_internals.h
|
||||
index 8e930a6fecd6589b858293d91b2454ea14ae7c73..a95dd02d4149a02ff40c759010e130c89ad1d848 100644
|
||||
--- a/src/node_internals.h
|
||||
+++ b/src/node_internals.h
|
||||
@@ -315,6 +315,14 @@ class ThreadPoolWork {
|
||||
const char* type_;
|
||||
};
|
||||
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+// Perfetto categories must be single strings (not comma-separated).
|
||||
+#define TRACING_CATEGORY_NODE "node"
|
||||
+#define TRACING_CATEGORY_NODE1(one) "node." #one
|
||||
+#define TRACING_CATEGORY_NODE2(one, two) "node." #one "." #two
|
||||
+#define NODE_TRACE_CATEGORY_ENABLED(category) \
|
||||
+ TRACE_EVENT_CATEGORY_ENABLED(category)
|
||||
+#else
|
||||
#define TRACING_CATEGORY_NODE "node"
|
||||
#define TRACING_CATEGORY_NODE1(one) \
|
||||
TRACING_CATEGORY_NODE "," \
|
||||
@@ -323,6 +331,9 @@ class ThreadPoolWork {
|
||||
TRACING_CATEGORY_NODE "," \
|
||||
TRACING_CATEGORY_NODE "." #one "," \
|
||||
TRACING_CATEGORY_NODE "." #one "." #two
|
||||
+#define NODE_TRACE_CATEGORY_ENABLED(category) \
|
||||
+ (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category) != 0)
|
||||
+#endif
|
||||
|
||||
// Functions defined in node.cc that are exposed via the bootstrapper object
|
||||
|
||||
diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc
|
||||
index 225b1465b7c97d972a38968faf6d685017a80bf0..4a53a07f4d5e79354e647ba3ff6e2e1095a5b684 100644
|
||||
--- a/src/node_trace_events.cc
|
||||
+++ b/src/node_trace_events.cc
|
||||
@@ -127,7 +127,8 @@ static void GetCategoryEnabledBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
node::Utf8Value category_name(isolate, args[0]);
|
||||
|
||||
const uint8_t* enabled_pointer =
|
||||
- TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_name.out());
|
||||
+ tracing::TraceEventHelper::GetCategoryGroupEnabled(
|
||||
+ category_name.out());
|
||||
uint8_t* enabled_pointer_cast = const_cast<uint8_t*>(enabled_pointer);
|
||||
uint8_t size = sizeof(*enabled_pointer_cast);
|
||||
|
||||
diff --git a/src/tracing/agent.cc b/src/tracing/agent.cc
|
||||
index eddcf6c3bf91b730d6ca72960e3048ceed7e7844..184e8647b2148bc597d9d3eb63f86ae99917c642 100644
|
||||
--- a/src/tracing/agent.cc
|
||||
@@ -297,48 +596,138 @@ index cd965d77b7859ff2edcf781a934594b5a9b6d251..fe1714ba77fddef693d37eeb8c7a196d
|
||||
void Flush(bool blocking) override;
|
||||
|
||||
static const int kTracesPerFile = 1 << 19;
|
||||
diff --git a/src/tracing/trace_categories.cc b/src/tracing/trace_categories.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..4abc4dc5d9ef9c2a9b2e3d85d858f4bbf5ac6432
|
||||
--- /dev/null
|
||||
+++ b/src/tracing/trace_categories.cc
|
||||
@@ -0,0 +1,5 @@
|
||||
+#include "tracing/trace_categories.h"
|
||||
+
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(node);
|
||||
+#endif
|
||||
diff --git a/src/tracing/trace_categories.h b/src/tracing/trace_categories.h
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..b28d4baa7bf766301c9281b80e0d29729ef9832e
|
||||
--- /dev/null
|
||||
+++ b/src/tracing/trace_categories.h
|
||||
@@ -0,0 +1,66 @@
|
||||
+#ifndef SRC_TRACING_TRACE_CATEGORIES_H_
|
||||
+#define SRC_TRACING_TRACE_CATEGORIES_H_
|
||||
+
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+
|
||||
+#ifdef BASE_TRACE_EVENT_BUILTIN_CATEGORIES_H_
|
||||
+// Compiling mode where Chromium's Perfetto TrackEvent
|
||||
+// is already set up (via PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(base)).
|
||||
+// Node trace categories (node.perf, node.async_hooks, etc.) will be treated
|
||||
+// as dynamic categories.
|
||||
+#else
|
||||
+// Set up Node's own Perfetto TrackEvent data source with its trace categories,
|
||||
+// following the same pattern V8 uses (PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE).
|
||||
+#define PERFETTO_ENABLE_LEGACY_TRACE_EVENTS 1
|
||||
+
|
||||
+#include "perfetto/tracing/track_event.h"
|
||||
+#include "perfetto/tracing/track_event_legacy.h"
|
||||
+
|
||||
+// Register Node.js trace categories as static Perfetto categories in the
|
||||
+// 'node' namespace.
|
||||
+PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(
|
||||
+ node,
|
||||
+ perfetto::Category("__metadata"),
|
||||
+ perfetto::Category("node"),
|
||||
+ perfetto::Category("node.perf"),
|
||||
+ perfetto::Category("node.perf.timerify"),
|
||||
+ perfetto::Category("node.perf.usertiming"),
|
||||
+ perfetto::Category("node.perf.event_loop"),
|
||||
+ perfetto::Category("node.async_hooks"),
|
||||
+ perfetto::Category("node.bootstrap"),
|
||||
+ perfetto::Category("node.dns.native"),
|
||||
+ perfetto::Category("node.environment"),
|
||||
+ perfetto::Category("node.fs.async"),
|
||||
+ perfetto::Category("node.fs.sync"),
|
||||
+ perfetto::Category("node.fs_dir.async"),
|
||||
+ perfetto::Category("node.fs_dir.sync"),
|
||||
+ perfetto::Category("node.http"),
|
||||
+ perfetto::Category("node.net.native"),
|
||||
+ perfetto::Category("node.promises.rejections"),
|
||||
+ perfetto::Category("node.realm"),
|
||||
+ perfetto::Category("node.threadpoolwork.async"),
|
||||
+ perfetto::Category("node.threadpoolwork.sync"),
|
||||
+ perfetto::Category("node.vm.script"),
|
||||
+ perfetto::Category("v8"));
|
||||
+
|
||||
+// Make Node's categories available through the default TrackEvent namespace
|
||||
+// so that TRACE_EVENT macros work without qualification.
|
||||
+PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(node);
|
||||
+
|
||||
+// These deprecated phase constants are not defined by Perfetto's legacy shim
|
||||
+// but are still exported to JavaScript by node_constants.cc.
|
||||
+#ifndef TRACE_EVENT_PHASE_ENTER_CONTEXT
|
||||
+#define TRACE_EVENT_PHASE_ENTER_CONTEXT ('(')
|
||||
+#endif
|
||||
+#ifndef TRACE_EVENT_PHASE_LEAVE_CONTEXT
|
||||
+#define TRACE_EVENT_PHASE_LEAVE_CONTEXT (')')
|
||||
+#endif
|
||||
+#ifndef TRACE_EVENT_PHASE_LINK_IDS
|
||||
+#define TRACE_EVENT_PHASE_LINK_IDS ('=')
|
||||
+#endif
|
||||
+
|
||||
+#endif // BASE_TRACE_EVENT_BUILTIN_CATEGORIES_H_
|
||||
+
|
||||
+#endif // defined(V8_USE_PERFETTO)
|
||||
+
|
||||
+#endif // SRC_TRACING_TRACE_CATEGORIES_H_
|
||||
diff --git a/src/tracing/trace_event.h b/src/tracing/trace_event.h
|
||||
index a662a081dc3bf356bf93e4063fcb043e4d8df07b..c89cdfe2b2681fbf9946200a03d7d1f7bad21226 100644
|
||||
index a662a081dc3bf356bf93e4063fcb043e4d8df07b..a7d0363e15a260feaaa5c7826a3b3137be531934 100644
|
||||
--- a/src/tracing/trace_event.h
|
||||
+++ b/src/tracing/trace_event.h
|
||||
@@ -69,8 +69,16 @@ enum CategoryGroupEnabledFlags {
|
||||
// for best performance when tracing is disabled.
|
||||
// const uint8_t*
|
||||
// TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(const char* category_group)
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
#define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED \
|
||||
node::tracing::TraceEventHelper::GetCategoryGroupEnabled
|
||||
+#else
|
||||
+#define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group) \
|
||||
+ ([](const char*) -> const uint8_t* { \
|
||||
+ static uint8_t no = 0; \
|
||||
+ return &no; \
|
||||
+ })(category_group)
|
||||
+#endif
|
||||
@@ -7,13 +7,23 @@
|
||||
|
||||
// Get the number of times traces have been recorded. This is used to implement
|
||||
// the TRACE_EVENT_IS_NEW_TRACE facility.
|
||||
@@ -114,10 +122,15 @@ enum CategoryGroupEnabledFlags {
|
||||
// const uint8_t* category_group_enabled,
|
||||
// const char* name,
|
||||
// uint64_t id)
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
#define TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION \
|
||||
if (auto controller = \
|
||||
node::tracing::TraceEventHelper::GetTracingController()) \
|
||||
controller->UpdateTraceEventDuration
|
||||
+#else
|
||||
+#define TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(category_group_enabled, name, event_handle) \
|
||||
+ (void)(category_group_enabled), (void)(name), (void)(event_handle)
|
||||
+#endif
|
||||
#include "v8-platform.h"
|
||||
#include "tracing/agent.h"
|
||||
-#include "trace_event_common.h"
|
||||
#include <atomic>
|
||||
|
||||
// Adds a metadata event to the trace log. The |AppendValueAsTraceFormat| method
|
||||
// on the convertable value will be called at flush time.
|
||||
@@ -319,12 +332,15 @@ class TraceEventHelper {
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+#include "tracing/trace_categories.h"
|
||||
+#else
|
||||
+#include "trace_event_common.h"
|
||||
+#endif
|
||||
+
|
||||
// This header file defines implementation details of how the trace macros in
|
||||
// trace_event_common.h collect and store trace events. Anything not
|
||||
// implementation-specific should go in trace_macros_common.h instead of here.
|
||||
|
||||
+#if !defined(V8_USE_PERFETTO)
|
||||
+// When Perfetto is enabled, all trace event macros and their internal
|
||||
+// implementation are provided by Perfetto's track event legacy shim
|
||||
+// (included via trace_categories.h). The following definitions are only
|
||||
+// needed for the non-Perfetto tracing backend.
|
||||
|
||||
// The pointer returned from GetCategoryGroupEnabled() points to a
|
||||
// value with zero or more of the following bits. Used in this class only.
|
||||
@@ -301,6 +311,8 @@ enum CategoryGroupEnabledFlags {
|
||||
INTERNAL_TRACE_EVENT_UID(ScopedContext) \
|
||||
INTERNAL_TRACE_EVENT_UID(scoped_context)(context);
|
||||
|
||||
+#endif // !defined(V8_USE_PERFETTO)
|
||||
+
|
||||
namespace node {
|
||||
namespace tracing {
|
||||
|
||||
@@ -319,15 +331,24 @@ class TraceEventHelper {
|
||||
static void SetAgent(Agent* agent);
|
||||
|
||||
static inline const uint8_t* GetCategoryGroupEnabled(const char* group) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+ // Under Perfetto, callers should use TRACE_EVENT_CATEGORY_ENABLED()
|
||||
+ // instead. This function exists only for backward compatibility with
|
||||
+ // non-Perfetto builds.
|
||||
+ static const uint8_t disabled = 0;
|
||||
+ return &disabled;
|
||||
+#else
|
||||
v8::TracingController* controller = GetTracingController();
|
||||
static const uint8_t disabled = 0;
|
||||
if (controller == nullptr) [[unlikely]] {
|
||||
@@ -346,56 +735,90 @@ index a662a081dc3bf356bf93e4063fcb043e4d8df07b..c89cdfe2b2681fbf9946200a03d7d1f7
|
||||
}
|
||||
return controller->GetCategoryGroupEnabled(group);
|
||||
+#endif
|
||||
+ return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -462,6 +478,7 @@ static inline uint64_t AddTraceEventImpl(
|
||||
const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args,
|
||||
const char** arg_names, const uint8_t* arg_types,
|
||||
const uint64_t* arg_values, unsigned int flags) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat> arg_convertibles[2];
|
||||
if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) {
|
||||
arg_convertibles[0].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
|
||||
@@ -478,6 +495,8 @@ static inline uint64_t AddTraceEventImpl(
|
||||
+#if !defined(V8_USE_PERFETTO)
|
||||
// TraceID encapsulates an ID that can either be an integer or pointer. Pointers
|
||||
// are by default mangled with the Process ID so that they are unlikely to
|
||||
// collide when the same pointer is used on different processes.
|
||||
@@ -478,6 +499,7 @@ static inline uint64_t AddTraceEventImpl(
|
||||
return controller->AddTraceEvent(phase, category_group_enabled, name, scope, id,
|
||||
bind_id, num_args, arg_names, arg_types,
|
||||
arg_values, arg_convertibles, flags);
|
||||
+#endif
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static V8_INLINE uint64_t AddTraceEventWithTimestampImpl(
|
||||
@@ -485,6 +504,7 @@ static V8_INLINE uint64_t AddTraceEventWithTimestampImpl(
|
||||
const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args,
|
||||
const char** arg_names, const uint8_t* arg_types,
|
||||
const uint64_t* arg_values, unsigned int flags, int64_t timestamp) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat> arg_convertibles[2];
|
||||
if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) {
|
||||
arg_convertibles[0].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
|
||||
@@ -501,12 +521,15 @@ static V8_INLINE uint64_t AddTraceEventWithTimestampImpl(
|
||||
@@ -501,6 +523,7 @@ static V8_INLINE uint64_t AddTraceEventWithTimestampImpl(
|
||||
return controller->AddTraceEventWithTimestamp(
|
||||
phase, category_group_enabled, name, scope, id, bind_id, num_args,
|
||||
arg_names, arg_types, arg_values, arg_convertibles, flags, timestamp);
|
||||
+#endif
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static V8_INLINE void AddMetadataEventImpl(
|
||||
const uint8_t* category_group_enabled, const char* name, int32_t num_args,
|
||||
const char** arg_names, const uint8_t* arg_types,
|
||||
const uint64_t* arg_values, unsigned int flags) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat> arg_convertibles[2];
|
||||
if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) {
|
||||
arg_convertibles[0].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
|
||||
@@ -522,6 +545,7 @@ static V8_INLINE void AddMetadataEventImpl(
|
||||
return agent->GetTracingController()->AddMetadataEvent(
|
||||
category_group_enabled, name, num_args, arg_names, arg_types, arg_values,
|
||||
arg_convertibles, flags);
|
||||
+#endif
|
||||
}
|
||||
@@ -716,6 +739,8 @@ class ScopedTracer {
|
||||
Data data_;
|
||||
};
|
||||
|
||||
// Define SetTraceValue for each allowed type. It stores the type and
|
||||
+#endif // !defined(V8_USE_PERFETTO)
|
||||
+
|
||||
} // namespace tracing
|
||||
} // namespace node
|
||||
|
||||
diff --git a/src/tracing/traced_value.h b/src/tracing/traced_value.h
|
||||
index 0bc9df81d87562243817a6618641a49b602654e3..b6dd8b9a9c21051f3d385d5ecea9c50c8b8b1629 100644
|
||||
--- a/src/tracing/traced_value.h
|
||||
+++ b/src/tracing/traced_value.h
|
||||
@@ -11,6 +11,26 @@
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+#include "perfetto/tracing/traced_value.h"
|
||||
+
|
||||
+namespace perfetto {
|
||||
+
|
||||
+template <>
|
||||
+struct TraceFormatTraits<
|
||||
+ std::unique_ptr<v8::ConvertableToTraceFormat>> {
|
||||
+ static void WriteIntoTrace(
|
||||
+ TracedValue context,
|
||||
+ const std::unique_ptr<v8::ConvertableToTraceFormat>& value) {
|
||||
+ std::string json;
|
||||
+ value->AppendAsTraceFormat(&json);
|
||||
+ std::move(context).WriteString(json);
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+} // namespace perfetto
|
||||
+#endif // defined(V8_USE_PERFETTO)
|
||||
+
|
||||
namespace node {
|
||||
namespace tracing {
|
||||
|
||||
diff --git a/unofficial.gni b/unofficial.gni
|
||||
index bff7b0650cfe8578a044e45d0f9e352859909695..a773152813376bef1fa227c331241a1d944c9317 100644
|
||||
--- a/unofficial.gni
|
||||
+++ b/unofficial.gni
|
||||
@@ -143,7 +143,10 @@ template("node_gn_build") {
|
||||
[ "node.gyp" ])
|
||||
|
||||
source_set("libnode") {
|
||||
- configs += [ ":node_internal_config" ]
|
||||
+ configs += [
|
||||
+ ":node_internal_config",
|
||||
+ "$node_v8_path:v8_tracing_config",
|
||||
+ ]
|
||||
public_configs = [
|
||||
":node_external_config",
|
||||
"deps/googletest:googletest_config",
|
||||
@@ -173,6 +176,7 @@ template("node_gn_build") {
|
||||
"//third_party/zstd:headers",
|
||||
"$node_simdutf_path",
|
||||
"$node_v8_path:v8_libplatform",
|
||||
+ "$node_v8_path:v8_tracing",
|
||||
]
|
||||
|
||||
cflags_cc = [
|
||||
|
||||
@@ -20,22 +20,18 @@ index ab7dc27de3e304f6d912d5834da47e3b4eb25495..b6c0fd4ceee989dac55c7d54e52fef18
|
||||
}
|
||||
}
|
||||
diff --git a/unofficial.gni b/unofficial.gni
|
||||
index 4ab316e45bd84e43a53335df60f847b17fe6c2fa..def9a302830e493e51cc2b3588816fcbd3a1bb51 100644
|
||||
index 43f09d1e68c88d3ba3b862a1a74769f73c370894..cedd2b0a0941fe66bdae479c4fc768ce3d7bc6ac 100644
|
||||
--- a/unofficial.gni
|
||||
+++ b/unofficial.gni
|
||||
@@ -143,7 +143,10 @@ template("node_gn_build") {
|
||||
[ "node.gyp" ])
|
||||
|
||||
source_set("libnode") {
|
||||
- configs += [ ":node_internal_config" ]
|
||||
+ configs += [
|
||||
+ ":node_internal_config",
|
||||
@@ -146,6 +146,7 @@ template("node_gn_build") {
|
||||
configs += [
|
||||
":node_internal_config",
|
||||
"$node_v8_path:v8_tracing_config",
|
||||
+ "//build/config/compiler:no_exit_time_destructors"
|
||||
+ ]
|
||||
]
|
||||
public_configs = [
|
||||
":node_external_config",
|
||||
"deps/googletest:googletest_config",
|
||||
@@ -364,6 +367,7 @@ template("node_gn_build") {
|
||||
@@ -368,6 +369,7 @@ template("node_gn_build") {
|
||||
"src/embedded_data.h",
|
||||
]
|
||||
include_dirs = [ "src", "tools" ]
|
||||
|
||||
@@ -18,10 +18,10 @@ Stage 3.
|
||||
Upstreamed in https://github.com/nodejs/node/pull/60364
|
||||
|
||||
diff --git a/src/node.cc b/src/node.cc
|
||||
index b9d35e60f39d1edd910cd0fc1e57157458db93f5..4421ddd05f69e32f38d074a4cc04e4e7eac89e76 100644
|
||||
index 6e7df97bfdb3bb2ff9fcbb0eba6118239018d632..2b221e84bb5e84829af8193b38eec31b57668e75 100644
|
||||
--- a/src/node.cc
|
||||
+++ b/src/node.cc
|
||||
@@ -778,7 +778,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
|
||||
@@ -783,7 +783,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
|
||||
|
||||
if (std::ranges::find(v8_args, "--no-js-source-phase-imports") ==
|
||||
v8_args.end()) {
|
||||
|
||||
@@ -12,10 +12,10 @@ This can be removed/refactored once Node.js upgrades to a version of V8
|
||||
containing the above CL.
|
||||
|
||||
diff --git a/src/node.cc b/src/node.cc
|
||||
index 0bc086ccd1ff449c0f3fb08a972a0c45d3178f1c..b9d35e60f39d1edd910cd0fc1e57157458db93f5 100644
|
||||
index ca74e83ef6f7b0e8b8496457af3813f07f52eb37..6e7df97bfdb3bb2ff9fcbb0eba6118239018d632 100644
|
||||
--- a/src/node.cc
|
||||
+++ b/src/node.cc
|
||||
@@ -1244,7 +1244,7 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
|
||||
@@ -1249,7 +1249,7 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
|
||||
result->platform_ = per_process::v8_platform.Platform();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ change, it seems to introduce an incompatibility when compiling
|
||||
using clang modules. Disabling them resolves the issue.
|
||||
|
||||
diff --git a/unofficial.gni b/unofficial.gni
|
||||
index def9a302830e493e51cc2b3588816fcbd3a1bb51..900c5e4d8a48d0725420518c923c7024518158b8 100644
|
||||
index cedd2b0a0941fe66bdae479c4fc768ce3d7bc6ac..86bd7d18ca299d0866c872b52fb0508174c148d9 100644
|
||||
--- a/unofficial.gni
|
||||
+++ b/unofficial.gni
|
||||
@@ -197,6 +197,10 @@ template("node_gn_build") {
|
||||
@@ -199,6 +199,10 @@ template("node_gn_build") {
|
||||
"CoreFoundation.framework",
|
||||
"Security.framework",
|
||||
]
|
||||
@@ -24,7 +24,7 @@ index def9a302830e493e51cc2b3588816fcbd3a1bb51..900c5e4d8a48d0725420518c923c7024
|
||||
}
|
||||
if (is_posix) {
|
||||
configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
|
||||
@@ -369,6 +373,12 @@ template("node_gn_build") {
|
||||
@@ -371,6 +375,12 @@ template("node_gn_build") {
|
||||
include_dirs = [ "src", "tools" ]
|
||||
configs += [ "//build/config/compiler:no_exit_time_destructors" ]
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ libc++ added [[nodiscard]] to std::filesystem::copy_options operator|=
|
||||
which causes build failures with -Werror.
|
||||
|
||||
diff --git a/src/node_file.cc b/src/node_file.cc
|
||||
index 547455bb5011677719a8de1f98cb447561bce6aa..385db5fd6fe5db6bb7ff17e98309b6cd605a82d3 100644
|
||||
index 46cd16b535d9bd651ef733ca52ea58db7d39b09f..7a7c71a0fcbb71e1c3dfcac7a00da207c4c3bf56 100644
|
||||
--- a/src/node_file.cc
|
||||
+++ b/src/node_file.cc
|
||||
@@ -3460,11 +3460,11 @@ static void CpSyncCopyDir(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -3467,11 +3467,11 @@ static void CpSyncCopyDir(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
auto file_copy_opts = std::filesystem::copy_options::recursive;
|
||||
if (force) {
|
||||
|
||||
@@ -89,7 +89,7 @@ index fb2af584a4ae777022c9ef8c20ada1edcbbbefdc..fe6300a5d5d2d6602a84cbd33736c213
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
diff --git a/src/env.cc b/src/env.cc
|
||||
index fdabe48dd7776c59298f7d972286d0d2ed062752..b5cf58cc953590493beb52abf249e33e486ffc46 100644
|
||||
index c185d822b29c0b691bbf5f724f71f59638c6184d..57a46c8be2e052b298ed841eed6f291d62711750 100644
|
||||
--- a/src/env.cc
|
||||
+++ b/src/env.cc
|
||||
@@ -611,7 +611,7 @@ IsolateData::~IsolateData() {}
|
||||
|
||||
@@ -19,7 +19,7 @@ index 2c95ac99be70b0750372e9c858753bf519498e3d..5ab30502fd232196739ca2b450e35cc9
|
||||
Local<Module> module = obj->module_.Get(isolate);
|
||||
if (module->GetStatus() < Module::kInstantiated) {
|
||||
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
|
||||
index e66d4fcb0c064f96cdb819c783027d864fe88d12..619980b36db457ef7e476eacd446e3bf2a9a71d2 100644
|
||||
index 9f11d32c70366524cf3b7c1cfdfd24f31e438e7b..3f1772b62aa0300540d25fb93012c49bce9d8134 100644
|
||||
--- a/src/node_contextify.cc
|
||||
+++ b/src/node_contextify.cc
|
||||
@@ -460,7 +460,7 @@ ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo<T>& args) {
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
chore_allow_customizing_microtask_policy_per_context.patch
|
||||
build_warn_instead_of_abort_on_builtin_pgo_profile_mismatch.patch
|
||||
src_use_legacy_trace_macros_in_perfetto_to_support_all_phases.patch
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Tue, 31 Mar 2026 07:05:17 +0900
|
||||
Subject: src: use legacy trace macros in perfetto to support all phases
|
||||
|
||||
Replace the phase-specific SDK macros with the INTERNAL_TRACE_EVENT_ADD
|
||||
and INTERNAL_TRACE_EVENT_ADD_WITH_ID legacy shim macros, which accept an
|
||||
arbitrary phase character and forward it through Perfetto's legacy
|
||||
compatibility layer. This supports nestable async ('b'/'e'/'n')
|
||||
and counter ('C') trace event phases needed for Node.js.
|
||||
|
||||
Additionally:
|
||||
|
||||
1. Clear TRACE_EVENT_FLAG_COPY before calling Perfetto macros. Perfetto
|
||||
manages string lifetimes internally via DynamicString and
|
||||
TRACE_STR_COPY; passing FLAG_COPY triggers a CHECK failure in
|
||||
Perfetto's legacy shim.
|
||||
|
||||
2. Default instant events (phase 'I') with GLOBAL scope to THREAD scope.
|
||||
Under Perfetto, global-scope instant events land on Track::Global(0),
|
||||
producing pid:0/tid:0 in the trace output, making them effectively
|
||||
invisible in trace viewers.
|
||||
|
||||
diff --git a/src/builtins/builtins-trace.cc b/src/builtins/builtins-trace.cc
|
||||
index c17d72d477d4c28d25e3f385d8af3c5b7024f7f7..5990e6cee1d08ba0e86059cb7f3affc878dcc632 100644
|
||||
--- a/src/builtins/builtins-trace.cc
|
||||
+++ b/src/builtins/builtins-trace.cc
|
||||
@@ -181,37 +181,44 @@ BUILTIN(Trace) {
|
||||
}
|
||||
|
||||
#if defined(V8_USE_PERFETTO)
|
||||
- // TODO(skyostil): Use interned names to reduce trace size.
|
||||
- auto trace_args = [&](perfetto::EventContext ctx) {
|
||||
+ // Perfetto handles string lifetimes internally (via DynamicString and
|
||||
+ // TRACE_STR_COPY), so TRACE_EVENT_FLAG_COPY must not be set — Perfetto's
|
||||
+ // legacy shim CHECKs against it.
|
||||
+ flags &= ~TRACE_EVENT_FLAG_COPY;
|
||||
+
|
||||
+ // Default instant events to thread scope under Perfetto. Without this,
|
||||
+ // scope bits are 0 (GLOBAL), which puts events on Track::Global(0)
|
||||
+ // resulting in pid:0/tid:0 in the trace output.
|
||||
+ if (phase == TRACE_EVENT_PHASE_INSTANT) {
|
||||
+ auto scope = flags & TRACE_EVENT_FLAG_SCOPE_MASK;
|
||||
+ if (scope == TRACE_EVENT_SCOPE_GLOBAL) {
|
||||
+ flags = (flags & ~TRACE_EVENT_FLAG_SCOPE_MASK) | TRACE_EVENT_SCOPE_THREAD;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Use the legacy trace event macros which support all phase types
|
||||
+ // (including nestable async 'b'/'e'/'n' and counter 'C' used by Node.js)
|
||||
+ if (flags & TRACE_EVENT_FLAG_HAS_ID) {
|
||||
if (num_args) {
|
||||
MaybeUtf8 arg_contents(isolate, Cast<String>(arg_json));
|
||||
- auto annotation = ctx.event()->add_debug_annotations();
|
||||
- annotation->set_name(arg_name);
|
||||
- annotation->set_legacy_json_value(*arg_contents);
|
||||
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(
|
||||
+ phase, dynamic_category, perfetto::DynamicString(*name), id, flags,
|
||||
+ arg_name, TRACE_STR_COPY(*arg_contents));
|
||||
+ } else {
|
||||
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(
|
||||
+ phase, dynamic_category, perfetto::DynamicString(*name), id, flags);
|
||||
}
|
||||
- if (flags & TRACE_EVENT_FLAG_HAS_ID) {
|
||||
- auto legacy_event = ctx.event()->set_legacy_event();
|
||||
- legacy_event->set_global_id(id);
|
||||
+ } else {
|
||||
+ if (num_args) {
|
||||
+ MaybeUtf8 arg_contents(isolate, Cast<String>(arg_json));
|
||||
+ INTERNAL_TRACE_EVENT_ADD(
|
||||
+ phase, dynamic_category, perfetto::DynamicString(*name), flags,
|
||||
+ arg_name, TRACE_STR_COPY(*arg_contents));
|
||||
+ } else {
|
||||
+ INTERNAL_TRACE_EVENT_ADD(phase, dynamic_category,
|
||||
+ perfetto::DynamicString(*name), flags);
|
||||
}
|
||||
- };
|
||||
-
|
||||
- switch (phase) {
|
||||
- case TRACE_EVENT_PHASE_BEGIN:
|
||||
- TRACE_EVENT_BEGIN(dynamic_category, perfetto::DynamicString(*name),
|
||||
- trace_args);
|
||||
- break;
|
||||
- case TRACE_EVENT_PHASE_END:
|
||||
- TRACE_EVENT_END(dynamic_category, trace_args);
|
||||
- break;
|
||||
- case TRACE_EVENT_PHASE_INSTANT:
|
||||
- TRACE_EVENT_INSTANT(dynamic_category, perfetto::DynamicString(*name),
|
||||
- trace_args);
|
||||
- break;
|
||||
- default:
|
||||
- THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
- isolate, NewTypeError(MessageTemplate::kTraceEventPhaseError));
|
||||
}
|
||||
-
|
||||
#else // !defined(V8_USE_PERFETTO)
|
||||
uint8_t arg_type;
|
||||
uint64_t arg_value;
|
||||
@@ -529,7 +529,14 @@ NodeBindings::NodeBindings(BrowserEnvironment browser_env)
|
||||
uv_loop_{InitEventLoop(browser_env, &worker_loop_)} {}
|
||||
|
||||
NodeBindings::~NodeBindings() {
|
||||
StopPolling();
|
||||
// Quit the embed thread.
|
||||
embed_closed_ = true;
|
||||
uv_sem_post(&embed_sem_);
|
||||
|
||||
WakeupEmbedThread();
|
||||
|
||||
// Wait for everything to be done.
|
||||
uv_thread_join(&embed_thread_);
|
||||
|
||||
// Clear uv.
|
||||
uv_sem_destroy(&embed_sem_);
|
||||
@@ -540,26 +547,6 @@ NodeBindings::~NodeBindings() {
|
||||
stop_and_close_uv_loop(uv_loop_);
|
||||
}
|
||||
|
||||
void NodeBindings::StopPolling() {
|
||||
if (!initialized_)
|
||||
return;
|
||||
|
||||
// Tell the embed thread to quit.
|
||||
embed_closed_ = true;
|
||||
|
||||
// The embed thread alternates between uv_sem_wait (waiting for UvRunOnce
|
||||
// to finish) and PollEvents (waiting for I/O). Wake it from both.
|
||||
uv_sem_post(&embed_sem_);
|
||||
WakeupEmbedThread();
|
||||
|
||||
// Wait for it to exit.
|
||||
uv_thread_join(&embed_thread_);
|
||||
|
||||
// Allow PrepareEmbedThread + StartPolling to restart.
|
||||
embed_closed_ = false;
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
node::IsolateData* NodeBindings::isolate_data(
|
||||
v8::Local<v8::Context> context) const {
|
||||
if (context->GetNumberOfEmbedderDataFields() <=
|
||||
@@ -946,21 +933,12 @@ void NodeBindings::PrepareEmbedThread() {
|
||||
if (initialized_)
|
||||
return;
|
||||
|
||||
// The async handle and semaphore live for the lifetime of this
|
||||
// NodeBindings instance (destroyed in ~NodeBindings), but the embed
|
||||
// thread itself may be stopped and restarted via StopPolling /
|
||||
// PrepareEmbedThread for pooled worklet contexts. Only init the
|
||||
// handles once.
|
||||
if (!embed_thread_prepared_) {
|
||||
// Add dummy handle for libuv, otherwise libuv would quit when there is
|
||||
// nothing to do.
|
||||
uv_async_init(uv_loop_, dummy_uv_handle_.get(), nullptr);
|
||||
|
||||
// Start worker that will interrupt main loop when having uv events.
|
||||
uv_sem_init(&embed_sem_, 0);
|
||||
embed_thread_prepared_ = true;
|
||||
}
|
||||
// Add dummy handle for libuv, otherwise libuv would quit when there is
|
||||
// nothing to do.
|
||||
uv_async_init(uv_loop_, dummy_uv_handle_.get(), nullptr);
|
||||
|
||||
// Start worker that will interrupt main loop when having uv events.
|
||||
uv_sem_init(&embed_sem_, 0);
|
||||
uv_thread_create(&embed_thread_, EmbedThreadRunner, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -157,12 +157,6 @@ class NodeBindings {
|
||||
// Notify embed thread to start polling after environment is loaded.
|
||||
void StartPolling();
|
||||
|
||||
// Stop the embed thread and polling without destroying handles or the loop.
|
||||
// After this call, PrepareEmbedThread + StartPolling can restart them.
|
||||
// Used by pooled worklets that need to pause the embed thread during
|
||||
// environment teardown but reuse the same NodeBindings for the next context.
|
||||
void StopPolling();
|
||||
|
||||
node::IsolateData* isolate_data(v8::Local<v8::Context> context) const;
|
||||
|
||||
// Gets/sets the environment to wrap uv loop.
|
||||
@@ -231,11 +225,6 @@ class NodeBindings {
|
||||
// Indicates whether polling thread has been created.
|
||||
bool initialized_ = false;
|
||||
|
||||
// Whether PrepareEmbedThread has initialized the semaphore and async handle.
|
||||
// Unlike |initialized_|, this is never reset — the handles live until the
|
||||
// destructor.
|
||||
bool embed_thread_prepared_ = false;
|
||||
|
||||
// Indicates whether the app code has finished loading
|
||||
// for ESM this is async after the module is loaded
|
||||
bool app_code_loaded_ = false;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" // nogncheck
|
||||
#include "third_party/blink/renderer/core/workers/worker_global_scope.h" // nogncheck
|
||||
#include "third_party/blink/renderer/core/workers/worker_settings.h" // nogncheck
|
||||
#include "third_party/blink/renderer/core/workers/worklet_global_scope.h" // nogncheck
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -207,20 +206,11 @@ bool WorkerHasNodeIntegration(blink::ExecutionContext* ec) {
|
||||
// owing to an inability to customize sandbox policies in these workers
|
||||
// given that they're run out-of-process.
|
||||
// Also avoid creating a Node.js environment for worklet global scope
|
||||
// created on the main thread — those share the page's V8 context where
|
||||
// Node is already wired up.
|
||||
// created on the main thread.
|
||||
if (ec->IsServiceWorkerGlobalScope() || ec->IsSharedWorkerGlobalScope() ||
|
||||
ec->IsMainThreadWorkletGlobalScope())
|
||||
return false;
|
||||
|
||||
// Off-main-thread worklets (AudioWorklet, PaintWorklet, AnimationWorklet,
|
||||
// SharedStorageWorklet) have their own dedicated worker thread but do not
|
||||
// derive from WorkerGlobalScope, so check for them separately and read the
|
||||
// flag from WorkletGlobalScope, which copies it out of the same
|
||||
// WorkerSettings as dedicated workers do.
|
||||
if (auto* wlgs = blink::DynamicTo<blink::WorkletGlobalScope>(ec))
|
||||
return wlgs->NodeIntegrationInWorker();
|
||||
|
||||
auto* wgs = blink::DynamicTo<blink::WorkerGlobalScope>(ec);
|
||||
if (!wgs)
|
||||
return false;
|
||||
@@ -243,9 +233,9 @@ void ElectronRendererClient::WorkerScriptReadyForEvaluationOnWorkerThread(
|
||||
return;
|
||||
|
||||
auto* current = WebWorkerObserver::GetCurrent();
|
||||
if (!current)
|
||||
current = WebWorkerObserver::Create();
|
||||
current->WorkerScriptReadyForEvaluation(context);
|
||||
if (current)
|
||||
return;
|
||||
WebWorkerObserver::Create()->WorkerScriptReadyForEvaluation(context);
|
||||
}
|
||||
|
||||
void ElectronRendererClient::WillDestroyWorkerContextOnWorkerThread(
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/threading/thread_local.h"
|
||||
#include "gin/converter.h"
|
||||
#include "shell/common/api/electron_bindings.h"
|
||||
#include "shell/common/gin_helper/event_emitter_caller.h"
|
||||
#include "shell/common/node_bindings.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/node_util.h"
|
||||
#include "third_party/blink/renderer/core/execution_context/execution_context.h" // nogncheck
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -24,23 +23,6 @@ namespace {
|
||||
static base::NoDestructor<base::ThreadLocalOwnedPointer<WebWorkerObserver>>
|
||||
lazy_tls;
|
||||
|
||||
// Returns true if `context` belongs to a worklet that runs on a thread
|
||||
// pooled by Blink's WorkletThreadHolder, where the worker thread can be
|
||||
// reused for multiple worklet contexts. For these scopes the
|
||||
// WebWorkerObserver and its NodeBindings must outlive the v8::Context so
|
||||
// the next pooled context can reuse them — Node.js cannot be re-initialized
|
||||
// on the same thread (the allocator shim only loads once). See callers of
|
||||
// blink::WorkletThreadHolder in third_party/blink for the authoritative
|
||||
// list.
|
||||
bool IsPooledWorkletContext(v8::Local<v8::Context> context) {
|
||||
auto* ec = blink::ExecutionContext::From(context);
|
||||
if (!ec)
|
||||
return false;
|
||||
return ec->IsAudioWorkletGlobalScope() || ec->IsPaintWorkletGlobalScope() ||
|
||||
ec->IsAnimationWorkletGlobalScope() ||
|
||||
ec->IsSharedStorageWorkletGlobalScope();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
@@ -66,21 +48,6 @@ WebWorkerObserver::~WebWorkerObserver() = default;
|
||||
|
||||
void WebWorkerObserver::WorkerScriptReadyForEvaluation(
|
||||
v8::Local<v8::Context> worker_context) {
|
||||
active_context_count_++;
|
||||
|
||||
if (environments_.empty()) {
|
||||
// First context on this thread - do full Node.js initialization.
|
||||
InitializeNewEnvironment(worker_context);
|
||||
} else {
|
||||
// Thread is being reused (AudioWorklet thread pooling). Share the
|
||||
// existing Node.js environment with the new context instead of
|
||||
// reinitializing, which would break existing contexts on this thread.
|
||||
ShareEnvironmentWithContext(worker_context);
|
||||
}
|
||||
}
|
||||
|
||||
void WebWorkerObserver::InitializeNewEnvironment(
|
||||
v8::Local<v8::Context> worker_context) {
|
||||
v8::Context::Scope context_scope(worker_context);
|
||||
v8::Isolate* const isolate = v8::Isolate::GetCurrent();
|
||||
v8::MicrotasksScope microtasks_scope(
|
||||
@@ -139,191 +106,26 @@ void WebWorkerObserver::InitializeNewEnvironment(
|
||||
environments_.insert(std::move(env));
|
||||
}
|
||||
|
||||
void WebWorkerObserver::ShareEnvironmentWithContext(
|
||||
v8::Local<v8::Context> worker_context) {
|
||||
v8::Context::Scope context_scope(worker_context);
|
||||
v8::Isolate* const isolate = v8::Isolate::GetCurrent();
|
||||
v8::MicrotasksScope microtasks_scope(
|
||||
worker_context, v8::MicrotasksScope::kDoNotRunMicrotasks);
|
||||
|
||||
// Get the existing environment from the first context on this thread.
|
||||
DCHECK(!environments_.empty());
|
||||
node::Environment* env = environments_.begin()->get();
|
||||
|
||||
// Initialize the V8 context for Node.js use.
|
||||
v8::Maybe<bool> initialized = node::InitializeContext(worker_context);
|
||||
CHECK(!initialized.IsNothing() && initialized.FromJust());
|
||||
|
||||
// Assign the existing Node.js environment to this new context so that
|
||||
// node::Environment::GetCurrent(context) returns the shared environment.
|
||||
env->AssignToContext(worker_context, env->principal_realm(),
|
||||
node::ContextInfo("electron_worker"));
|
||||
|
||||
// Get process and require from the original context to make Node.js
|
||||
// APIs available in the new context.
|
||||
v8::Local<v8::Context> original_context = env->context();
|
||||
v8::Local<v8::Object> original_global = original_context->Global();
|
||||
v8::Local<v8::Object> new_global = worker_context->Global();
|
||||
|
||||
v8::Local<v8::Value> process_value;
|
||||
CHECK(original_global
|
||||
->Get(original_context, gin::StringToV8(isolate, "process"))
|
||||
.ToLocal(&process_value));
|
||||
|
||||
v8::Local<v8::Value> require_value;
|
||||
CHECK(original_global
|
||||
->Get(original_context, gin::StringToV8(isolate, "require"))
|
||||
.ToLocal(&require_value));
|
||||
|
||||
// Set up 'global' as an alias for globalThis. Node.js bootstrapping normally
|
||||
// does this during LoadEnvironment, but we skip full bootstrap for shared
|
||||
// contexts.
|
||||
new_global
|
||||
->Set(worker_context, gin::StringToV8(isolate, "global"), new_global)
|
||||
.Check();
|
||||
|
||||
new_global
|
||||
->Set(worker_context, gin::StringToV8(isolate, "process"), process_value)
|
||||
.Check();
|
||||
new_global
|
||||
->Set(worker_context, gin::StringToV8(isolate, "require"), require_value)
|
||||
.Check();
|
||||
|
||||
// Copy Buffer from the original context if it exists.
|
||||
v8::Local<v8::Value> buffer_value;
|
||||
if (original_global->Get(original_context, gin::StringToV8(isolate, "Buffer"))
|
||||
.ToLocal(&buffer_value) &&
|
||||
!buffer_value->IsUndefined()) {
|
||||
new_global
|
||||
->Set(worker_context, gin::StringToV8(isolate, "Buffer"), buffer_value)
|
||||
.Check();
|
||||
}
|
||||
|
||||
// Restore the Blink implementations of web APIs that Node.js may
|
||||
// have deleted. For first-context init this is done by the node_init script
|
||||
// but we can't run that for shared contexts (it calls internalBinding).
|
||||
// Instead, copy the blink-prefixed values set during first init.
|
||||
for (const std::string_view key :
|
||||
{"fetch", "Response", "FormData", "Request", "Headers", "EventSource"}) {
|
||||
// First, check if the new context has a working Blink version.
|
||||
v8::MaybeLocal<v8::Value> blink_value =
|
||||
new_global->Get(worker_context, gin::StringToV8(isolate, key));
|
||||
if (!blink_value.IsEmpty() && !blink_value.ToLocalChecked()->IsUndefined())
|
||||
continue;
|
||||
// If not, copy from the original context.
|
||||
std::string blink_key = base::StrCat({"blink", key});
|
||||
v8::Local<v8::Value> orig_value;
|
||||
if (original_global->Get(original_context, gin::StringToV8(isolate, key))
|
||||
.ToLocal(&orig_value) &&
|
||||
!orig_value->IsUndefined()) {
|
||||
new_global->Set(worker_context, gin::StringToV8(isolate, key), orig_value)
|
||||
.Check();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebWorkerObserver::ContextWillDestroy(v8::Local<v8::Context> context) {
|
||||
node::Environment* env = node::Environment::GetCurrent(context);
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
const bool is_pooled_worklet = IsPooledWorkletContext(context);
|
||||
|
||||
active_context_count_--;
|
||||
|
||||
if (active_context_count_ == 0) {
|
||||
// Last context on this thread — full cleanup.
|
||||
{
|
||||
v8::Context::Scope context_scope(env->context());
|
||||
|
||||
// Emit the "exit" event on the process object. We avoid using
|
||||
// gin_helper::EmitEvent here because it goes through
|
||||
// CallMethodWithArgs, which creates a node::CallbackScope. During
|
||||
// worker shutdown (PrepareForShutdownOnWorkerThread), the
|
||||
// CallbackScope destructor's InternalCallbackScope::Close() tries to
|
||||
// process ticks and microtask checkpoints, which can SEGV because the
|
||||
// worker context is being torn down by Blink.
|
||||
v8::Isolate* isolate = env->isolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> ctx = env->context();
|
||||
v8::Local<v8::Value> emit_v;
|
||||
if (env->process_object()
|
||||
->Get(ctx, gin::StringToV8(isolate, "emit"))
|
||||
.ToLocal(&emit_v) &&
|
||||
emit_v->IsFunction()) {
|
||||
v8::Local<v8::Value> args[] = {gin::StringToV8(isolate, "exit")};
|
||||
v8::TryCatch try_catch(isolate);
|
||||
emit_v.As<v8::Function>()
|
||||
->Call(ctx, env->process_object(), 1, args)
|
||||
.FromMaybe(v8::Local<v8::Value>());
|
||||
// We are mid-teardown and about to destroy the worker's
|
||||
// node::Environment, so we cannot let an exception thrown by an
|
||||
// 'exit' listener propagate back into Blink (it would assert in
|
||||
// V8::FromJustIsNothing on the next call into V8). Log it and
|
||||
// explicitly reset the TryCatch so the destructor doesn't rethrow.
|
||||
if (try_catch.HasCaught()) {
|
||||
if (auto message = try_catch.Message(); !message.IsEmpty()) {
|
||||
std::string str;
|
||||
if (gin::ConvertFromV8(isolate, message->Get(), &str))
|
||||
LOG(ERROR) << "Exception thrown from worker 'exit' handler: "
|
||||
<< str;
|
||||
}
|
||||
try_catch.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent UvRunOnce from using the environment after it's destroyed.
|
||||
node_bindings_->set_uv_env(nullptr);
|
||||
|
||||
// Stop the embed thread before destroying environments. The embed
|
||||
// thread's PollEvents and FreeEnvironment's uv_run both compete for
|
||||
// completions on the same libuv event loop; on Windows (IOCP) this
|
||||
// race can deadlock. Joining the embed thread first eliminates the
|
||||
// contention so FreeEnvironment's uv_run can drain handles cleanly.
|
||||
// For pooled worklets the thread is restarted in
|
||||
// InitializeNewEnvironment via PrepareEmbedThread + StartPolling.
|
||||
node_bindings_->StopPolling();
|
||||
|
||||
// Destroying the node environment will also run the uv loop.
|
||||
{
|
||||
util::ExplicitMicrotasksScope microtasks_scope(
|
||||
context->GetMicrotaskQueue());
|
||||
environments_.clear();
|
||||
}
|
||||
|
||||
// ElectronBindings is tracking node environments.
|
||||
electron_bindings_->EnvironmentDestroyed(env);
|
||||
|
||||
// For non-pooled worker contexts (e.g., dedicated workers) Blink does
|
||||
// not reuse the worker thread, so tear down the observer completely.
|
||||
//
|
||||
// For pooled worklet contexts (AudioWorklet, PaintWorklet,
|
||||
// AnimationWorklet, SharedStorageWorklet — see
|
||||
// blink::WorkletThreadHolder) the same NodeBindings must be reused
|
||||
// for the next context on the thread because Node.js cannot be
|
||||
// re-initialized on the same thread. Keep the observer alive and let
|
||||
// the next WorkerScriptReadyForEvaluation call
|
||||
// InitializeNewEnvironment, which restarts the embed thread via
|
||||
// PrepareEmbedThread + StartPolling.
|
||||
if (!is_pooled_worklet) {
|
||||
lazy_tls->Set(nullptr); // destroys *this; do not access members below
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Other contexts still use the shared environment. Just unassign
|
||||
// this context from the environment if it's not the primary context
|
||||
// (the primary context must stay assigned because env->context()
|
||||
// references it, and UvRunOnce enters that context scope).
|
||||
if (context != env->context()) {
|
||||
env->UnassignFromContext(context);
|
||||
}
|
||||
// If the destroyed context IS the primary context, we leave the env
|
||||
// assigned to it. The env's PrincipalRealm holds a Global<Context>
|
||||
// reference that keeps the V8 context alive even though Blink has
|
||||
// torn down its side. This is safe because UvRunOnce only needs
|
||||
// the V8 context scope, not Blink-side objects.
|
||||
if (env) {
|
||||
v8::Context::Scope context_scope(env->context());
|
||||
gin_helper::EmitEvent(env->isolate(), env->process_object(), "exit");
|
||||
}
|
||||
|
||||
// Destroying the node environment will also run the uv loop.
|
||||
{
|
||||
util::ExplicitMicrotasksScope microtasks_scope(
|
||||
context->GetMicrotaskQueue());
|
||||
base::EraseIf(environments_,
|
||||
[env](auto const& item) { return item.get() == env; });
|
||||
}
|
||||
|
||||
// ElectronBindings is tracking node environments.
|
||||
electron_bindings_->EnvironmentDestroyed(env);
|
||||
|
||||
if (lazy_tls->Get())
|
||||
lazy_tls->Set(nullptr);
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -40,17 +40,9 @@ class WebWorkerObserver {
|
||||
void ContextWillDestroy(v8::Local<v8::Context> context);
|
||||
|
||||
private:
|
||||
// Full initialization for the first context on a thread.
|
||||
void InitializeNewEnvironment(v8::Local<v8::Context> context);
|
||||
// Share existing environment with a new context on a reused thread.
|
||||
void ShareEnvironmentWithContext(v8::Local<v8::Context> context);
|
||||
|
||||
std::unique_ptr<NodeBindings> node_bindings_;
|
||||
std::unique_ptr<ElectronBindings> electron_bindings_;
|
||||
base::flat_set<std::shared_ptr<node::Environment>> environments_;
|
||||
|
||||
// Number of active contexts using the environment on this thread.
|
||||
size_t active_context_count_ = 0;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -185,4 +185,112 @@ ifdescribe(!(['arm', 'arm64'].includes(process.arch)) || (process.platform !== '
|
||||
expect(parsed.traceEvents.some((x: any) => x.cat === 'disabled-by-default-v8.cpu_profiler' && x.name === 'ProfileChunk')).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('node trace categories', () => {
|
||||
it('captures performance.mark() as instant trace events', async function () {
|
||||
const { performance } = require('node:perf_hooks');
|
||||
|
||||
await contentTracing.startRecording({
|
||||
included_categories: ['node.perf.usertiming']
|
||||
});
|
||||
|
||||
performance.mark('test-trace-mark');
|
||||
|
||||
const resultPath = await contentTracing.stopRecording();
|
||||
const data = fs.readFileSync(resultPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const markEvents = parsed.traceEvents.filter(
|
||||
(x: any) => x.cat === 'node.perf.usertiming' && x.name === 'test-trace-mark'
|
||||
);
|
||||
expect(markEvents).to.have.lengthOf.at.least(1, 'should have node.perf.usertiming events for performance.mark()');
|
||||
expect(markEvents[0].ph).to.equal('I', 'performance.mark() should emit instant (I) phase events');
|
||||
});
|
||||
|
||||
it('captures performance.measure() as nestable async begin/end trace events', async function () {
|
||||
const { performance } = require('node:perf_hooks');
|
||||
|
||||
await contentTracing.startRecording({
|
||||
included_categories: ['node.perf.usertiming']
|
||||
});
|
||||
|
||||
performance.mark('trace-measure-start');
|
||||
await setTimeout(100);
|
||||
performance.mark('trace-measure-end');
|
||||
performance.measure('test-trace-measure', 'trace-measure-start', 'trace-measure-end');
|
||||
|
||||
const resultPath = await contentTracing.stopRecording();
|
||||
const data = fs.readFileSync(resultPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const measureEvents = parsed.traceEvents.filter(
|
||||
(x: any) => x.cat === 'node.perf.usertiming' && x.name === 'test-trace-measure'
|
||||
);
|
||||
expect(measureEvents.some((x: any) => x.ph === 'b')).to.be.true('should have nestable async begin (b) event');
|
||||
expect(measureEvents.some((x: any) => x.ph === 'e')).to.be.true('should have nestable async end (e) event');
|
||||
});
|
||||
|
||||
it('captures node.fs.sync trace events for file operations', async function () {
|
||||
await contentTracing.startRecording({
|
||||
included_categories: ['node.fs.sync']
|
||||
});
|
||||
|
||||
fs.readFileSync(__filename, 'utf8');
|
||||
|
||||
const resultPath = await contentTracing.stopRecording();
|
||||
const data = fs.readFileSync(resultPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const fsEvents = parsed.traceEvents.filter(
|
||||
(x: any) => typeof x.cat === 'string' && x.cat.includes('node.fs.sync')
|
||||
);
|
||||
expect(fsEvents).to.have.lengthOf.at.least(1, 'should have node.fs.sync trace events');
|
||||
});
|
||||
|
||||
it('captures multiple node categories simultaneously', async function () {
|
||||
const vm = require('node:vm');
|
||||
|
||||
await contentTracing.startRecording({
|
||||
included_categories: ['node.async_hooks', 'node.vm.script']
|
||||
});
|
||||
|
||||
vm.runInNewContext('1 + 1');
|
||||
await fs.promises.readFile(__filename, 'utf8');
|
||||
|
||||
const resultPath = await contentTracing.stopRecording();
|
||||
const data = fs.readFileSync(resultPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const asyncHooksEvents = parsed.traceEvents.filter(
|
||||
(x: any) => typeof x.cat === 'string' && x.cat.includes('node.async_hooks')
|
||||
);
|
||||
const vmEvents = parsed.traceEvents.filter(
|
||||
(x: any) => typeof x.cat === 'string' && x.cat.includes('node.vm.script')
|
||||
);
|
||||
expect(asyncHooksEvents).to.have.lengthOf.at.least(1, 'should have node.async_hooks events');
|
||||
expect(vmEvents).to.have.lengthOf.at.least(1, 'should have node.vm.script events');
|
||||
});
|
||||
|
||||
it('captures events using wildcard category pattern node.fs.*', async function () {
|
||||
await contentTracing.startRecording({
|
||||
included_categories: ['node.fs.*']
|
||||
});
|
||||
|
||||
fs.readFileSync(__filename, 'utf8');
|
||||
await fs.promises.readFile(__filename, 'utf8');
|
||||
|
||||
const resultPath = await contentTracing.stopRecording();
|
||||
const data = fs.readFileSync(resultPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const syncEvents = parsed.traceEvents.filter(
|
||||
(x: any) => typeof x.cat === 'string' && x.cat.includes('node.fs.sync')
|
||||
);
|
||||
const asyncEvents = parsed.traceEvents.filter(
|
||||
(x: any) => typeof x.cat === 'string' && x.cat.includes('node.fs.async')
|
||||
);
|
||||
expect(syncEvents).to.have.lengthOf.at.least(1, 'should have node.fs.sync events from wildcard pattern');
|
||||
expect(asyncEvents).to.have.lengthOf.at.least(1, 'should have node.fs.async events from wildcard pattern');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1625,27 +1625,6 @@ describe('chromium features', () => {
|
||||
expect(data).to.equal('function function function function function');
|
||||
});
|
||||
|
||||
it('AudioWorklet keeps node integration across pooled worker threads', async () => {
|
||||
// Regression test for https://github.com/electron/electron/issues/41263.
|
||||
// Blink pools the AudioWorklet backing thread (Chromium CL:5270028) so
|
||||
// the Nth+ AudioWorklet on a page reuses the same thread; the page
|
||||
// creates several AudioWorklet contexts in sequence and asserts node
|
||||
// integration is wired up in every one of them.
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
|
||||
w.loadURL(`file://${fixturesPath}/pages/audio-worklet.html`);
|
||||
const [, results] = await once(ipcMain, 'audio-worklet-result');
|
||||
expect(results).to.be.an('array').with.length.greaterThan(0);
|
||||
for (const r of results) expect(r).to.equal('ok');
|
||||
});
|
||||
|
||||
describe('SharedWorker', () => {
|
||||
it('can work', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
|
||||
44
spec/fixtures/pages/audio-worklet.html
vendored
44
spec/fixtures/pages/audio-worklet.html
vendored
@@ -1,44 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
// Create a number of AudioContext + AudioWorklet pairs in sequence so
|
||||
// that Blink's WorkletThreadHolder pools and reuses the underlying
|
||||
// worker thread (Chromium CL:5270028). For each context we ask the
|
||||
// worklet to report whether `require` is a function and post that back
|
||||
// via its MessagePort. The bug being guarded is that the Nth+ pooled
|
||||
// worklet would silently lose its Node.js environment, so the test
|
||||
// must run enough iterations to exercise thread reuse.
|
||||
const NUM_CONTEXTS = 6;
|
||||
|
||||
async function runOne(index) {
|
||||
const audioCtx = new AudioContext();
|
||||
try {
|
||||
await audioCtx.audioWorklet.addModule('../workers/audio_worklet_node.js');
|
||||
const node = new AudioWorkletNode(audioCtx, 'node-integration-probe');
|
||||
const reply = new Promise((resolve) => {
|
||||
node.port.onmessage = (e) => resolve(e.data);
|
||||
});
|
||||
node.port.postMessage('probe');
|
||||
node.connect(audioCtx.destination);
|
||||
return await reply;
|
||||
} finally {
|
||||
await audioCtx.close();
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const results = [];
|
||||
for (let i = 0; i < NUM_CONTEXTS; i++) {
|
||||
try {
|
||||
results.push(await runOne(i));
|
||||
} catch (err) {
|
||||
results.push(`error: ${err && err.message ? err.message : err}`);
|
||||
}
|
||||
}
|
||||
ipcRenderer.send('audio-worklet-result', results);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
28
spec/fixtures/workers/audio_worklet_node.js
vendored
28
spec/fixtures/workers/audio_worklet_node.js
vendored
@@ -1,28 +0,0 @@
|
||||
// Reports whether the Node.js environment is wired up inside this
|
||||
// AudioWorklet's global scope. Used by spec/fixtures/pages/audio-worklet.html
|
||||
// to verify that nodeIntegrationInWorker keeps working when Blink reuses a
|
||||
// pooled worker thread for multiple AudioWorklet contexts.
|
||||
class NodeIntegrationProbeProcessor extends AudioWorkletProcessor {
|
||||
constructor () {
|
||||
super();
|
||||
this.port.onmessage = () => {
|
||||
let info;
|
||||
try {
|
||||
// require should be a function and `node:timers` should resolve.
|
||||
const ok = typeof require === 'function' &&
|
||||
typeof require('node:timers').setImmediate === 'function' &&
|
||||
typeof process === 'object';
|
||||
info = ok ? 'ok' : 'missing';
|
||||
} catch (err) {
|
||||
info = `throw: ${err && err.message ? err.message : err}`;
|
||||
}
|
||||
this.port.postMessage(info);
|
||||
};
|
||||
}
|
||||
|
||||
process () {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
registerProcessor('node-integration-probe', NodeIntegrationProbeProcessor);
|
||||
Reference in New Issue
Block a user