Skip to content

Commit

Permalink
feat: node inspect argv parser, allow exception state to be set from …
Browse files Browse the repository at this point in the history
…command line
  • Loading branch information
iloveitaly committed Jul 28, 2023
1 parent 059a674 commit 836df08
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 49 deletions.
49 changes: 3 additions & 46 deletions lib/internal/debugger/inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ const {
AbortController,
} = require('internal/abort_controller');

const { 0: InspectClient, 1: createRepl } =
const { 0: InspectClient, 1: createRepl, 2: parseArguments } =
[
require('internal/debugger/inspect_client'),
require('internal/debugger/inspect_repl'),
require('internal/debugger/util/argument_parser')
];

const debuglog = util.debuglog('inspect');
Expand Down Expand Up @@ -286,50 +287,6 @@ class NodeInspector {
}
}

function parseArgv(args) {
const target = ArrayPrototypeShift(args);
let host = '127.0.0.1';
let port = 9229;
let isRemote = false;
let script = target;
let scriptArgs = args;

const hostMatch = RegExpPrototypeExec(/^([^:]+):(\d+)$/, target);
const portMatch = RegExpPrototypeExec(/^--port=(\d+)$/, target);

if (hostMatch) {
// Connecting to remote debugger
host = hostMatch[1];
port = Number(hostMatch[2]);
isRemote = true;
script = null;
} else if (portMatch) {
// Start on custom port
port = Number(portMatch[1]);
script = args[0];
scriptArgs = ArrayPrototypeSlice(args, 1);
} else if (args.length === 1 && RegExpPrototypeExec(/^\d+$/, args[0]) !== null &&
target === '-p') {
// Start debugger against a given pid
const pid = Number(args[0]);
try {
process._debugProcess(pid);
} catch (e) {
if (e.code === 'ESRCH') {
process.stderr.write(`Target process: ${pid} doesn't exist.\n`);
process.exit(kGenericUserError);
}
throw e;
}
script = null;
isRemote = true;
}

return {
host, port, isRemote, script, scriptArgs,
};
}

function startInspect(argv = ArrayPrototypeSlice(process.argv, 2),
stdin = process.stdin,
stdout = process.stdout) {
Expand All @@ -343,7 +300,7 @@ function startInspect(argv = ArrayPrototypeSlice(process.argv, 2),
process.exit(kInvalidCommandLineArgument);
}

const options = parseArgv(argv);
const options = parseArguments(argv);
const inspector = new NodeInspector(options, stdin, stdout);

stdin.resume();
Expand Down
6 changes: 3 additions & 3 deletions lib/internal/debugger/inspect_repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ function aliasProperties(target, mapping) {
}

function createRepl(inspector) {
const { Debugger, HeapProfiler, Profiler, Runtime } = inspector;
const { Debugger, HeapProfiler, Profiler, Runtime, options: commandLineOptions } = inspector;

let repl;

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

// Things we need to reset when the app restarts
Expand Down Expand Up @@ -883,7 +883,7 @@ function createRepl(inspector) {
}

Debugger.on('paused', ({ callFrames, reason /* , hitBreakpoints */ }) => {
if (process.env.NODE_INSPECT_RESUME_ON_START === '1' &&
if ((process.env.NODE_INSPECT_RESUME_ON_START === '1' || commandLineOptions.inspectResumeOnStart === true) &&
reason === 'Break on start') {
debuglog('Paused on start, but NODE_INSPECT_RESUME_ON_START' +
' environment variable is set to 1, resuming');
Expand Down
111 changes: 111 additions & 0 deletions lib/internal/debugger/util/argument_parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
const {
ArrayPrototypeShift,
ArrayPrototypeSplice,
ArrayPrototypeIncludes,
StringPrototypeSplit,
RegExpPrototypeExec,
} = primordials;

function parseBoolean(value) {
return value === 'true' || value === '1' || value === 'yes';
}

function validatePauseOnExceptionState(value) {
const validStates = ['uncaught', 'none', 'all'];
if (!ArrayPrototypeIncludes(validStates, value)) {
throw new Error(`Invalid state passed for pauseOnExceptionState: ${value}. Must be one of 'uncaught', 'none', or 'all'.`);
}
return value;
}

function parseArguments(argv) {
const legacyArguments = processLegacyArgs(argv)

let options = {
pauseOnExceptionState: undefined,
inspectResumeOnStart: undefined
}

// `NODE_INSPECT_OPTIONS` is parsed first and can be overwritten by command line arguments

if (process.env.NODE_INSPECT_OPTIONS) {
const envOptions = StringPrototypeSplit(process.env.NODE_INSPECT_OPTIONS, ' ');
for (let i = 0; i < envOptions.length; i++) {
switch (envOptions[i]) {
case '--pause-on-exception-state':
options.pauseOnExceptionState = validatePauseOnExceptionState(envOptions[++i]);
break;
case '--inspect-resume-on-start':
options.inspectResumeOnStart = parseBoolean(envOptions[++i]);
break;
}
}
}

for (let i = 0; i < argv.length;) {
switch (argv[i]) {
case '--pause-on-exception-state':
options.pauseOnExceptionState = validatePauseOnExceptionState(argv[i+1]);
ArrayPrototypeSplice(argv, i, 2);
break;
case '--inspect-resume-on-start':
options.inspectResumeOnStart = parseBoolean(argv[i+1]);
ArrayPrototypeSplice(argv, i, 2);
break;
default:
i++;
break;
}
}

return {...options, ...legacyArguments};
}

// the legacy `node inspect` options assumed the first argument was the target
// to avoid breaking existing scripts, we maintain this behavior

function processLegacyArgs(args) {
const target = ArrayPrototypeShift(args);
let host = '127.0.0.1';
let port = 9229;
let isRemote = false;
let script = target;
let scriptArgs = args;

const hostMatch = RegExpPrototypeExec(/^([^:]+):(\d+)$/, target);
const portMatch = RegExpPrototypeExec(/^--port=(\d+)$/, target);

if (hostMatch) {
// Connecting to remote debugger
host = hostMatch[1];
port = Number(hostMatch[2]);
isRemote = true;
script = null;
} else if (portMatch) {
// Start on custom port
port = Number(portMatch[1]);
script = args[0];
scriptArgs = ArrayPrototypeSlice(args, 1);
} else if (args.length === 1 && RegExpPrototypeExec(/^\d+$/, args[0]) !== null &&
target === '-p') {
// Start debugger against a given pid
const pid = Number(args[0]);
try {
process._debugProcess(pid);
} catch (e) {
if (e.code === 'ESRCH') {
process.stderr.write(`Target process: ${pid} doesn't exist.\n`);
process.exit(kGenericUserError);
}
throw e;
}
script = null;
isRemote = true;
}

return {
host, port, isRemote, script, scriptArgs,
};
}

module.exports = parseArguments;

0 comments on commit 836df08

Please sign in to comment.