mirror of
https://github.com/nodejs/node.git
synced 2025-05-01 08:42:45 +00:00
inspector: introduce inspector/promises API
PR-URL: https://github.com/nodejs/node/pull/44250 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Ruy Adorno <ruyadorno@google.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
parent
a7a672c68f
commit
0324529e0f
@ -11,94 +11,40 @@ inspector.
|
||||
|
||||
It can be accessed using:
|
||||
|
||||
```js
|
||||
```mjs
|
||||
import * as inspector from 'node:inspector/promises';
|
||||
```
|
||||
|
||||
```cjs
|
||||
const inspector = require('node:inspector/promises');
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```mjs
|
||||
import * as inspector from 'node:inspector';
|
||||
```
|
||||
|
||||
```cjs
|
||||
const inspector = require('node:inspector');
|
||||
```
|
||||
|
||||
## `inspector.close()`
|
||||
## Promises API
|
||||
|
||||
> Stability: 1 - Experimental
|
||||
|
||||
<!-- YAML
|
||||
added: v9.0.0
|
||||
changes:
|
||||
- version:
|
||||
- v18.10.0
|
||||
- v16.18.0
|
||||
pr-url: https://github.com/nodejs/node/pull/44489
|
||||
description: The API is exposed in the worker threads.
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Deactivate the inspector. Blocks until there are no active connections.
|
||||
|
||||
## `inspector.console`
|
||||
|
||||
* {Object} An object to send messages to the remote inspector console.
|
||||
|
||||
```js
|
||||
require('node:inspector').console.log('a message');
|
||||
```
|
||||
|
||||
The inspector console does not have API parity with Node.js
|
||||
console.
|
||||
|
||||
## `inspector.open([port[, host[, wait]]])`
|
||||
|
||||
* `port` {number} Port to listen on for inspector connections. Optional.
|
||||
**Default:** what was specified on the CLI.
|
||||
* `host` {string} Host to listen on for inspector connections. Optional.
|
||||
**Default:** what was specified on the CLI.
|
||||
* `wait` {boolean} Block until a client has connected. Optional.
|
||||
**Default:** `false`.
|
||||
|
||||
Activate inspector on host and port. Equivalent to
|
||||
`node --inspect=[[host:]port]`, but can be done programmatically after node has
|
||||
started.
|
||||
|
||||
If wait is `true`, will block until a client has connected to the inspect port
|
||||
and flow control has been passed to the debugger client.
|
||||
|
||||
See the [security warning][] regarding the `host`
|
||||
parameter usage.
|
||||
|
||||
## `inspector.url()`
|
||||
|
||||
* Returns: {string|undefined}
|
||||
|
||||
Return the URL of the active inspector, or `undefined` if there is none.
|
||||
|
||||
```console
|
||||
$ node --inspect -p 'inspector.url()'
|
||||
Debugger listening on ws://127.0.0.1:9229/166e272e-7a30-4d09-97ce-f1c012b43c34
|
||||
For help, see: https://nodejs.org/en/docs/inspector
|
||||
ws://127.0.0.1:9229/166e272e-7a30-4d09-97ce-f1c012b43c34
|
||||
|
||||
$ node --inspect=localhost:3000 -p 'inspector.url()'
|
||||
Debugger listening on ws://localhost:3000/51cf8d0e-3c36-4c59-8efd-54519839e56a
|
||||
For help, see: https://nodejs.org/en/docs/inspector
|
||||
ws://localhost:3000/51cf8d0e-3c36-4c59-8efd-54519839e56a
|
||||
|
||||
$ node -p 'inspector.url()'
|
||||
undefined
|
||||
```
|
||||
|
||||
## `inspector.waitForDebugger()`
|
||||
|
||||
<!-- YAML
|
||||
added: v12.7.0
|
||||
-->
|
||||
|
||||
Blocks until a client (existing or connected later) has sent
|
||||
`Runtime.runIfWaitingForDebugger` command.
|
||||
|
||||
An exception will be thrown if there is no active inspector.
|
||||
|
||||
## Class: `inspector.Session`
|
||||
### Class: `inspector.Session`
|
||||
|
||||
* Extends: {EventEmitter}
|
||||
|
||||
The `inspector.Session` is used for dispatching messages to the V8 inspector
|
||||
back-end and receiving message responses and notifications.
|
||||
|
||||
### `new inspector.Session()`
|
||||
#### `new inspector.Session()`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
@ -108,7 +54,7 @@ Create a new instance of the `inspector.Session` class. The inspector session
|
||||
needs to be connected through [`session.connect()`][] before the messages
|
||||
can be dispatched to the inspector backend.
|
||||
|
||||
### Event: `'inspectorNotification'`
|
||||
#### Event: `'inspectorNotification'`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
@ -126,7 +72,7 @@ session.on('inspectorNotification', (message) => console.log(message.method));
|
||||
|
||||
It is also possible to subscribe only to notifications with specific method:
|
||||
|
||||
### Event: `<inspector-protocol-method>`;
|
||||
#### Event: `<inspector-protocol-method>`;
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
@ -148,7 +94,7 @@ session.on('Debugger.paused', ({ params }) => {
|
||||
// [ '/the/file/that/has/the/breakpoint.js:11:0' ]
|
||||
```
|
||||
|
||||
### `session.connect()`
|
||||
#### `session.connect()`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
@ -156,7 +102,7 @@ added: v8.0.0
|
||||
|
||||
Connects a session to the inspector back-end.
|
||||
|
||||
### `session.connectToMainThread()`
|
||||
#### `session.connectToMainThread()`
|
||||
|
||||
<!-- YAML
|
||||
added: v12.11.0
|
||||
@ -165,7 +111,7 @@ added: v12.11.0
|
||||
Connects a session to the main thread inspector back-end. An exception will
|
||||
be thrown if this API was not called on a Worker thread.
|
||||
|
||||
### `session.disconnect()`
|
||||
#### `session.disconnect()`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
@ -176,7 +122,176 @@ with an error. [`session.connect()`][] will need to be called to be able to send
|
||||
messages again. Reconnected session will lose all inspector state, such as
|
||||
enabled agents or configured breakpoints.
|
||||
|
||||
### `session.post(method[, params][, callback])`
|
||||
#### `session.post(method[, params])`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* `method` {string}
|
||||
* `params` {Object}
|
||||
* Returns: {Promise}
|
||||
|
||||
Posts a message to the inspector back-end.
|
||||
|
||||
```mjs
|
||||
import { Session } from 'node:inspector/promises';
|
||||
try {
|
||||
const session = new Session();
|
||||
session.connect();
|
||||
const result = await session.post('Runtime.evaluate', { expression: '2 + 2' });
|
||||
console.log(result);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
// Output: { type: 'number', value: 4, description: '4' }
|
||||
```
|
||||
|
||||
The latest version of the V8 inspector protocol is published on the
|
||||
[Chrome DevTools Protocol Viewer][].
|
||||
|
||||
Node.js inspector supports all the Chrome DevTools Protocol domains declared
|
||||
by V8. Chrome DevTools Protocol domain provides an interface for interacting
|
||||
with one of the runtime agents used to inspect the application state and listen
|
||||
to the run-time events.
|
||||
|
||||
#### Example usage
|
||||
|
||||
Apart from the debugger, various V8 Profilers are available through the DevTools
|
||||
protocol.
|
||||
|
||||
##### CPU profiler
|
||||
|
||||
Here's an example showing how to use the [CPU Profiler][]:
|
||||
|
||||
```mjs
|
||||
import { Session } from 'node:inspector/promises';
|
||||
import fs from 'node:fs';
|
||||
const session = new Session();
|
||||
session.connect();
|
||||
|
||||
await session.post('Profiler.enable');
|
||||
await session.post('Profiler.start');
|
||||
// Invoke business logic under measurement here...
|
||||
|
||||
// some time later...
|
||||
const { profile } = await session.post('Profiler.stop');
|
||||
|
||||
// Write profile to disk, upload, etc.
|
||||
fs.writeFileSync('./profile.cpuprofile', JSON.stringify(profile));
|
||||
```
|
||||
|
||||
##### Heap profiler
|
||||
|
||||
Here's an example showing how to use the [Heap Profiler][]:
|
||||
|
||||
```mjs
|
||||
import { Session } from 'node:inspector/promises';
|
||||
import fs from 'node:fs';
|
||||
const session = new Session();
|
||||
|
||||
const fd = fs.openSync('profile.heapsnapshot', 'w');
|
||||
|
||||
session.connect();
|
||||
|
||||
session.on('HeapProfiler.addHeapSnapshotChunk', (m) => {
|
||||
fs.writeSync(fd, m.params.chunk);
|
||||
});
|
||||
|
||||
const result = await session.post('HeapProfiler.takeHeapSnapshot', null);
|
||||
console.log('HeapProfiler.takeHeapSnapshot done:', result);
|
||||
session.disconnect();
|
||||
fs.closeSync(fd);
|
||||
```
|
||||
|
||||
## Callback API
|
||||
|
||||
### Class: `inspector.Session`
|
||||
|
||||
* Extends: {EventEmitter}
|
||||
|
||||
The `inspector.Session` is used for dispatching messages to the V8 inspector
|
||||
back-end and receiving message responses and notifications.
|
||||
|
||||
#### `new inspector.Session()`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
-->
|
||||
|
||||
Create a new instance of the `inspector.Session` class. The inspector session
|
||||
needs to be connected through [`session.connect()`][] before the messages
|
||||
can be dispatched to the inspector backend.
|
||||
|
||||
#### Event: `'inspectorNotification'`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
-->
|
||||
|
||||
* {Object} The notification message object
|
||||
|
||||
Emitted when any notification from the V8 Inspector is received.
|
||||
|
||||
```js
|
||||
session.on('inspectorNotification', (message) => console.log(message.method));
|
||||
// Debugger.paused
|
||||
// Debugger.resumed
|
||||
```
|
||||
|
||||
It is also possible to subscribe only to notifications with specific method:
|
||||
|
||||
#### Event: `<inspector-protocol-method>`;
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
-->
|
||||
|
||||
* {Object} The notification message object
|
||||
|
||||
Emitted when an inspector notification is received that has its method field set
|
||||
to the `<inspector-protocol-method>` value.
|
||||
|
||||
The following snippet installs a listener on the [`'Debugger.paused'`][]
|
||||
event, and prints the reason for program suspension whenever program
|
||||
execution is suspended (through breakpoints, for example):
|
||||
|
||||
```js
|
||||
session.on('Debugger.paused', ({ params }) => {
|
||||
console.log(params.hitBreakpoints);
|
||||
});
|
||||
// [ '/the/file/that/has/the/breakpoint.js:11:0' ]
|
||||
```
|
||||
|
||||
#### `session.connect()`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
-->
|
||||
|
||||
Connects a session to the inspector back-end.
|
||||
|
||||
#### `session.connectToMainThread()`
|
||||
|
||||
<!-- YAML
|
||||
added: v12.11.0
|
||||
-->
|
||||
|
||||
Connects a session to the main thread inspector back-end. An exception will
|
||||
be thrown if this API was not called on a Worker thread.
|
||||
|
||||
#### `session.disconnect()`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
-->
|
||||
|
||||
Immediately close the session. All pending message callbacks will be called
|
||||
with an error. [`session.connect()`][] will need to be called to be able to send
|
||||
messages again. Reconnected session will lose all inspector state, such as
|
||||
enabled agents or configured breakpoints.
|
||||
|
||||
#### `session.post(method[, params][, callback])`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.0.0
|
||||
@ -210,12 +325,12 @@ by V8. Chrome DevTools Protocol domain provides an interface for interacting
|
||||
with one of the runtime agents used to inspect the application state and listen
|
||||
to the run-time events.
|
||||
|
||||
## Example usage
|
||||
#### Example usage
|
||||
|
||||
Apart from the debugger, various V8 Profilers are available through the DevTools
|
||||
protocol.
|
||||
|
||||
### CPU profiler
|
||||
##### CPU profiler
|
||||
|
||||
Here's an example showing how to use the [CPU Profiler][]:
|
||||
|
||||
@ -240,7 +355,7 @@ session.post('Profiler.enable', () => {
|
||||
});
|
||||
```
|
||||
|
||||
### Heap profiler
|
||||
##### Heap profiler
|
||||
|
||||
Here's an example showing how to use the [Heap Profiler][]:
|
||||
|
||||
@ -264,6 +379,82 @@ session.post('HeapProfiler.takeHeapSnapshot', null, (err, r) => {
|
||||
});
|
||||
```
|
||||
|
||||
## Common Objects
|
||||
|
||||
### `inspector.close()`
|
||||
|
||||
<!-- YAML
|
||||
added: v9.0.0
|
||||
changes:
|
||||
- version: v18.10.0
|
||||
pr-url: https://github.com/nodejs/node/pull/44489
|
||||
description: The API is exposed in the worker threads.
|
||||
-->
|
||||
|
||||
Deactivate the inspector. Blocks until there are no active connections.
|
||||
|
||||
### `inspector.console`
|
||||
|
||||
* {Object} An object to send messages to the remote inspector console.
|
||||
|
||||
```js
|
||||
require('node:inspector').console.log('a message');
|
||||
```
|
||||
|
||||
The inspector console does not have API parity with Node.js
|
||||
console.
|
||||
|
||||
### `inspector.open([port[, host[, wait]]])`
|
||||
|
||||
* `port` {number} Port to listen on for inspector connections. Optional.
|
||||
**Default:** what was specified on the CLI.
|
||||
* `host` {string} Host to listen on for inspector connections. Optional.
|
||||
**Default:** what was specified on the CLI.
|
||||
* `wait` {boolean} Block until a client has connected. Optional.
|
||||
**Default:** `false`.
|
||||
|
||||
Activate inspector on host and port. Equivalent to
|
||||
`node --inspect=[[host:]port]`, but can be done programmatically after node has
|
||||
started.
|
||||
|
||||
If wait is `true`, will block until a client has connected to the inspect port
|
||||
and flow control has been passed to the debugger client.
|
||||
|
||||
See the [security warning][] regarding the `host`
|
||||
parameter usage.
|
||||
|
||||
### `inspector.url()`
|
||||
|
||||
* Returns: {string|undefined}
|
||||
|
||||
Return the URL of the active inspector, or `undefined` if there is none.
|
||||
|
||||
```console
|
||||
$ node --inspect -p 'inspector.url()'
|
||||
Debugger listening on ws://127.0.0.1:9229/166e272e-7a30-4d09-97ce-f1c012b43c34
|
||||
For help, see: https://nodejs.org/en/docs/inspector
|
||||
ws://127.0.0.1:9229/166e272e-7a30-4d09-97ce-f1c012b43c34
|
||||
|
||||
$ node --inspect=localhost:3000 -p 'inspector.url()'
|
||||
Debugger listening on ws://localhost:3000/51cf8d0e-3c36-4c59-8efd-54519839e56a
|
||||
For help, see: https://nodejs.org/en/docs/inspector
|
||||
ws://localhost:3000/51cf8d0e-3c36-4c59-8efd-54519839e56a
|
||||
|
||||
$ node -p 'inspector.url()'
|
||||
undefined
|
||||
```
|
||||
|
||||
### `inspector.waitForDebugger()`
|
||||
|
||||
<!-- YAML
|
||||
added: v12.7.0
|
||||
-->
|
||||
|
||||
Blocks until a client (existing or connected later) has sent
|
||||
`Runtime.runIfWaitingForDebugger` command.
|
||||
|
||||
An exception will be thrown if there is no active inspector.
|
||||
|
||||
[CPU Profiler]: https://chromedevtools.github.io/devtools-protocol/v8/Profiler
|
||||
[Chrome DevTools Protocol Viewer]: https://chromedevtools.github.io/devtools-protocol/v8/
|
||||
[Heap Profiler]: https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler
|
||||
|
22
lib/inspector/promises.js
Normal file
22
lib/inspector/promises.js
Normal file
@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
const inspector = require('inspector');
|
||||
const { promisify } = require('util');
|
||||
const { FunctionPrototypeBind } = primordials;
|
||||
class Session extends inspector.Session {
|
||||
#post = promisify(FunctionPrototypeBind(super.post, this));
|
||||
/**
|
||||
* Posts a message to the inspector back-end.
|
||||
* @param {string} method
|
||||
* @param {Record<unknown, unknown>} [params]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async post(method, params) {
|
||||
return this.#post(method, params);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
...inspector,
|
||||
Session,
|
||||
};
|
@ -114,7 +114,7 @@ void BuiltinLoader::InitializeBuiltinCategories() {
|
||||
|
||||
builtin_categories_.cannot_be_required = std::set<std::string> {
|
||||
#if !HAVE_INSPECTOR
|
||||
"inspector", "internal/util/inspector",
|
||||
"inspector", "inspector/promises", "internal/util/inspector",
|
||||
#endif // !HAVE_INSPECTOR
|
||||
|
||||
#if !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT)
|
||||
|
61
test/parallel/test-inspector-promises.js
Normal file
61
test/parallel/test-inspector-promises.js
Normal file
@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
common.skipIfInspectorDisabled();
|
||||
|
||||
const assert = require('assert');
|
||||
const inspector = require('inspector/promises');
|
||||
|
||||
const { basename } = require('path');
|
||||
const currentFilename = basename(__filename);
|
||||
|
||||
{
|
||||
// Ensure that inspector/promises has the same signature as inspector
|
||||
assert.deepStrictEqual(Reflect.ownKeys(inspector), Reflect.ownKeys(require('inspector')));
|
||||
}
|
||||
|
||||
(async () => {
|
||||
{
|
||||
// Ensure that session.post returns a valid promisified result
|
||||
const session = new inspector.Session();
|
||||
session.connect();
|
||||
|
||||
await session.post('Profiler.enable');
|
||||
await session.post('Profiler.start');
|
||||
|
||||
const {
|
||||
profile
|
||||
} = await session.post('Profiler.stop');
|
||||
|
||||
const {
|
||||
callFrame: {
|
||||
url,
|
||||
},
|
||||
} = profile.nodes.find(({
|
||||
callFrame,
|
||||
}) => {
|
||||
return callFrame.url.includes(currentFilename);
|
||||
});
|
||||
session.disconnect();
|
||||
assert.deepStrictEqual(basename(url), currentFilename);
|
||||
}
|
||||
{
|
||||
// Ensure that even if a post function is slower than another, Promise.all will get it in order
|
||||
const session = new inspector.Session();
|
||||
session.connect();
|
||||
|
||||
const sum1 = session.post('Runtime.evaluate', { expression: '2 + 2' });
|
||||
const exp = 'new Promise((r) => setTimeout(() => r(6), 100))';
|
||||
const sum2 = session.post('Runtime.evaluate', { expression: exp, awaitPromise: true });
|
||||
const sum3 = session.post('Runtime.evaluate', { expression: '4 + 4' });
|
||||
|
||||
const results = (await Promise.all([
|
||||
sum1,
|
||||
sum2,
|
||||
sum3,
|
||||
])).map(({ result: { value } }) => value);
|
||||
|
||||
session.disconnect();
|
||||
assert.deepStrictEqual(results, [ 4, 6, 8 ]);
|
||||
}
|
||||
})().then(common.mustCall());
|
Loading…
Reference in New Issue
Block a user