Skip to content
This repository was archived by the owner on Feb 1, 2022. It is now read-only.

Commit b64abe8

Browse files
author
Jan Krems
committed
feat: Support for debugging a pid
1 parent 759f187 commit b64abe8

File tree

2 files changed

+121
-53
lines changed

2 files changed

+121
-53
lines changed

lib/_inspect.js

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -53,53 +53,6 @@ function getDefaultPort() {
5353
return 9229;
5454
}
5555

56-
function runScript(script, scriptArgs, inspectPort, childPrint) {
57-
return new Promise((resolve) => {
58-
const args = [
59-
'--inspect',
60-
`--debug-brk=${inspectPort}`,
61-
].concat([script], scriptArgs);
62-
const child = spawn(process.execPath, args);
63-
child.stdout.setEncoding('utf8');
64-
child.stderr.setEncoding('utf8');
65-
child.stdout.on('data', childPrint);
66-
child.stderr.on('data', childPrint);
67-
68-
let output = '';
69-
function waitForListenHint(text) {
70-
output += text;
71-
if (/chrome-devtools:\/\//.test(output)) {
72-
child.stderr.removeListener('data', waitForListenHint);
73-
resolve(child);
74-
}
75-
}
76-
77-
child.stderr.on('data', waitForListenHint);
78-
});
79-
}
80-
81-
function createAgentProxy(domain, client) {
82-
const agent = new EventEmitter();
83-
agent.then = (...args) => {
84-
// TODO: potentially fetch the protocol and pretty-print it here.
85-
const descriptor = {
86-
[util.inspect.custom](depth, { stylize }) {
87-
return stylize(`[Agent ${domain}]`, 'special');
88-
},
89-
};
90-
return Promise.resolve(descriptor).then(...args);
91-
};
92-
93-
return new Proxy(agent, {
94-
get(target, name) {
95-
if (name in target) return target[name];
96-
return function callVirtualMethod(params) {
97-
return client.callMethod(`${domain}.${name}`, params);
98-
};
99-
},
100-
});
101-
}
102-
10356
function portIsFree(host, port, timeout = 2000) {
10457
const retryDelay = 150;
10558
let didTimeOut = false;
@@ -139,6 +92,56 @@ function portIsFree(host, port, timeout = 2000) {
13992
});
14093
}
14194

95+
function runScript(script, scriptArgs, inspectHost, inspectPort, childPrint) {
96+
return portIsFree(inspectHost, inspectPort)
97+
.then(() => {
98+
return new Promise((resolve) => {
99+
const args = [
100+
'--inspect',
101+
`--debug-brk=${inspectPort}`,
102+
].concat([script], scriptArgs);
103+
const child = spawn(process.execPath, args);
104+
child.stdout.setEncoding('utf8');
105+
child.stderr.setEncoding('utf8');
106+
child.stdout.on('data', childPrint);
107+
child.stderr.on('data', childPrint);
108+
109+
let output = '';
110+
function waitForListenHint(text) {
111+
output += text;
112+
if (/chrome-devtools:\/\//.test(output)) {
113+
child.stderr.removeListener('data', waitForListenHint);
114+
resolve(child);
115+
}
116+
}
117+
118+
child.stderr.on('data', waitForListenHint);
119+
});
120+
});
121+
}
122+
123+
function createAgentProxy(domain, client) {
124+
const agent = new EventEmitter();
125+
agent.then = (...args) => {
126+
// TODO: potentially fetch the protocol and pretty-print it here.
127+
const descriptor = {
128+
[util.inspect.custom](depth, { stylize }) {
129+
return stylize(`[Agent ${domain}]`, 'special');
130+
},
131+
};
132+
return Promise.resolve(descriptor).then(...args);
133+
};
134+
135+
return new Proxy(agent, {
136+
get(target, name) {
137+
if (name in target) return target[name];
138+
return function callVirtualMethod(params) {
139+
return client.callMethod(`${domain}.${name}`, params);
140+
};
141+
},
142+
});
143+
}
144+
142145
class NodeInspector {
143146
constructor(options, stdin, stdout) {
144147
this.options = options;
@@ -152,6 +155,7 @@ class NodeInspector {
152155
this._runScript = runScript.bind(null,
153156
options.script,
154157
options.scriptArgs,
158+
options.host,
155159
options.port,
156160
this.childPrint.bind(this));
157161
} else {
@@ -220,12 +224,7 @@ class NodeInspector {
220224
this.killChild();
221225
const { host, port } = this.options;
222226

223-
const runOncePortIsFree = () => {
224-
return portIsFree(host, port)
225-
.then(() => this._runScript());
226-
};
227-
228-
return runOncePortIsFree().then((child) => {
227+
return this._runScript().then((child) => {
229228
this.child = child;
230229

231230
let connectionAttempts = 0;
@@ -308,6 +307,22 @@ function parseArgv([target, ...args]) {
308307
port = parseInt(portMatch[1], 10);
309308
script = args[0];
310309
scriptArgs = args.slice(1);
310+
} else if (args.length === 1 && /^\d+$/.test(args[0]) && target === '-p') {
311+
// Start debugger against a given pid
312+
const pid = parseInt(args[0], 10);
313+
try {
314+
process._debugProcess(pid);
315+
} catch (e) {
316+
if (e.code === 'ESRCH') {
317+
/* eslint-disable no-console */
318+
console.error(`Target process: ${pid} doesn't exist.`);
319+
/* eslint-enable no-console */
320+
process.exit(1);
321+
}
322+
throw e;
323+
}
324+
script = null;
325+
isRemote = true;
311326
}
312327

313328
return {
@@ -327,6 +342,7 @@ function startInspect(argv = process.argv.slice(2),
327342

328343
console.error(`Usage: ${invokedAs} script.js`);
329344
console.error(` ${invokedAs} <host>:<port>`);
345+
console.error(` ${invokedAs} -p <pid>`);
330346
process.exit(1);
331347
}
332348

test/cli/pid.test.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict';
2+
const { spawn } = require('child_process');
3+
const Path = require('path');
4+
5+
const { test } = require('tap');
6+
7+
const startCLI = require('./start-cli');
8+
9+
function launchTarget(...args) {
10+
const childProc = spawn(process.execPath, args);
11+
return Promise.resolve(childProc);
12+
}
13+
14+
// process.debugPort is our proxy for "the version of node used to run this
15+
// test suite doesn't support SIGUSR1 for enabling --inspect for a process".
16+
const defaultsToOldProtocol = process.debugPort === 5858;
17+
18+
test('examples/alive.js', { skip: defaultsToOldProtocol }, (t) => {
19+
const script = Path.join('examples', 'alive.js');
20+
let cli = null;
21+
let target = null;
22+
23+
function cleanup(error) {
24+
if (cli) {
25+
cli.quit();
26+
cli = null;
27+
}
28+
if (target) {
29+
target.kill();
30+
target = null;
31+
}
32+
if (error) throw error;
33+
}
34+
35+
return launchTarget(script)
36+
.then((childProc) => {
37+
target = childProc;
38+
cli = startCLI(['-p', `${target.pid}`]);
39+
return cli.waitForPrompt();
40+
})
41+
.then(() => cli.command('sb("alive.js", 3)'))
42+
.then(() => cli.waitFor(/break/))
43+
.then(() => cli.waitForPrompt())
44+
.then(() => {
45+
t.match(
46+
cli.output,
47+
'> 3 ++x;',
48+
'marks the 3rd line');
49+
})
50+
.then(() => cleanup())
51+
.then(null, cleanup);
52+
});

0 commit comments

Comments
 (0)