Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/api/debugger.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ To begin watching an expression, type `watch('my_expression')`. The command
evaluates to `true`
* `clearBreakpoint('script.js', 1)`, `cb(...)`: Clear breakpoint in `script.js`
on line 1
* `clearBreakpoint('fn()')`, `cb(...)`: Clear breakpoint set on `fn`

It is also possible to set a breakpoint in a file (module) that
is not loaded yet:
Expand Down
37 changes: 22 additions & 15 deletions lib/internal/debugger/inspect_repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,8 @@ function createRepl(inspector) {
return;
}

function formatLocation(location) {
function formatBreakpoint({ location, functionName }) {
if (functionName !== undefined) return `${functionName}()`;
if (!location) return '<unknown location>';
const script = knownScripts[location.scriptId];
const scriptUrl = script ? script.url : location.scriptUrl;
Expand All @@ -741,7 +742,7 @@ function createRepl(inspector) {
const breaklist = ArrayPrototypeJoin(
ArrayPrototypeMap(
knownBreakpoints,
(bp, idx) => `#${idx} ${formatLocation(bp.location)}`),
(bp, idx) => `#${idx} ${formatBreakpoint(bp)}`),
'\n');
print(breaklist);
}
Expand Down Expand Up @@ -779,20 +780,23 @@ function createRepl(inspector) {

// setBreakpoint('fn()'): Break when a function is called
if (StringPrototypeEndsWith(script, '()')) {
const debugExpr = `debug(${script.slice(0, -2)})`;
const debugCall = selectedFrame ?
const functionName = StringPrototypeSlice(script, 0, -2);
const evaluation = selectedFrame ?
Debugger.evaluateOnCallFrame({
callFrameId: selectedFrame.callFrameId,
expression: debugExpr,
includeCommandLineAPI: true,
expression: functionName,
}) :
Runtime.evaluate({
expression: debugExpr,
includeCommandLineAPI: true,
});
return PromisePrototypeThen(debugCall, ({ result, wasThrown }) => {
Runtime.evaluate({ expression: functionName });
return PromisePrototypeThen(evaluation, ({ result, wasThrown }) => {
if (wasThrown) return convertResultToError(result);
return undefined; // This breakpoint can't be removed the same way
if (result.type !== 'function') {
throw new ERR_DEBUGGER_ERROR(`${functionName} is not a function`);
}
return PromisePrototypeThen(
Debugger.setBreakpointOnFunctionCall({ objectId: result.objectId, condition }),
({ breakpointId }) => {
ArrayPrototypePush(knownBreakpoints, { breakpointId, functionName });
});
});
}

Expand Down Expand Up @@ -854,7 +858,10 @@ function createRepl(inspector) {
}

function clearBreakpoint(url, line) {
const breakpoint = ArrayPrototypeFind(knownBreakpoints, ({ location }) => {
const breakpoint = ArrayPrototypeFind(knownBreakpoints, ({ location, functionName }) => {
if (line === undefined) {
return functionName !== undefined && `${functionName}()` === url;
}
if (!location) return false;
const script = knownScripts[location.scriptId];
if (!script) return false;
Expand All @@ -864,7 +871,7 @@ function createRepl(inspector) {
);
});
if (!breakpoint) {
print(`Could not find breakpoint at ${url}:${line}`);
print(`Could not find breakpoint at ${line === undefined ? url : `${url}:${line}`}`);
return PromiseResolve();
}
return PromisePrototypeThen(
Expand All @@ -879,7 +886,7 @@ function createRepl(inspector) {
const lastBreakpoints = ArrayPrototypeSplice(knownBreakpoints, 0);
const newBreakpoints = ArrayPrototypeMap(
ArrayPrototypeFilter(lastBreakpoints,
({ location }) => !!location.scriptUrl),
({ location }) => !!location?.scriptUrl),
({ location }) => setBreakpoint(location.scriptUrl,
location.lineNumber + 1));
if (!newBreakpoints.length) return PromiseResolve();
Expand Down
5 changes: 3 additions & 2 deletions test/parallel/test-debugger-break.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ const cli = startCLI([script]);

await cli.command('breakpoints');
assert.ok(cli.output.includes(`#0 ${script}:6`));
assert.ok(cli.output.includes(`#1 ${script}:16`));
assert.ok(cli.output.includes('#1 otherFunction()'));
assert.ok(cli.output.includes(`#2 ${script}:16`));

await cli.command('list()');
assert.match(
Expand Down Expand Up @@ -105,7 +106,7 @@ const cli = startCLI([script]);

await cli.stepCommand('');
assert.ok(
cli.output.includes(`debugCommand in ${script}:14`),
cli.output.includes(`break in ${script}:14`),
'found function breakpoint we set above');
})().finally(() => cli.quit())
.then(common.mustCall());
41 changes: 41 additions & 0 deletions test/parallel/test-debugger-function-breakpoints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict';
const common = require('../common');

common.skipIfInspectorDisabled();

const fixtures = require('../common/fixtures');
const startCLI = require('../common/debugger');

const assert = require('assert');
const path = require('path');

const scriptFullPath = fixtures.path('debugger', 'break.js');
const script = path.relative(process.cwd(), scriptFullPath);
const cli = startCLI([script]);

(async () => {
await cli.waitForInitialBreak();
await cli.waitForPrompt();

await cli.command('sb("sayHello()")');
await cli.command('sb("otherFunction()")');
await cli.command('breakpoints');
assert.ok(cli.output.includes('#0 sayHello()'));
assert.ok(cli.output.includes('#1 otherFunction()'));

await cli.command('sb("x()")');
assert.match(cli.output, /x is not a function/);

await cli.command('cb("otherFunction()")');
await cli.command('cb("missing()")');
assert.match(cli.output, /Could not find breakpoint at missing\(\)/);
await cli.command('breakpoints');
assert.ok(cli.output.includes('#0 sayHello()'));
assert.ok(!cli.output.includes('otherFunction()'));

await cli.stepCommand('cont');
assert.ok(
cli.output.includes(`break in ${script}:5`),
'pauses on the first statement of sayHello');
})().finally(() => cli.quit())
.then(common.mustCall());