Skip to content

Commit c0ae3b2

Browse files
authored
inspector: introduce the --inspect-wait flag
PR-URL: nodejs#52734 Reviewed-By: Daeyeon Jeong <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Minwoo Jung <[email protected]>
1 parent cbc88b6 commit c0ae3b2

File tree

7 files changed

+90
-10
lines changed

7 files changed

+90
-10
lines changed

doc/api/cli.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,7 @@ Activate inspector on `host:port`. Default is `127.0.0.1:9229`.
12721272
V8 inspector integration allows tools such as Chrome DevTools and IDEs to debug
12731273
and profile Node.js instances. The tools attach to Node.js instances via a
12741274
tcp port and communicate using the [Chrome DevTools Protocol][].
1275+
See [V8 Inspector integration for Node.js][] for further explanation on Node.js debugger.
12751276

12761277
<!-- Anchor to make sure old links find a target -->
12771278

@@ -1302,6 +1303,8 @@ added: v7.6.0
13021303
Activate inspector on `host:port` and break at start of user script.
13031304
Default `host:port` is `127.0.0.1:9229`.
13041305

1306+
See [V8 Inspector integration for Node.js][] for further explanation on Node.js debugger.
1307+
13051308
### `--inspect-port=[host:]port`
13061309

13071310
<!-- YAML
@@ -1323,6 +1326,17 @@ Specify ways of the inspector web socket url exposure.
13231326
By default inspector websocket url is available in stderr and under `/json/list`
13241327
endpoint on `http://host:port/json/list`.
13251328

1329+
### `--inspect-wait[=[host:]port]`
1330+
1331+
<!-- YAML
1332+
added: REPLACEME
1333+
-->
1334+
1335+
Activate inspector on `host:port` and wait for debugger to be attached.
1336+
Default `host:port` is `127.0.0.1:9229`.
1337+
1338+
See [V8 Inspector integration for Node.js][] for further explanation on Node.js debugger.
1339+
13261340
### `-i`, `--interactive`
13271341

