@@ -237,6 +237,11 @@ added:
237237 - v26.1.0
238238 - v24.16.0
239239changes:
240+ - version: REPLACEME
241+ pr-url: REPLACEME
242+ description: Add `--pid <pid>` and `--host <host>`/`--port <port>` to probe a process that's
243+ already running. The report now includes a top-level `target` field to describe what was
244+ probed, and a `locations` field showing which probe is bound to which location.
240245 - version:
241246 - v26.6.0
242247 - v24.20.0
@@ -270,8 +275,10 @@ changes:
270275` node inspect ` supports a non-interactive probe mode for inspecting runtime values
271276in an application via the flag ` --probe ` .
272277
273- Currently, probe mode only supports launching a new process from the entry point
274- script specified on the command line.
278+ There are two ways to use probe mode:
279+
280+ * Launch a new process and probe it by specifying the entry point script.
281+ * Attach to and probe a running process using ` --pid <pid> ` or ` --host <host> ` /` --port <port> ` .
275282
276283The probe mode sets one or more source breakpoints, evaluates specified
277284expressions whenever the execution reaches a breakpoint, and prints one
@@ -281,10 +288,18 @@ printf-style debugging without having to modify the application code and
281288clean up afterwards. It also supports structured JSON output for tool use.
282289
283290``` console
291+ # To launch a new process and probe it:
284292$ node inspect --probe < file> :< line> [:< col> ] --expr < expr> [--cond < expr> ] [--max-hit < n> ]
285293 [--probe <file>:<line>[:<col>] --expr <expr> [--cond <expr>] [--max-hit <n>] ...]
286294 [--json] [--preview] [--timeout=<ms>] [--port=<port>]
287295 [--] [<node-option> ...] <script> [<script-args> ...]
296+
297+ # To activate the inspector in an already-running process and probe it:
298+ $ node inspect --probe < file> :< line> [:< col> ] --expr < expr> [...] --pid < pid> [--host < host> ] [--port < port> ]
299+
300+ # To probe a running process whose inspector is already listening for connections:
301+ $ node inspect --probe < file> :< line> [:< col> ] --expr < expr> [...] --host < host> [--port < port> ]
302+ $ node inspect --probe < file> :< line> [:< col> ] --expr < expr> [...] --port < port> [--host < host> ]
288303```
289304
290305* ` --probe <file>:<line>[:<col>] ` : Source location of the probe. When execution
@@ -300,17 +315,32 @@ $ node inspect --probe <file>:<line>[:<col>] --expr <expr> [--cond <expr>] [--ma
300315 is treated as false.
301316* ` --max-hit <n> ` : An optional per-probe limit on the number of times the probe
302317 can be hit. When not specified, there's no hit limit. When any probe reaches
303- its hit limit, the probing process will detach and report the results. The process
304- being probed will continue to run. If any other probe is never reached by the time
305- the session ends, it will be reported as a missed probe.
306- * ` --timeout=<ms> ` : A global wall-clock deadline for the entire probe session.
307- The default is ` 30000 ` . This can be used to probe a long-running application
308- that can be terminated externally.
318+ its hit limit, the probing session finishes and reports the results. If the
319+ probed process was launched by probe mode, it's then terminated. If the process
320+ was already running before being attached for probing, it is resumed and left running.
321+ If any other probe is never reached by the time the session ends, the missed probes will
322+ be reported.
323+ * ` --timeout=<ms> ` : A deadline in milliseconds. The default is ` 30000 ` . It is
324+ applied first to the target responding to the inspector and binding the
325+ probes, then to the probing session itself, so a run can take up to about
326+ twice the limit to time out. If the target does not respond in time during startup,
327+ the report ends with an ` error ` and the probing process exits with a non-zero
328+ code. If the process was attached to instead of launched by probe mode,
329+ ` --timeout ` is an alternative to ` --max-hit ` for ending a probing session
330+ without terminating the probed process.
309331* ` --json ` : If used, prints a structured JSON report instead of the default text report.
310332* ` --preview ` : If used, non-primitive values will include CDP property previews for
311333 object-like JSON probe values.
312- * ` --port=<port> ` : Selects the local inspector port where the probing session
313- will listen. Defaults to ` 0 ` , which requests a random port.
334+ * ` --port=<port> ` : When the probe mode launches the probed process, it specifies the port
335+ the launched child process' inspector listens on and defaults to ` 0 ` , which requests a
336+ random port. When the probe mode attaches to a running process, this specifies the port to connect to
337+ and defaults to ` 9229 ` , while ` 0 ` is invalid.
338+ * ` --host <host> ` : In attach mode, the host to connect to. Defaults to
339+ ` 127.0.0.1 ` . It is an error to combine it with an entry point script.
340+ * ` --pid <pid> ` : Attach to a running Node.js process by PID instead of
341+ launching a child. This activates the inspector of the target process if it
342+ is not active yet, then connects to ` --host ` :` --port ` . See
343+ [ Attaching to a running process] ( #attaching-to-a-running-process ) .
314344* ` -- ` is optional unless the child needs its own Node.js flags.
315345
316346Additional rules about the composition of the options:
@@ -339,6 +369,65 @@ $ node inspect --probe app.js:10 --expr "user"
339369 --json --preview -- --no-warnings app.js --arg-for-app=foo
340370```
341371
372+ ### Attaching to a running process
373+
374+ When the entry point script is not specified and ` --probe ` is used, probe mode can attach
375+ to a process that is already running instead of launching a child process.
376+ This is useful for probing a long-running application, when users want to inspect runtime
377+ values without restarting it.
378+
379+ When ` --pid ` is specified, probe mode activates the inspector of the process by
380+ sending ` SIGUSR1 ` on POSIX platforms. The process must be a Node.js process,
381+ otherwise it may be terminated by ` SIGUSR1 ` . The probing process will retry
382+ connecting using ` --host ` and ` --port ` until the timeout fires.
383+ If ` --pid ` is not specified, the inspector in the target process must already be listening.
384+ If the connection is refused, an error is reported immediately.
385+
386+ ``` console
387+ # Probing a process by PID, which may not already have the inspector listening.
388+ # End the probing session when the specified probe is reached 5 times.
389+ $ node inspect --probe app.js:42 --expr " req.url" --max-hit 5 --pid 12345
390+
391+ # Probing a process that' s already listening for inspector connections at 127.0.0.1:9229.
392+ # End the probing session when the specified timeout is reached.
393+ $ node inspect --probe app.js:42 --expr " req.url" --timeout 50000 --host 127.0.0.1 --port 9229
394+ ```
395+
396+ Probing by attaching to a process differs from launching one in several ways.
397+
398+ * The scripts are already loaded in the process when the breakpoints are bound,
399+ and the ` locations ` field for each probe in the report shows where it bound.
400+ An empty list indicates that no loaded script matched the probe, e.g.
401+ when there's a typo in the path or the line.
402+ * When the probing session ends, it resumes and detaches from the probed process.
403+ The target should continue running and ** its inspector will remain activated** .
404+ Probe mode does not kill a process it did not launch.
405+ * If the target is waiting for ` --inspect-brk ` or ` --inspect-wait ` when the
406+ session attaches to it, these would be released so that the probes can run.
407+ * An attach session ends when one of the following happens:
408+ * A probe reaches ` --max-hit ` . The report ends with ` completed ` .
409+ * The target finishes on its own. The report ends with ` completed ` , or
410+ with ` miss ` when some probes are never hit. The target is then allowed to
411+ exit.
412+ * The target exits with an uncaught exception. The report ends with ` probe_target_exit `
413+ and an ` error ` that describes the exception. The probing process exits with code 0.
414+ * ` --timeout ` fires. The report ends with ` timeout ` and the hits recorded
415+ so far, and the probing process exits with code 0. If a probe expression
416+ is still running at that point, the session reports an ` error ` and exits
417+ with code 1 instead. The expression is terminated after at most another
418+ ` --timeout ` , unless it is blocked inside a native call, and the target
419+ then resumes.
420+ * The target does not respond during startup within ` --timeout ` , for
421+ example because its main thread is blocked for synchronous work. The
422+ report then ends with an ` error ` , and the probing process exits with code 1.
423+ * If the target is killed during the session, the session ends with an
424+ ` error ` . If it's attached via ` --pid ` , this is reported as
425+ ` probe_target_exit ` with exit code 0. If it's attached via ` --host ` and ` --port ` ,
426+ an exit cannot be distinguished from a network failure, so it is reported as
427+ ` probe_failure ` with a non-zero exit code.
428+ * The target's stdout and stderr are not captured, so the terminal ` error `
429+ events have no ` error.stderr ` field.
430+
342431### Probe output format
343432
344433When the probe session ends, the probing process prints a final report of all the probe hits and results.
@@ -380,12 +469,19 @@ When `--json` is used, the output shape looks like this:
380469
381470``` console
382471$ node inspect --json --probe cli.js:5 --expr ' rss' cli.js
383- {"v":2,"probes":[{"expr":"rss","target":{"suffix":"cli.js","line":5}}],"results":[{"probe":0,"event":"hit","hit":1,"location":{"url":"file:///path/to/cli.js","line":5,"column":3},"result":{"type":"number","value":55443456,"description":"55443456"}},{"probe":0,"event":"hit","hit":2,"location":{"url":"file:///path/to/cli.js","line":5,"column":3},"result":{"type":"number","value":55574528,"description":"55574528"}},{"event":"completed"}]}
472+ {"v":2,"target":{"argv":["cli.js"]}," probes":[{"expr":"rss","target":{"suffix":"cli.js","line":5},"locations":[{"url":"file:///path/to/cli.js","line":5,"column":3}] }],"results":[{"probe":0,"event":"hit","hit":1,"location":{"url":"file:///path/to/cli.js","line":5,"column":3},"result":{"type":"number","value":55443456,"description":"55443456"}},{"probe":0,"event":"hit","hit":2,"location":{"url":"file:///path/to/cli.js","line":5,"column":3},"result":{"type":"number","value":55574528,"description":"55574528"}},{"event":"completed"}]}
384473```
385474
386475``` json
387476{
388477 "v" : 2 , // Probe JSON schema version.
478+ "target" : {
479+ // Describes what was probed. The shape depends on how the session was started:
480+ // launch mode: { "argv": ["cli.js"] }
481+ // --pid <pid>: { "pid": 12345, "host": "127.0.0.1", "port": 9229 }
482+ // --host/--port: { "host": "127.0.0.1", "port": 9229 }
483+ "argv" : [" cli.js" ]
484+ },
389485 "probes" : [
390486 {
391487 "expr" : " rss" , // The expression paired with --probe.
@@ -397,7 +493,13 @@ $ node inspect --json --probe cli.js:5 --expr 'rss' cli.js
397493 // from the target and will be reported in each hit's `location` field.
398494 "suffix" : " cli.js" ,
399495 "line" : 5
400- }
496+ },
497+ // Locations where the probe's breakpoint is bound. The shape is the same as the `location`
498+ // of a hit. There is one entry per matching script, and it's empty when no loaded script
499+ // matches the target, or when the session ends before the probe is bound.
500+ "locations" : [
501+ { "url" : " file:///path/to/cli.js" , "line" : 5 , "column" : 3 }
502+ ]
401503 // `condition` is present only when the probe was given a --cond expression.
402504 // `maxHit` is present only when the probe was given a --max-hit limit.
403505 }
@@ -477,18 +579,23 @@ $ node inspect --json --probe cli.js:5 --expr 'rss' cli.js
477579
478580### Output and exit codes
479581
480- Probe mode only prints the final probe report to stdout, and otherwise silences
481- stdout/stderr from the child process. When the probing session ends,
582+ Probe mode only prints the final probe report to stdout. If the probed process is launched by
583+ probe mode, the stdout/stderr of the child process is silenced . When the probing session ends,
482584the probing process typically exits with code ` 0 ` and prints a final report to
483585stdout. If the child process exits with a non-zero code before the probe
484586session ends, or the probe session cannot complete for another reason, the
485587final report records a terminal ` error ` event.
486588
487- When ` error.code ` is ` 'probe_failure' ` or ` 'probe_timeout' ` , the probing process
488- exits with a non-zero code, indicating recorded hits may be incomplete.
489- In this case, ` error.message ` will contain recovery hints, and ` error.probe ` ,
490- when present, is an index into the report's ` probes ` array that identifies
491- the possible culprit probe on a best-effort basis to help guide debugging.
589+ When ` error.code ` is ` 'probe_failure' ` , the probing process exits with a
590+ non-zero code, indicating recorded hits may be incomplete. In this case,
591+ ` error.message ` will contain recovery hints, and ` error.probe ` , when present,
592+ is an index into the report's ` probes ` array that identifies the possible
593+ culprit probe on a best-effort basis to help guide debugging.
594+
595+ When ` error.code ` is ` 'probe_timeout' ` , the probing process exits with a
596+ non-zero code if it launched the process, and with ` 0 ` if it attached to a
597+ running process, where the timeout is considered a normal end of the session. See
598+ [ Attaching to a running process] ( #attaching-to-a-running-process ) .
492599
493600Invalid arguments and fatal launch or connect failures may cause the
494601probing process to exit with a non-zero code and print an error message
0 commit comments