0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00
nodejs/test/sequential/test-inspector-async-hook-setup-at-signal.js

83 lines
3.0 KiB
JavaScript
Raw Normal View History

// Flags: --expose-internals
inspector: enable async stack traces Implement a special async_hooks listener that forwards information about async tasks to V8Inspector asyncTask* API, thus enabling DevTools feature "async stack traces". The feature is enabled only on 64bit platforms due to a technical limitation of V8 Inspector: inspector uses a pointer as a task id, while async_hooks use 64bit numbers as ids. To avoid performance penalty of async_hooks when not debugging, the new listener is enabled only when the process enters a debug mode: - When the process is started with `--inspect` or `--inspect-brk`, the listener is enabled immediately and async stack traces lead all the way to the first tick of the event loop. - When the debug mode is enabled via SIGUSR1 or `_debugProcess()`, the listener is enabled together with the debugger. As a result, only async operations started after the signal was received will be correctly observed and reported to V8 Inspector. For example, a `setInterval()` called in the first tick of the event will not be shown in the async stack trace when the callback is invoked. This behaviour is consistent with Chrome DevTools. Last but not least, this commit fixes handling of InspectorAgent's internal property `enabled_` to ensure it's set back to `false` after the debugger is deactivated (typically via `process._debugEnd()`). Fixes: https://github.com/nodejs/node/issues/11370 PR-URL: https://github.com/nodejs/node/pull/13870 Reviewed-by: Timothy Gu <timothygu99@gmail.com> Reviewed-by: Anna Henningsen <anna@addaleax.net>
2017-07-17 16:51:26 +02:00
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
common.skipIf32Bits();
common.crashOnUnhandledRejection();
const { NodeInstance } = require('../common/inspector-helper.js');
inspector: enable async stack traces Implement a special async_hooks listener that forwards information about async tasks to V8Inspector asyncTask* API, thus enabling DevTools feature "async stack traces". The feature is enabled only on 64bit platforms due to a technical limitation of V8 Inspector: inspector uses a pointer as a task id, while async_hooks use 64bit numbers as ids. To avoid performance penalty of async_hooks when not debugging, the new listener is enabled only when the process enters a debug mode: - When the process is started with `--inspect` or `--inspect-brk`, the listener is enabled immediately and async stack traces lead all the way to the first tick of the event loop. - When the debug mode is enabled via SIGUSR1 or `_debugProcess()`, the listener is enabled together with the debugger. As a result, only async operations started after the signal was received will be correctly observed and reported to V8 Inspector. For example, a `setInterval()` called in the first tick of the event will not be shown in the async stack trace when the callback is invoked. This behaviour is consistent with Chrome DevTools. Last but not least, this commit fixes handling of InspectorAgent's internal property `enabled_` to ensure it's set back to `false` after the debugger is deactivated (typically via `process._debugEnd()`). Fixes: https://github.com/nodejs/node/issues/11370 PR-URL: https://github.com/nodejs/node/pull/13870 Reviewed-by: Timothy Gu <timothygu99@gmail.com> Reviewed-by: Anna Henningsen <anna@addaleax.net>
2017-07-17 16:51:26 +02:00
const assert = require('assert');
const script = `
process._rawDebug('Waiting until a signal enables the inspector...');
let waiting = setInterval(waitUntilDebugged, 50);
function waitUntilDebugged() {
if (!process.binding('inspector').isEnabled()) return;
clearInterval(waiting);
// At this point, even though the Inspector is enabled, the default async
// call stack depth is 0. We need a chance to call
// Debugger.setAsyncCallStackDepth *before* activating the actual timer for
// async stack traces to work. Directly using a debugger statement would be
// too brittle, and using a longer timeout would unnecessarily slow down the
inspector: enable async stack traces Implement a special async_hooks listener that forwards information about async tasks to V8Inspector asyncTask* API, thus enabling DevTools feature "async stack traces". The feature is enabled only on 64bit platforms due to a technical limitation of V8 Inspector: inspector uses a pointer as a task id, while async_hooks use 64bit numbers as ids. To avoid performance penalty of async_hooks when not debugging, the new listener is enabled only when the process enters a debug mode: - When the process is started with `--inspect` or `--inspect-brk`, the listener is enabled immediately and async stack traces lead all the way to the first tick of the event loop. - When the debug mode is enabled via SIGUSR1 or `_debugProcess()`, the listener is enabled together with the debugger. As a result, only async operations started after the signal was received will be correctly observed and reported to V8 Inspector. For example, a `setInterval()` called in the first tick of the event will not be shown in the async stack trace when the callback is invoked. This behaviour is consistent with Chrome DevTools. Last but not least, this commit fixes handling of InspectorAgent's internal property `enabled_` to ensure it's set back to `false` after the debugger is deactivated (typically via `process._debugEnd()`). Fixes: https://github.com/nodejs/node/issues/11370 PR-URL: https://github.com/nodejs/node/pull/13870 Reviewed-by: Timothy Gu <timothygu99@gmail.com> Reviewed-by: Anna Henningsen <anna@addaleax.net>
2017-07-17 16:51:26 +02:00
// test on most machines. Triggering a debugger break through an interval is
// a faster and more reliable way.
process._rawDebug('Signal received, waiting for debugger setup');
waiting = setInterval(() => { debugger; }, 50);
}
// This function is called by the inspector client (session)
function setupTimeoutWithBreak() {
clearInterval(waiting);
process._rawDebug('Debugger ready, setting up timeout with a break');
setTimeout(() => { debugger; }, 50);
}
`;
async function waitForInitialSetup(session) {
console.error('[test]', 'Waiting for initial setup');
await session.waitForBreakOnLine(15, '[eval]');
}
async function setupTimeoutForStackTrace(session) {
console.error('[test]', 'Setting up timeout for async stack trace');
await session.send([
{ 'method': 'Runtime.evaluate',
'params': { expression: 'setupTimeoutWithBreak()' } },
{ 'method': 'Debugger.resume' }
]);
}
async function checkAsyncStackTrace(session) {
console.error('[test]', 'Verify basic properties of asyncStackTrace');
const paused = await session.waitForBreakOnLine(22, '[eval]');
assert(paused.params.asyncStackTrace,
`${Object.keys(paused.params)} contains "asyncStackTrace" property`);
assert(paused.params.asyncStackTrace.description, 'Timeout');
assert(paused.params.asyncStackTrace.callFrames
.some((frame) => frame.functionName === 'setupTimeoutWithBreak'));
}
async function runTests() {
const instance = await NodeInstance.startViaSignal(script);
const session = await instance.connectInspectorSession();
await session.send([
{ 'method': 'Runtime.enable' },
{ 'method': 'Debugger.enable' },
{ 'method': 'Debugger.setAsyncCallStackDepth',
'params': { 'maxDepth': 10 } },
{ 'method': 'Debugger.setBlackboxPatterns',
'params': { 'patterns': [] } },
{ 'method': 'Runtime.runIfWaitingForDebugger' }
]);
await waitForInitialSetup(session);
await setupTimeoutForStackTrace(session);
await checkAsyncStackTrace(session);
console.error('[test]', 'Stopping child instance');
session.disconnect();
instance.kill();
}
runTests();