13281342
<!-- YAML
@@ -2662,6 +2676,7 @@ one is included in the list below.
26622676
* `--inspect-brk`
26632677
* `--inspect-port`, `--debug-port`
26642678
* `--inspect-publish-uid`
2679+
* `--inspect-wait`
26652680
* `--inspect`
26662681
* `--max-http-header-size`
26672682
* `--napi-modules`
@@ -3152,6 +3167,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
31523167
[ScriptCoverage]: https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ScriptCoverage
31533168
[ShadowRealm]: https://github.com/tc39/proposal-shadowrealm
31543169
[Source Map]: https://sourcemaps.info/spec.html
3170+
[V8 Inspector integration for Node.js]: debugger.md#v8-inspector-integration-for-nodejs
31553171
[V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html
31563172
[V8 code cache]: https://v8.dev/blog/code-caching-for-devs
31573173
[`"type"`]: packages.md#type

doc/api/debugger.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,21 @@ V8 Inspector can be enabled by passing the `--inspect` flag when starting a
234234
Node.js application. It is also possible to supply a custom port with that flag,
235235
e.g. `--inspect=9222` will accept DevTools connections on port 9222.
236236

237-
To break on the first line of the application code, pass the `--inspect-brk`
238-
flag instead of `--inspect`.
237+
Using the `--inspect` flag will execute the code immediately before debugger is connected.
238+
This means that the code will start running before you can start debugging, which might
239+
not be ideal if you want to debug from the very beginning.
240+
241+
In such cases, you have two alternatives:
242+
243+
1. `--inspect-wait` flag: This flag will wait for debugger to be attached before executing the code.
244+
This allows you to start debugging right from the beginning of the execution.
245+
2. `--inspect-brk` flag: Unlike `--inspect`, this flag will break on the first line of the code
246+
as soon as debugger is attached. This is useful when you want to debug the code step by step
247+
from the very beginning, without any code execution prior to debugging.
248+
249+
So, when deciding between `--inspect`, `--inspect-wait`, and `--inspect-brk`, consider whether you want
250+
the code to start executing immediately, wait for debugger to be attached before execution,
251+
or break on the first line for step-by-step debugging.
239252

240253
```console
241254
$ node --inspect index.js

doc/node.1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,11 @@ and
272272
Default is
273273
.Sy stderr,http .
274274
.
275+
.It Fl -inspect-wait Ns = Ns Ar [host:]port
276+
Activate inspector on
277+
.Ar host:port
278+
and wait for debugger to be attached.
279+
.
275280
.It Fl -inspect Ns = Ns Ar [host:]port
276281
Activate inspector on
277282
.Ar host:port .

src/inspector_agent.cc

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -743,20 +743,24 @@ bool Agent::Start(const std::string& path,
743743
}, parent_env_);
744744

745745
bool wait_for_connect = options.wait_for_connect();
746+
bool should_break_first_line = options.should_break_first_line();
746747
if (parent_handle_) {
747-
wait_for_connect = parent_handle_->WaitForConnect();
748-
parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
748+
should_break_first_line = parent_handle_->WaitForConnect();
749+
parent_handle_->WorkerStarted(client_->getThreadHandle(),
750+
should_break_first_line);
749751
} else if (!options.inspector_enabled || !options.allow_attaching_debugger ||
750752
!StartIoThread()) {
751753
return false;
752754
}
753755

754-
// Patch the debug options to implement waitForDebuggerOnStart for
755-
// the NodeWorker.enable method.
756-
if (wait_for_connect) {
757-
CHECK(!parent_env_->has_serialized_options());
758-
debug_options_.EnableBreakFirstLine();
759-
parent_env_->options()->get_debug_options()->EnableBreakFirstLine();
756+
if (wait_for_connect || should_break_first_line) {
757+
// Patch the debug options to implement waitForDebuggerOnStart for
758+
// the NodeWorker.enable method.
759+
if (should_break_first_line) {
760+
CHECK(!parent_env_->has_serialized_options());
761+
debug_options_.EnableBreakFirstLine();
762+
parent_env_->options()->get_debug_options()->EnableBreakFirstLine();
763+
}
760764
client_->waitForFrontend();
761765
}
762766
return true;

src/node_options.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,14 @@ DebugOptionsParser::DebugOptionsParser() {
331331
Implies("--inspect-brk-node", "--inspect");
332332
AddAlias("--inspect-brk-node=", { "--inspect-port", "--inspect-brk-node" });
333333

334+
AddOption(
335+
"--inspect-wait",
336+
"activate inspector on host:port and wait for debugger to be attached",
337+
&DebugOptions::inspect_wait,
338+
kAllowedInEnvvar);
339+
Implies("--inspect-wait", "--inspect");
340+
AddAlias("--inspect-wait=", {"--inspect-port", "--inspect-wait"});
341+
334342
AddOption("--inspect-publish-uid",
335343
"comma separated list of destinations for inspector uid"
336344
"(default: stderr,http)",

src/node_options.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ class DebugOptions : public Options {
7171
bool allow_attaching_debugger = true;
7272
// --inspect
7373
bool inspector_enabled = false;
74+
// --inspect-wait
75+
bool inspect_wait = false;
7476
// --debug
7577
bool deprecated_debug = false;
7678
// --inspect-brk
@@ -93,6 +95,10 @@ class DebugOptions : public Options {
9395
}
9496

9597
bool wait_for_connect() const {
98+
return break_first_line || break_node_first_line || inspect_wait;
99+
}
100+
101+
bool should_break_first_line() const {
96102
return break_first_line || break_node_first_line;
97103
}
98104

test/parallel/test-inspector-wait.mjs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as common from '../common/index.mjs';
2+
3+
common.skipIfInspectorDisabled();
4+
5+
import assert from 'node:assert';
6+
import { NodeInstance } from '../common/inspector-helper.js';
7+
8+
9+
async function runTests() {
10+
const child = new NodeInstance(['--inspect-wait=0'], 'console.log(0);');
11+
const session = await child.connectInspectorSession();
12+
await session.send({ method: 'NodeRuntime.enable' });
13+
await session.waitForNotification('NodeRuntime.waitingForDebugger');
14+
15+
// The execution should be paused until the debugger is attached
16+
while (await child.nextStderrString() !== 'Debugger attached.');
17+
18+
await session.send({ 'method': 'Runtime.runIfWaitingForDebugger' });
19+
20+
// Wait for the execution to finish
21+
while (await child.nextStderrString() !== 'Waiting for the debugger to disconnect...');
22+
23+
await session.send({ method: 'NodeRuntime.disable' });
24+
session.disconnect();
25+
assert.strictEqual((await child.expectShutdown()).exitCode, 0);
26+
}
27+
28+
runTests().then(common.mustCall());

0 commit comments

Comments
 (0)