Skip to content

Commit bcb802c

Browse files
authored
sea: allow using inspector command line flags with SEA
The inspector command line flags should be allowed via NODE_OPTIONS, though it should not be consumed when passed directly to the SEA. This patch allows it to consume inspector flags from NODE_OPTIONS and add tests for different behaviors. PR-URL: #59568 Fixes: #51688 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Darshan Sen <[email protected]>
1 parent 4e0af20 commit bcb802c

File tree

4 files changed

+193
-4
lines changed

4 files changed

+193
-4
lines changed

src/node_options.cc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,6 @@ void Parse(
422422
// TODO(addaleax): Make that unnecessary.
423423

424424
DebugOptionsParser::DebugOptionsParser() {
425-
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
426-
if (sea::IsSingleExecutable()) return;
427-
#endif
428-
429425
AddOption("--inspect-port",
430426
"set host:port for inspector",
431427
&DebugOptions::host_port,

test/sequential/sequential.status

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ test-single-executable-application-disable-experimental-sea-warning: SKIP
6161
test-single-executable-application-empty: SKIP
6262
test-single-executable-application-exec-argv: SKIP
6363
test-single-executable-application-exec-argv-empty: SKIP
64+
test-single-executable-application-inspect-in-sea-flags: SKIP
65+
test-single-executable-application-inspect: SKIP
6466
test-single-executable-application-snapshot: SKIP
6567
test-single-executable-application-snapshot-and-code-cache: SKIP
6668
test-single-executable-application-snapshot-worker: SKIP
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
'use strict';
2+
3+
// This tests that the debugger flag --inspect passed directly to a single executable
4+
// application would not be consumed by Node.js but passed to the application
5+
// instead.
6+
7+
require('../common');
8+
const assert = require('assert');
9+
const { writeFileSync, existsSync } = require('fs');
10+
const { spawnSyncAndAssert } = require('../common/child_process');
11+
const tmpdir = require('../common/tmpdir');
12+
const { spawnSyncAndExitWithoutError } = require('../common/child_process');
13+
14+
const {
15+
generateSEA,
16+
skipIfSingleExecutableIsNotSupported,
17+
} = require('../common/sea');
18+
19+
skipIfSingleExecutableIsNotSupported();
20+
21+
const configFile = tmpdir.resolve('sea-config.json');
22+
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
23+
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
24+
25+
tmpdir.refresh();
26+
27+
writeFileSync(tmpdir.resolve('sea.js'), `console.log(process.argv);`, 'utf-8');
28+
29+
// Create SEA configuration
30+
writeFileSync(configFile, `
31+
{
32+
"main": "sea.js",
33+
"output": "sea-prep.blob"
34+
}
35+
`);
36+
37+
// Generate the SEA prep blob
38+
spawnSyncAndExitWithoutError(
39+
process.execPath,
40+
['--experimental-sea-config', 'sea-config.json'],
41+
{ cwd: tmpdir.path }
42+
);
43+
44+
assert(existsSync(seaPrepBlob));
45+
46+
// Generate the SEA executable
47+
generateSEA(outputFile, process.execPath, seaPrepBlob);
48+
49+
// Spawn the SEA with inspect option
50+
spawnSyncAndAssert(
51+
outputFile,
52+
['--inspect=0'],
53+
{
54+
env: {
55+
...process.env,
56+
},
57+
},
58+
{
59+
stdout(data) {
60+
assert.match(data, /--inspect=0/);
61+
return true;
62+
},
63+
stderr(data) {
64+
assert.doesNotMatch(data, /Debugger listening/);
65+
return true;
66+
},
67+
trim: true,
68+
}
69+
);
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
'use strict';
2+
3+
// This tests the creation of a single executable application that can be
4+
// debugged using the inspector protocol with NODE_OPTIONS=--inspect-brk=0
5+
6+
require('../common');
7+
const assert = require('assert');
8+
const { writeFileSync, existsSync } = require('fs');
9+
const { spawn } = require('child_process');
10+
const tmpdir = require('../common/tmpdir');
11+
const { spawnSyncAndExitWithoutError } = require('../common/child_process');
12+
13+
const {
14+
generateSEA,
15+
skipIfSingleExecutableIsNotSupported,
16+
} = require('../common/sea');
17+
18+
skipIfSingleExecutableIsNotSupported();
19+
20+
const configFile = tmpdir.resolve('sea-config.json');
21+
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
22+
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
23+
24+
tmpdir.refresh();
25+
26+
// Create a simple hello world script
27+
writeFileSync(tmpdir.resolve('hello.js'), `console.log('Hello, world!');`, 'utf-8');
28+
29+
// Create SEA configuration
30+
writeFileSync(configFile, `
31+
{
32+
"main": "hello.js",
33+
"output": "sea-prep.blob"
34+
}
35+
`);
36+
37+
// Generate the SEA prep blob
38+
spawnSyncAndExitWithoutError(
39+
process.execPath,
40+
['--experimental-sea-config', 'sea-config.json'],
41+
{ cwd: tmpdir.path }
42+
);
43+
44+
assert(existsSync(seaPrepBlob));
45+
46+
// Generate the SEA executable
47+
generateSEA(outputFile, process.execPath, seaPrepBlob);
48+
49+
// Spawn the SEA with inspect option
50+
const seaProcess = spawn(outputFile, [], {
51+
env: {
52+
...process.env,
53+
NODE_OPTIONS: '--inspect-brk=0',
54+
},
55+
});
56+
57+
let debuggerUrl = null;
58+
let seaStderr = '';
59+
60+
seaProcess.stderr.setEncoding('utf8');
61+
seaProcess.stdout.setEncoding('utf8');
62+
63+
seaProcess.stdout.on('data', (data) => {
64+
console.log(`[SEA][STDOUT] ${data}`);
65+
});
66+
67+
seaProcess.stderr.on('data', (data) => {
68+
console.log(`[SEA][STDERR] ${data}`);
69+
seaStderr += data;
70+
71+
// Parse the debugger listening message
72+
const match = seaStderr.match(/Debugger listening on ws:\/\/([\d.]+):(\d+)\//);
73+
if (match && !debuggerUrl) {
74+
const host = match[1];
75+
const port = match[2];
76+
debuggerUrl = `${host}:${port}`;
77+
78+
console.log(`Running ${process.execPath} inspect ${debuggerUrl}`);
79+
// Once we have the debugger URL, spawn the inspector CLI
80+
const inspectorProcess = spawn(process.execPath, ['inspect', debuggerUrl], {
81+
stdio: ['pipe', 'pipe', 'pipe'],
82+
});
83+
84+
let inspectorStdout = '';
85+
inspectorProcess.stdout.setEncoding('utf8');
86+
inspectorProcess.stderr.setEncoding('utf8');
87+
88+
inspectorProcess.stdout.on('data', (data) => {
89+
console.log(`[INSPECT][STDOUT] ${data}`);
90+
inspectorStdout += data;
91+
92+
// Check if we successfully connected
93+
const matches = [...inspectorStdout.matchAll(/debug> /g)];
94+
if (inspectorStdout.includes(`connecting to ${host}:${port} ... ok`) &&
95+
matches.length >= 2) {
96+
// We are at the second prompt, which means we can send commands to terminate both now.
97+
console.log('Sending .exit command to inspector...');
98+
inspectorProcess.stdin.write('.exit\n');
99+
}
100+
});
101+
102+
inspectorProcess.stderr.on('data', (data) => {
103+
console.log(`[INSPECT][STDERR] ${data}`);
104+
});
105+
106+
inspectorProcess.on('close', (code) => {
107+
assert.strictEqual(code, 0, `Inspector process exited with code ${code}.`);
108+
});
109+
110+
inspectorProcess.on('error', (err) => {
111+
throw err;
112+
});
113+
}
114+
});
115+
116+
seaProcess.on('close', (code) => {
117+
assert.strictEqual(code, 0, `SEA process exited with code ${code}.`);
118+
});
119+
120+
seaProcess.on('error', (err) => {
121+
throw err;
122+
});

0 commit comments

Comments
 (0)