-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
inspector: JavaScript bindings for the inspector #12263
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Inspector | ||
|
||
> Stability: 1 - Experimental | ||
|
||
The `inspector` module provides an API for interacting with the V8 inspector. | ||
|
||
It can be accessed using: | ||
|
||
```js | ||
const inspector = require('inspector'); | ||
``` | ||
|
||
## Class: inspector.Session | ||
|
||
The `inspector.Session` is used for dispatching messages to the V8 inspector | ||
back-end and receiving message responses and notifications. | ||
|
||
### Constructor: new inspector.Session() | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
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. | ||
|
||
`inspector.Session` is an [`EventEmitter`][] with the following events: | ||
|
||
### Event: 'inspectorNotification' | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
* {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: REPLACEME | ||
--> | ||
|
||
* {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)); | ||
// [ '/node/test/inspector/test-bindings.js:11:0' ] | ||
``` | ||
|
||
### session.connect() | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
Connects a session to the inspector back-end. An exception will be thrown | ||
if there is already a connected session esteblished either through API or by | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: established. Also, "the API". |
||
a front-end connected to the Inspector WebSocket port. | ||
|
||
### session.post(method[, params][, callback]) | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
* method {string} | ||
* params {Object} | ||
* callback {Function} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. any errors thrown by the callback are caught and ignored, though a message is written to stderr:
That doesn't seem right, shouldn't it propogate up as an uncaught exception? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Native code now propagates the exception. Sending "uncaughtException" event may be cleaner - I am not entirely sure. Also, how do I send that event - do I do |
||
|
||
Posts a message to the inspector back-end. `callback` will be notified when | ||
a response is received. `callback` is a function that accepts two optional | ||
arguments - error and message-specific result. | ||
|
||
```js | ||
session.post('Runtime.evaluate', {'expression': '2 + 2'}, | ||
(error, {result}) => console.log(result.value)); | ||
// Output: { type: 'number', value: 4, description: '4' } | ||
``` | ||
|
||
The latest version of the V8 inspector protocol is published on the | ||
[Chrome DevTools Protocol Viewer][]. | ||
|
||
Node inspector supports all the Chrome DevTools Protocol domains declared | ||
by the V8. Chrome DevTools Protocol domain provides an interface for interacting | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just "V8". :-) |
||
with one of the runtime agents used to inspect the application state and listen | ||
to the run-time events. | ||
|
||
### session.disconnect() | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
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.connect()`]: #sessionconnect | ||
[`Debugger.paused`]: https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#event-paused | ||
[`EventEmitter`]: events.html#events_class_eventemitter | ||
[Chrome DevTools Protocol Viewer]: https://chromedevtools.github.io/devtools-protocol/v8/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
'use strict'; | ||
|
||
const connect = process.binding('inspector').connect; | ||
const EventEmitter = require('events'); | ||
const util = require('util'); | ||
|
||
if (!connect) | ||
throw new Error('Inspector is not available'); | ||
|
||
const connectionSymbol = Symbol('connectionProperty'); | ||
const messageCallbacksSymbol = Symbol('messageCallbacks'); | ||
const nextIdSymbol = Symbol('nextId'); | ||
const onMessageSymbol = Symbol('onMessage'); | ||
|
||
class Session extends EventEmitter { | ||
constructor() { | ||
super(); | ||
this[connectionSymbol] = null; | ||
this[nextIdSymbol] = 1; | ||
this[messageCallbacksSymbol] = new Map(); | ||
} | ||
|
||
connect() { | ||
if (this[connectionSymbol]) | ||
throw new Error('Already connected'); | ||
this[connectionSymbol] = | ||
connect((message) => this[onMessageSymbol](message)); | ||
} | ||
|
||
[onMessageSymbol](message) { | ||
const parsed = JSON.parse(message); | ||
if (parsed.id) { | ||
const callback = this[messageCallbacksSymbol].get(parsed.id); | ||
this[messageCallbacksSymbol].delete(parsed.id); | ||
if (callback) | ||
callback(parsed.error || null, parsed.result || null); | ||
} else { | ||
this.emit(parsed.method, parsed); | ||
this.emit('inspectorNotification', parsed); | ||
} | ||
} | ||
|
||
post(method, params, callback) { | ||
if (typeof method !== 'string') | ||
throw new TypeError( | ||
`"method" must be a string, got ${typeof method} instead`); | ||
if (!callback && util.isFunction(params)) { | ||
callback = params; | ||
params = null; | ||
} | ||
if (params && typeof params !== 'object') | ||
throw new TypeError( | ||
`"params" must be an object, got ${typeof params} instead`); | ||
if (callback && typeof callback !== 'function') | ||
throw new TypeError( | ||
`"callback" must be a function, got ${typeof callback} instead`); | ||
|
||
if (!this[connectionSymbol]) | ||
throw new Error('Session is not connected'); | ||
const id = this[nextIdSymbol]++; | ||
const message = {id, method}; | ||
if (params) { | ||
message['params'] = params; | ||
} | ||
if (callback) { | ||
this[messageCallbacksSymbol].set(id, callback); | ||
} | ||
this[connectionSymbol].dispatch(JSON.stringify(message)); | ||
} | ||
|
||
disconnect() { | ||
if (!this[connectionSymbol]) | ||
return; | ||
this[connectionSymbol].disconnect(); | ||
this[connectionSymbol] = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reset There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
const remainingCallbacks = this[messageCallbacksSymbol].values(); | ||
for (const callback of remainingCallbacks) { | ||
process.nextTick(callback, new Error('Session was closed')); | ||
} | ||
this[messageCallbacksSymbol].clear(); | ||
this[nextIdSymbol] = 1; | ||
} | ||
} | ||
|
||
module.exports = { | ||
Session | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might conflict with existing npm "inspector" package by @AndreasMadsen. Pinging him to see if that package is still useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping @AndreasMadsen again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't hear back from @AndreasMadsen, I think we should rename the module to avoid conflict with the existing module on npm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any suggestions for an alternative name? I see
node-inspector
andv8-inspector
taken, as well asjsinspector
(sojs-inspector
seems to be available).We could call it
inspector-session
- but the plan is to provide more inspector controls, such as start/stop server on specific port, programmatic access to UUID, adding domains, etc.