diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index f4a45c99858c55..5f28ea6a0e8193 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -189,7 +189,7 @@ function lookupPublicResource(resource) { // Used by C++ to call all init() callbacks. Because some state can be setup // from C++ there's no need to perform all the same operations as in // emitInitScript. -function emitInitNative(asyncId, type, triggerAsyncId, resource) { +function emitInitNative(asyncId, type, triggerAsyncId, resource, isPromiseHook) { active_hooks.call_depth += 1; resource = lookupPublicResource(resource); // Use a single try/catch for all hooks to avoid setting up one per iteration. @@ -199,6 +199,10 @@ function emitInitNative(asyncId, type, triggerAsyncId, resource) { // eslint-disable-next-line no-var for (var i = 0; i < active_hooks.array.length; i++) { if (typeof active_hooks.array[i][init_symbol] === 'function') { + if (isPromiseHook && + active_hooks.array[i][kNoPromiseHook]) { + continue; + } active_hooks.array[i][init_symbol]( asyncId, type, triggerAsyncId, resource, @@ -222,7 +226,7 @@ function emitInitNative(asyncId, type, triggerAsyncId, resource) { // Called from native. The asyncId stack handling is taken care of there // before this is called. -function emitHook(symbol, asyncId) { +function emitHook(symbol, asyncId, isPromiseHook) { active_hooks.call_depth += 1; // Use a single try/catch for all hook to avoid setting up one per // iteration. @@ -232,6 +236,10 @@ function emitHook(symbol, asyncId) { // eslint-disable-next-line no-var for (var i = 0; i < active_hooks.array.length; i++) { if (typeof active_hooks.array[i][symbol] === 'function') { + if (isPromiseHook && + active_hooks.array[i][kNoPromiseHook]) { + continue; + } active_hooks.array[i][symbol](asyncId); } } @@ -321,7 +329,7 @@ function promiseInitHook(promise, parent) { trackPromise(promise, parent); const asyncId = promise[async_id_symbol]; const triggerAsyncId = promise[trigger_async_id_symbol]; - emitInitScript(asyncId, 'PROMISE', triggerAsyncId, promise); + emitInitScript(asyncId, 'PROMISE', triggerAsyncId, promise, true); } function promiseInitHookWithDestroyTracking(promise, parent) { @@ -339,14 +347,14 @@ function promiseBeforeHook(promise) { trackPromise(promise); const asyncId = promise[async_id_symbol]; const triggerId = promise[trigger_async_id_symbol]; - emitBeforeScript(asyncId, triggerId, promise); + emitBeforeScript(asyncId, triggerId, promise, true); } function promiseAfterHook(promise) { trackPromise(promise); const asyncId = promise[async_id_symbol]; if (hasHooks(kAfter)) { - emitAfterNative(asyncId); + emitAfterNative(asyncId, true); } if (asyncId === executionAsyncId()) { // This condition might not be true if async_hooks was enabled during @@ -361,7 +369,7 @@ function promiseAfterHook(promise) { function promiseResolveHook(promise) { trackPromise(promise); const asyncId = promise[async_id_symbol]; - emitPromiseResolveNative(asyncId); + emitPromiseResolveNative(asyncId, true); } let wantPromiseHook = false; @@ -492,7 +500,7 @@ function promiseResolveHooksExist() { } -function emitInitScript(asyncId, type, triggerAsyncId, resource) { +function emitInitScript(asyncId, type, triggerAsyncId, resource, isPromiseHook = false) { // Short circuit all checks for the common case. Which is that no hooks have // been set. Do this to remove performance impact for embedders (and core). if (!hasHooks(kInit)) @@ -502,15 +510,15 @@ function emitInitScript(asyncId, type, triggerAsyncId, resource) { triggerAsyncId = getDefaultTriggerAsyncId(); } - emitInitNative(asyncId, type, triggerAsyncId, resource); + emitInitNative(asyncId, type, triggerAsyncId, resource, isPromiseHook); } -function emitBeforeScript(asyncId, triggerAsyncId, resource) { +function emitBeforeScript(asyncId, triggerAsyncId, resource, isPromiseHook = false) { pushAsyncContext(asyncId, triggerAsyncId, resource); if (hasHooks(kBefore)) - emitBeforeNative(asyncId); + emitBeforeNative(asyncId, isPromiseHook); } diff --git a/lib/internal/net.js b/lib/internal/net.js index 4e6accb57a4297..d380d8a41982e2 100644 --- a/lib/internal/net.js +++ b/lib/internal/net.js @@ -93,8 +93,8 @@ function isLoopback(host) { return ( hostLower === 'localhost' || hostLower.startsWith('127.') || - hostLower.startsWith('[::1]') || - hostLower.startsWith('[0:0:0:0:0:0:0:1]') + hostLower === '[::1]' || + hostLower === '[0:0:0:0:0:0:0:1]' ); } diff --git a/src/node_report.cc b/src/node_report.cc index 2dcdfa8b139cf7..1b5fd64726add2 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -233,11 +233,11 @@ static void WriteNodeReport(Isolate* isolate, size_t expected_results = 0; env->ForEachWorker([&](Worker* w) { - expected_results += w->RequestInterrupt([&](Environment* env) { + expected_results += w->RequestInterrupt([&, w = w](Environment* env) { std::ostringstream os; - - GetNodeReport( - env, "Worker thread subreport", trigger, Local(), os); + std::string name = + "Worker thread subreport [" + std::string(w->name()) + "]"; + GetNodeReport(env, name.c_str(), trigger, Local(), os); Mutex::ScopedLock lock(workers_mutex); worker_infos.emplace_back(os.str()); diff --git a/src/node_worker.h b/src/node_worker.h index 9e80a764a8bd71..54e76e5f98b26b 100644 --- a/src/node_worker.h +++ b/src/node_worker.h @@ -62,6 +62,7 @@ class Worker : public AsyncWrap { bool is_stopped() const; const SnapshotData* snapshot_data() const { return snapshot_data_; } bool is_internal() const { return is_internal_; } + std::string_view name() const { return name_; } static void New(const v8::FunctionCallbackInfo& args); static void CloneParentEnvVars( diff --git a/test/parallel/test-inspector-debug-async-hook.js b/test/parallel/test-inspector-debug-async-hook.js new file mode 100644 index 00000000000000..e8f13b40c7e43a --- /dev/null +++ b/test/parallel/test-inspector-debug-async-hook.js @@ -0,0 +1,31 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +const test = require('node:test'); +const { NodeInstance } = require('../common/inspector-helper'); + +const script = ` +import { createHook } from "async_hooks" +import fs from "fs" + +const hook = createHook({ + after() { + } +}); +hook.enable(true); +console.log('Async hook enabled'); +`; + +test('inspector async hooks should not crash in debug build', async () => { + const instance = new NodeInstance([ + '--inspect-brk=0', + ], script); + const session = await instance.connectInspectorSession(); + await session.send({ method: 'NodeRuntime.enable' }); + await session.waitForNotification('NodeRuntime.waitingForDebugger'); + await session.send({ method: 'Runtime.enable' }); + await session.send({ method: 'Debugger.enable' }); + await session.send({ id: 6, method: 'Debugger.setAsyncCallStackDepth', params: { maxDepth: 32 } }); + await session.send({ method: 'Runtime.runIfWaitingForDebugger' }); + await session.waitForDisconnect(); +}); diff --git a/test/report/test-report-worker.js b/test/report/test-report-worker.js index 26b28219c84088..f17e3986e97813 100644 --- a/test/report/test-report-worker.js +++ b/test/report/test-report-worker.js @@ -13,7 +13,7 @@ async function basic() { parentPort.once('message', () => { /* Wait for message to stop the Worker */ }); - `, { eval: true }); + `, { eval: true, name: 'hello' }); await once(w, 'online'); @@ -22,7 +22,9 @@ async function basic() { assert.strictEqual(report.workers.length, 1); helper.validateContent(report.workers[0]); assert.strictEqual(report.workers[0].header.threadId, w.threadId); - + assert.strictEqual(report.workers[0].header.event, + 'Worker thread subreport [hello]', + report.workers[0].header.event); w.postMessage({}); await once(w, 'exit');