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

Commit 4179506

Browse files
author
Jan Krems
committed
feat: Support for debugging a pid
1 parent 8612100 commit 4179506

File tree

3 files changed

+122
-53
lines changed

3 files changed

+122
-53
lines changed

lib/_inspect.js

+69-52
Original file line numberDiff line numberDiff line change
@@ -53,52 +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-brk=${inspectPort}`,
60-
].concat([script], scriptArgs);
61-
const child = spawn(process.execPath, args);
62-
child.stdout.setEncoding('utf8');
63-
child.stderr.setEncoding('utf8');
64-
child.stdout.on('data', childPrint);
65-
child.stderr.on('data', childPrint);
66-
67-
let output = '';
68-
function waitForListenHint(text) {
69-
output += text;
70-
if (/^Debugger listening on/.test(output)) {
71-
child.stderr.removeListener('data', waitForListenHint);
72-
resolve(child);
73-
}
74-
}
75-
76-
child.stderr.on('data', waitForListenHint);
77-
});
78-
}
79-
80-
function createAgentProxy(domain, client) {
81-
const agent = new EventEmitter();
82-
agent.then = (...args) => {
83-
// TODO: potentially fetch the protocol and pretty-print it here.
84-
const descriptor = {
85-
[util.inspect.custom](depth, { stylize }) {
86-
return stylize(`[Agent ${domain}]`, 'special');
87-
},
88-
};
89-
return Promise.resolve(descriptor).then(...args);
90-
};
91-
92-
return new Proxy(agent, {
93-
get(target, name) {
94-
if (name in target) return target[name];
95-
return function callVirtualMethod(params) {
96-
return client.callMethod(`${domain}.${name}`, params);
97-
};
98-
},
99-
});
100-
}
101-
10256
function portIsFree(host, port, timeout = 2000) {
10357
const retryDelay = 150;
10458
let didTimeOut = false;
@@ -138,6 +92,56 @@ function portIsFree(host, port, timeout = 2000) {
13892
});
13993
}
14094

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+
141145
class NodeInspector {
142146
constructor(options, stdin, stdout) {
143147
this.options = options;
@@ -151,6 +155,7 @@ class NodeInspector {
151155
this._runScript = runScript.bind(null,
152156
options.script,
153157
options.scriptArgs,
158+
options.host,
154159
options.port,
155160
this.childPrint.bind(this));
156161
} else {
@@ -219,12 +224,7 @@ class NodeInspector {
219224
this.killChild();
220225
const { host, port } = this.options;
221226

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

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

313329
return {
@@ -326,6 +342,7 @@ function startInspect(argv = process.argv.slice(2),
326342

327343
console.error(`Usage: ${invokedAs} script.js`);
328344
console.error(` ${invokedAs} <host>:<port>`);
345+
console.error(` ${invokedAs} -p <pid>`);
329346
process.exit(1);
330347
}
331348

test/cli/launch.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ test('examples/three-lines.js', (t) => {
3636
t.match(cli.output, 'debug>', 'prints a prompt');
3737
t.match(
3838
cli.output,
39-
new RegExp(`< Debugger listening on [^\n]*9229`),
39+
/< Debugger listening on [^\n]*9229/,
4040
'forwards child output');
4141
})
4242
.then(() => cli.command('["hello", "world"].join(" ")'))

test/cli/pid.test.js

+52
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)