Skip to content

Commit 836df08

Browse files
committed
feat: node inspect argv parser, allow exception state to be set from command line
1 parent 059a674 commit 836df08

File tree

3 files changed

+117
-49
lines changed

3 files changed

+117
-49
lines changed

lib/internal/debugger/inspect.js

+3-46
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ const {
3232
AbortController,
3333
} = require('internal/abort_controller');
3434

35-
const { 0: InspectClient, 1: createRepl } =
35+
const { 0: InspectClient, 1: createRepl, 2: parseArguments } =
3636
[
3737
require('internal/debugger/inspect_client'),
3838
require('internal/debugger/inspect_repl'),
39+
require('internal/debugger/util/argument_parser')
3940
];
4041

4142
const debuglog = util.debuglog('inspect');
@@ -286,50 +287,6 @@ class NodeInspector {
286287
}
287288
}
288289

289-
function parseArgv(args) {
290-
const target = ArrayPrototypeShift(args);
291-
let host = '127.0.0.1';
292-
let port = 9229;
293-
let isRemote = false;
294-
let script = target;
295-
let scriptArgs = args;
296-
297-
const hostMatch = RegExpPrototypeExec(/^([^:]+):(\d+)$/, target);
298-
const portMatch = RegExpPrototypeExec(/^--port=(\d+)$/, target);
299-
300-
if (hostMatch) {
301-
// Connecting to remote debugger
302-
host = hostMatch[1];
303-
port = Number(hostMatch[2]);
304-
isRemote = true;
305-
script = null;
306-
} else if (portMatch) {
307-
// Start on custom port
308-
port = Number(portMatch[1]);
309-
script = args[0];
310-
scriptArgs = ArrayPrototypeSlice(args, 1);
311-
} else if (args.length === 1 && RegExpPrototypeExec(/^\d+$/, args[0]) !== null &&
312-
target === '-p') {
313-
// Start debugger against a given pid
314-
const pid = Number(args[0]);
315-
try {
316-
process._debugProcess(pid);
317-
} catch (e) {
318-
if (e.code === 'ESRCH') {
319-
process.stderr.write(`Target process: ${pid} doesn't exist.\n`);
320-
process.exit(kGenericUserError);
321-
}
322-
throw e;
323-
}
324-
script = null;
325-
isRemote = true;
326-
}
327-
328-
return {
329-
host, port, isRemote, script, scriptArgs,
330-
};
331-
}
332-
333290
function startInspect(argv = ArrayPrototypeSlice(process.argv, 2),
334291
stdin = process.stdin,
335292
stdout = process.stdout) {
@@ -343,7 +300,7 @@ function startInspect(argv = ArrayPrototypeSlice(process.argv, 2),
343300
process.exit(kInvalidCommandLineArgument);
344301
}
345302

346-
const options = parseArgv(argv);
303+
const options = parseArguments(argv);
347304
const inspector = new NodeInspector(options, stdin, stdout);
348305

349306
stdin.resume();

lib/internal/debugger/inspect_repl.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ function aliasProperties(target, mapping) {
361361
}
362362

363363
function createRepl(inspector) {
364-
const { Debugger, HeapProfiler, Profiler, Runtime } = inspector;
364+
const { Debugger, HeapProfiler, Profiler, Runtime, options: commandLineOptions } = inspector;
365365

366366
let repl;
367367

@@ -370,7 +370,7 @@ function createRepl(inspector) {
370370
const watchedExpressions = [];
371371
const knownBreakpoints = [];
372372
let heapSnapshotPromise = null;
373-
let pauseOnExceptionState = process.env.NODE_INSPECT_PAUSE_ON_EXCEPTION_STATE || 'none';
373+
let pauseOnExceptionState = commandLineOptions.pauseOnExceptionState || 'none';
374374
let lastCommand;
375375

376376
// Things we need to reset when the app restarts
@@ -883,7 +883,7 @@ function createRepl(inspector) {
883883
}
884884

885885
Debugger.on('paused', ({ callFrames, reason /* , hitBreakpoints */ }) => {
886-
if (process.env.NODE_INSPECT_RESUME_ON_START === '1' &&
886+
if ((process.env.NODE_INSPECT_RESUME_ON_START === '1' || commandLineOptions.inspectResumeOnStart === true) &&
887887
reason === 'Break on start') {
888888
debuglog('Paused on start, but NODE_INSPECT_RESUME_ON_START' +
889889
' environment variable is set to 1, resuming');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
const {
2+
ArrayPrototypeShift,
3+
ArrayPrototypeSplice,
4+
ArrayPrototypeIncludes,
5+
StringPrototypeSplit,
6+
RegExpPrototypeExec,
7+
} = primordials;
8+
9+
function parseBoolean(value) {
10+
return value === 'true' || value === '1' || value === 'yes';
11+
}
12+
13+
function validatePauseOnExceptionState(value) {
14+
const validStates = ['uncaught', 'none', 'all'];
15+
if (!ArrayPrototypeIncludes(validStates, value)) {
16+
throw new Error(`Invalid state passed for pauseOnExceptionState: ${value}. Must be one of 'uncaught', 'none', or 'all'.`);
17+
}
18+
return value;
19+
}
20+
21+
function parseArguments(argv) {
22+
const legacyArguments = processLegacyArgs(argv)
23+
24+
let options = {
25+
pauseOnExceptionState: undefined,
26+
inspectResumeOnStart: undefined
27+
}
28+
29+
// `NODE_INSPECT_OPTIONS` is parsed first and can be overwritten by command line arguments
30+
31+
if (process.env.NODE_INSPECT_OPTIONS) {
32+
const envOptions = StringPrototypeSplit(process.env.NODE_INSPECT_OPTIONS, ' ');
33+
for (let i = 0; i < envOptions.length; i++) {
34+
switch (envOptions[i]) {
35+
case '--pause-on-exception-state':
36+
options.pauseOnExceptionState = validatePauseOnExceptionState(envOptions[++i]);
37+
break;
38+
case '--inspect-resume-on-start':
39+
options.inspectResumeOnStart = parseBoolean(envOptions[++i]);
40+
break;
41+
}
42+
}
43+
}
44+
45+
for (let i = 0; i < argv.length;) {
46+
switch (argv[i]) {
47+
case '--pause-on-exception-state':
48+
options.pauseOnExceptionState = validatePauseOnExceptionState(argv[i+1]);
49+
ArrayPrototypeSplice(argv, i, 2);
50+
break;
51+
case '--inspect-resume-on-start':
52+
options.inspectResumeOnStart = parseBoolean(argv[i+1]);
53+
ArrayPrototypeSplice(argv, i, 2);
54+
break;
55+
default:
56+
i++;
57+
break;
58+
}
59+
}
60+
61+
return {...options, ...legacyArguments};
62+
}
63+
64+
// the legacy `node inspect` options assumed the first argument was the target
65+
// to avoid breaking existing scripts, we maintain this behavior
66+
67+
function processLegacyArgs(args) {
68+
const target = ArrayPrototypeShift(args);
69+
let host = '127.0.0.1';
70+
let port = 9229;
71+
let isRemote = false;
72+
let script = target;
73+
let scriptArgs = args;
74+
75+
const hostMatch = RegExpPrototypeExec(/^([^:]+):(\d+)$/, target);
76+
const portMatch = RegExpPrototypeExec(/^--port=(\d+)$/, target);
77+
78+
if (hostMatch) {
79+
// Connecting to remote debugger
80+
host = hostMatch[1];
81+
port = Number(hostMatch[2]);
82+
isRemote = true;
83+
script = null;
84+
} else if (portMatch) {
85+
// Start on custom port
86+
port = Number(portMatch[1]);
87+
script = args[0];
88+
scriptArgs = ArrayPrototypeSlice(args, 1);
89+
} else if (args.length === 1 && RegExpPrototypeExec(/^\d+$/, args[0]) !== null &&
90+
target === '-p') {
91+
// Start debugger against a given pid
92+
const pid = Number(args[0]);
93+
try {
94+
process._debugProcess(pid);
95+
} catch (e) {
96+
if (e.code === 'ESRCH') {
97+
process.stderr.write(`Target process: ${pid} doesn't exist.\n`);
98+
process.exit(kGenericUserError);
99+
}
100+
throw e;
101+
}
102+
script = null;
103+
isRemote = true;
104+
}
105+
106+
return {
107+
host, port, isRemote, script, scriptArgs,
108+
};
109+
}
110+
111+
module.exports = parseArguments;

0 commit comments

Comments
 (0)