Skip to content

Commit

Permalink
inspector: introduce the --inspect-wait flag
Browse files Browse the repository at this point in the history
PR-URL: #52734
Reviewed-By: Daeyeon Jeong <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Minwoo Jung <[email protected]>
  • Loading branch information
cola119 authored and marco-ippolito committed Jun 17, 2024
1 parent 4102244 commit 8cb1312
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 10 deletions.
16 changes: 16 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,7 @@ Activate inspector on `host:port`. Default is `127.0.0.1:9229`.
V8 inspector integration allows tools such as Chrome DevTools and IDEs to debug
and profile Node.js instances. The tools attach to Node.js instances via a
tcp port and communicate using the [Chrome DevTools Protocol][].
See [V8 Inspector integration for Node.js][] for further explanation on Node.js debugger.

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

Expand Down Expand Up @@ -1226,6 +1227,8 @@ added: v7.6.0
Activate inspector on `host:port` and break at start of user script.
Default `host:port` is `127.0.0.1:9229`.

See [V8 Inspector integration for Node.js][] for further explanation on Node.js debugger.

### `--inspect-port=[host:]port`

<!-- YAML
Expand All @@ -1247,6 +1250,17 @@ Specify ways of the inspector web socket url exposure.
By default inspector websocket url is available in stderr and under `/json/list`
endpoint on `http://host:port/json/list`.

### `--inspect-wait[=[host:]port]`

<!-- YAML
added: REPLACEME
-->

Activate inspector on `host:port` and wait for debugger to be attached.
Default `host:port` is `127.0.0.1:9229`.

See [V8 Inspector integration for Node.js][] for further explanation on Node.js debugger.

### `-i`, `--interactive`

<!-- YAML
Expand Down Expand Up @@ -2520,6 +2534,7 @@ one is included in the list below.
* `--inspect-brk`
* `--inspect-port`, `--debug-port`
* `--inspect-publish-uid`
* `--inspect-wait`
* `--inspect`
* `--max-http-header-size`
* `--napi-modules`
Expand Down Expand Up @@ -2971,6 +2986,7 @@ done
[ShadowRealm]: https://github.com/tc39/proposal-shadowrealm
[Source Map]: https://sourcemaps.info/spec.html
[Subresource Integrity]: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
[V8 Inspector integration for Node.js]: debugger.md#v8-inspector-integration-for-nodejs
[V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html
[Web Crypto API]: webcrypto.md
[`"type"`]: packages.md#type
Expand Down
17 changes: 15 additions & 2 deletions doc/api/debugger.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,21 @@ V8 Inspector can be enabled by passing the `--inspect` flag when starting a
Node.js application. It is also possible to supply a custom port with that flag,
e.g. `--inspect=9222` will accept DevTools connections on port 9222.

To break on the first line of the application code, pass the `--inspect-brk`
flag instead of `--inspect`.
Using the `--inspect` flag will execute the code immediately before debugger is connected.
This means that the code will start running before you can start debugging, which might
not be ideal if you want to debug from the very beginning.

In such cases, you have two alternatives:

1. `--inspect-wait` flag: This flag will wait for debugger to be attached before executing the code.
This allows you to start debugging right from the beginning of the execution.
2. `--inspect-brk` flag: Unlike `--inspect`, this flag will break on the first line of the code
as soon as debugger is attached. This is useful when you want to debug the code step by step
from the very beginning, without any code execution prior to debugging.

So, when deciding between `--inspect`, `--inspect-wait`, and `--inspect-brk`, consider whether you want
the code to start executing immediately, wait for debugger to be attached before execution,
or break on the first line for step-by-step debugging.

```console
$ node --inspect index.js
Expand Down
5 changes: 5 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,11 @@ and
Default is
.Sy stderr,http .
.
.It Fl -inspect-wait Ns = Ns Ar [host:]port
Activate inspector on
.Ar host:port
and wait for debugger to be attached.
.
.It Fl -inspect Ns = Ns Ar [host:]port
Activate inspector on
.Ar host:port .
Expand Down
20 changes: 12 additions & 8 deletions src/inspector_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -743,20 +743,24 @@ bool Agent::Start(const std::string& path,
}, parent_env_);

bool wait_for_connect = options.wait_for_connect();
bool should_break_first_line = options.should_break_first_line();
if (parent_handle_) {
wait_for_connect = parent_handle_->WaitForConnect();
parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
should_break_first_line = parent_handle_->WaitForConnect();
parent_handle_->WorkerStarted(client_->getThreadHandle(),
should_break_first_line);
} else if (!options.inspector_enabled || !options.allow_attaching_debugger ||
!StartIoThread()) {
return false;
}

// Patch the debug options to implement waitForDebuggerOnStart for
// the NodeWorker.enable method.
if (wait_for_connect) {
CHECK(!parent_env_->has_serialized_options());
debug_options_.EnableBreakFirstLine();
parent_env_->options()->get_debug_options()->EnableBreakFirstLine();
if (wait_for_connect || should_break_first_line) {
// Patch the debug options to implement waitForDebuggerOnStart for
// the NodeWorker.enable method.
if (should_break_first_line) {
CHECK(!parent_env_->has_serialized_options());
debug_options_.EnableBreakFirstLine();
parent_env_->options()->get_debug_options()->EnableBreakFirstLine();
}
client_->waitForFrontend();
}
return true;
Expand Down
8 changes: 8 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,14 @@ DebugOptionsParser::DebugOptionsParser() {
Implies("--inspect-brk-node", "--inspect");
AddAlias("--inspect-brk-node=", { "--inspect-port", "--inspect-brk-node" });

AddOption(
"--inspect-wait",
"activate inspector on host:port and wait for debugger to be attached",
&DebugOptions::inspect_wait,
kAllowedInEnvvar);
Implies("--inspect-wait", "--inspect");
AddAlias("--inspect-wait=", {"--inspect-port", "--inspect-wait"});

AddOption("--inspect-publish-uid",
"comma separated list of destinations for inspector uid"
"(default: stderr,http)",
Expand Down
6 changes: 6 additions & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class DebugOptions : public Options {
bool allow_attaching_debugger = true;
// --inspect
bool inspector_enabled = false;
// --inspect-wait
bool inspect_wait = false;
// --debug
bool deprecated_debug = false;
// --inspect-brk
Expand All @@ -93,6 +95,10 @@ class DebugOptions : public Options {
}

bool wait_for_connect() const {
return break_first_line || break_node_first_line || inspect_wait;
}

bool should_break_first_line() const {
return break_first_line || break_node_first_line;
}

Expand Down
28 changes: 28 additions & 0 deletions test/parallel/test-inspector-wait.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as common from '../common/index.mjs';

common.skipIfInspectorDisabled();

import assert from 'node:assert';
import { NodeInstance } from '../common/inspector-helper.js';


async function runTests() {
const child = new NodeInstance(['--inspect-wait=0'], 'console.log(0);');
const session = await child.connectInspectorSession();
await session.send({ method: 'NodeRuntime.enable' });
await session.waitForNotification('NodeRuntime.waitingForDebugger');

// The execution should be paused until the debugger is attached
while (await child.nextStderrString() !== 'Debugger attached.');

await session.send({ 'method': 'Runtime.runIfWaitingForDebugger' });

// Wait for the execution to finish
while (await child.nextStderrString() !== 'Waiting for the debugger to disconnect...');

await session.send({ method: 'NodeRuntime.disable' });
session.disconnect();
assert.strictEqual((await child.expectShutdown()).exitCode, 0);
}

runTests().then(common.mustCall());

0 comments on commit 8cb1312

Please sign in to comment.