inspector: add inspector.waitForDebugger()
This method blocks current node process until a client sends Runtime.runifWaitingForDebugger. It can be useful when we need to report inspector.url() before waiting for connection: ``` inspector.open(0, undefined, false); fs.writeFileSync(someFileName, inspector.url()); inspector.waitForDebugger(); ``` PR-URL: https://github.com/nodejs/node/pull/28453 Reviewed-By: Eugene Ostroukhov <eostroukhov@google.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
b30dca8d9e
commit
f02dfdb2b5
@ -1188,6 +1188,11 @@ after the session had already closed.
|
|||||||
|
|
||||||
An error occurred while issuing a command via the `inspector` module.
|
An error occurred while issuing a command via the `inspector` module.
|
||||||
|
|
||||||
|
<a id="ERR_INSPECTOR_NOT_ACTIVE"></a>
|
||||||
|
### ERR_INSPECTOR_NOT_ACTIVE
|
||||||
|
|
||||||
|
The `inspector` is not active when `inspector.waitForDebugger()` is called.
|
||||||
|
|
||||||
<a id="ERR_INSPECTOR_NOT_AVAILABLE"></a>
|
<a id="ERR_INSPECTOR_NOT_AVAILABLE"></a>
|
||||||
### ERR_INSPECTOR_NOT_AVAILABLE
|
### ERR_INSPECTOR_NOT_AVAILABLE
|
||||||
|
|
||||||
|
@ -52,6 +52,16 @@ parameter usage.
|
|||||||
|
|
||||||
Return the URL of the active inspector, or `undefined` if there is none.
|
Return the URL of the active inspector, or `undefined` if there is none.
|
||||||
|
|
||||||
|
## inspector.waitForDebugger()
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
The `inspector.Session` is used for dispatching messages to the V8 inspector
|
The `inspector.Session` is used for dispatching messages to the V8 inspector
|
||||||
|
@ -8,6 +8,7 @@ const {
|
|||||||
ERR_INSPECTOR_COMMAND,
|
ERR_INSPECTOR_COMMAND,
|
||||||
ERR_INSPECTOR_NOT_AVAILABLE,
|
ERR_INSPECTOR_NOT_AVAILABLE,
|
||||||
ERR_INSPECTOR_NOT_CONNECTED,
|
ERR_INSPECTOR_NOT_CONNECTED,
|
||||||
|
ERR_INSPECTOR_NOT_ACTIVE,
|
||||||
ERR_INVALID_ARG_TYPE,
|
ERR_INVALID_ARG_TYPE,
|
||||||
ERR_INVALID_CALLBACK
|
ERR_INVALID_CALLBACK
|
||||||
} = require('internal/errors').codes;
|
} = require('internal/errors').codes;
|
||||||
@ -19,7 +20,12 @@ if (!hasInspector)
|
|||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const { validateString } = require('internal/validators');
|
const { validateString } = require('internal/validators');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const { Connection, open, url } = internalBinding('inspector');
|
const {
|
||||||
|
Connection,
|
||||||
|
open,
|
||||||
|
url,
|
||||||
|
waitForDebugger
|
||||||
|
} = internalBinding('inspector');
|
||||||
|
|
||||||
const connectionSymbol = Symbol('connectionProperty');
|
const connectionSymbol = Symbol('connectionProperty');
|
||||||
const messageCallbacksSymbol = Symbol('messageCallbacks');
|
const messageCallbacksSymbol = Symbol('messageCallbacks');
|
||||||
@ -105,10 +111,22 @@ class Session extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function inspectorOpen(port, host, wait) {
|
||||||
|
open(port, host);
|
||||||
|
if (wait)
|
||||||
|
waitForDebugger();
|
||||||
|
}
|
||||||
|
|
||||||
|
function inspectorWaitForDebugger() {
|
||||||
|
if (!waitForDebugger())
|
||||||
|
throw new ERR_INSPECTOR_NOT_ACTIVE();
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
open: (port, host, wait) => open(port, host, !!wait),
|
open: inspectorOpen,
|
||||||
close: process._debugEnd,
|
close: process._debugEnd,
|
||||||
url: url,
|
url: url,
|
||||||
|
waitForDebugger: inspectorWaitForDebugger,
|
||||||
// This is dynamically added during bootstrap,
|
// This is dynamically added during bootstrap,
|
||||||
// where the console from the VM is still available
|
// where the console from the VM is still available
|
||||||
console: require('internal/util/inspector').consoleFromVM,
|
console: require('internal/util/inspector').consoleFromVM,
|
||||||
|
@ -887,6 +887,7 @@ E('ERR_INPUT_TYPE_NOT_ALLOWED', '--input-type can only be used with string ' +
|
|||||||
E('ERR_INSPECTOR_ALREADY_CONNECTED', '%s is already connected', Error);
|
E('ERR_INSPECTOR_ALREADY_CONNECTED', '%s is already connected', Error);
|
||||||
E('ERR_INSPECTOR_CLOSED', 'Session was closed', Error);
|
E('ERR_INSPECTOR_CLOSED', 'Session was closed', Error);
|
||||||
E('ERR_INSPECTOR_COMMAND', 'Inspector error %d: %s', Error);
|
E('ERR_INSPECTOR_COMMAND', 'Inspector error %d: %s', Error);
|
||||||
|
E('ERR_INSPECTOR_NOT_ACTIVE', 'Inspector is not active', Error);
|
||||||
E('ERR_INSPECTOR_NOT_AVAILABLE', 'Inspector is not available', Error);
|
E('ERR_INSPECTOR_NOT_AVAILABLE', 'Inspector is not available', Error);
|
||||||
E('ERR_INSPECTOR_NOT_CONNECTED', 'Session is not connected', Error);
|
E('ERR_INSPECTOR_NOT_CONNECTED', 'Session is not connected', Error);
|
||||||
E('ERR_INTERNAL_ASSERTION', (message) => {
|
E('ERR_INTERNAL_ASSERTION', (message) => {
|
||||||
|
@ -237,7 +237,6 @@ void IsEnabled(const FunctionCallbackInfo<Value>& args) {
|
|||||||
void Open(const FunctionCallbackInfo<Value>& args) {
|
void Open(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
Agent* agent = env->inspector_agent();
|
Agent* agent = env->inspector_agent();
|
||||||
bool wait_for_connect = false;
|
|
||||||
|
|
||||||
if (args.Length() > 0 && args[0]->IsUint32()) {
|
if (args.Length() > 0 && args[0]->IsUint32()) {
|
||||||
uint32_t port = args[0].As<Uint32>()->Value();
|
uint32_t port = args[0].As<Uint32>()->Value();
|
||||||
@ -249,12 +248,15 @@ void Open(const FunctionCallbackInfo<Value>& args) {
|
|||||||
agent->host_port()->set_host(*host);
|
agent->host_port()->set_host(*host);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.Length() > 2 && args[2]->IsBoolean()) {
|
|
||||||
wait_for_connect = args[2]->BooleanValue(args.GetIsolate());
|
|
||||||
}
|
|
||||||
agent->StartIoThread();
|
agent->StartIoThread();
|
||||||
if (wait_for_connect)
|
}
|
||||||
|
|
||||||
|
void WaitForDebugger(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
Agent* agent = env->inspector_agent();
|
||||||
|
if (agent->IsActive())
|
||||||
agent->WaitForConnect();
|
agent->WaitForConnect();
|
||||||
|
args.GetReturnValue().Set(agent->IsActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Url(const FunctionCallbackInfo<Value>& args) {
|
void Url(const FunctionCallbackInfo<Value>& args) {
|
||||||
@ -285,6 +287,7 @@ void Initialize(Local<Object> target, Local<Value> unused,
|
|||||||
env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart);
|
env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart);
|
||||||
env->SetMethod(target, "open", Open);
|
env->SetMethod(target, "open", Open);
|
||||||
env->SetMethodNoSideEffect(target, "url", Url);
|
env->SetMethodNoSideEffect(target, "url", Url);
|
||||||
|
env->SetMethod(target, "waitForDebugger", WaitForDebugger);
|
||||||
|
|
||||||
env->SetMethod(target, "asyncTaskScheduled", AsyncTaskScheduledWrapper);
|
env->SetMethod(target, "asyncTaskScheduled", AsyncTaskScheduledWrapper);
|
||||||
env->SetMethod(target, "asyncTaskCanceled",
|
env->SetMethod(target, "asyncTaskCanceled",
|
||||||
|
63
test/parallel/test-inspector-wait-for-connection.js
Normal file
63
test/parallel/test-inspector-wait-for-connection.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Flags: --expose-internals
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
common.skipIfInspectorDisabled();
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const { NodeInstance } = require('../common/inspector-helper.js');
|
||||||
|
|
||||||
|
async function runTests() {
|
||||||
|
const child = new NodeInstance(['-e', `(${main.toString()})()`], '', '');
|
||||||
|
const session = await child.connectInspectorSession();
|
||||||
|
await session.send({ method: 'Runtime.enable' });
|
||||||
|
// Check that there is only one console message received.
|
||||||
|
await session.waitForConsoleOutput('log', 'before wait for debugger');
|
||||||
|
assert.ok(!session.unprocessedNotifications()
|
||||||
|
.some((n) => n.method === 'Runtime.consoleAPICalled'));
|
||||||
|
// Check that inspector.url() is available between inspector.open() and
|
||||||
|
// inspector.waitForDebugger()
|
||||||
|
const { result: { value } } = await session.send({
|
||||||
|
method: 'Runtime.evaluate',
|
||||||
|
params: {
|
||||||
|
expression: 'process._ws',
|
||||||
|
includeCommandLineAPI: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assert.ok(value.startsWith('ws://'));
|
||||||
|
session.send({ method: 'Runtime.runIfWaitingForDebugger' });
|
||||||
|
// Check that messages after first and before second waitForDebugger are
|
||||||
|
// received
|
||||||
|
await session.waitForConsoleOutput('log', 'after wait for debugger');
|
||||||
|
await session.waitForConsoleOutput('log', 'before second wait for debugger');
|
||||||
|
assert.ok(!session.unprocessedNotifications()
|
||||||
|
.some((n) => n.method === 'Runtime.consoleAPICalled'));
|
||||||
|
const secondSession = await child.connectInspectorSession();
|
||||||
|
// Check that inspector.waitForDebugger can be resumed from another session
|
||||||
|
secondSession.send({ method: 'Runtime.runIfWaitingForDebugger' });
|
||||||
|
await session.waitForConsoleOutput('log', 'after second wait for debugger');
|
||||||
|
assert.ok(!session.unprocessedNotifications()
|
||||||
|
.some((n) => n.method === 'Runtime.consoleAPICalled'));
|
||||||
|
secondSession.disconnect();
|
||||||
|
session.disconnect();
|
||||||
|
|
||||||
|
function main(prefix) {
|
||||||
|
const inspector = require('inspector');
|
||||||
|
inspector.open(0, undefined, false);
|
||||||
|
process._ws = inspector.url();
|
||||||
|
console.log('before wait for debugger');
|
||||||
|
inspector.waitForDebugger();
|
||||||
|
console.log('after wait for debugger');
|
||||||
|
console.log('before second wait for debugger');
|
||||||
|
inspector.waitForDebugger();
|
||||||
|
console.log('after second wait for debugger');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that inspector.waitForDebugger throws if there is no active
|
||||||
|
// inspector
|
||||||
|
const re = /^Error \[ERR_INSPECTOR_NOT_ACTIVE\]: Inspector is not active$/;
|
||||||
|
assert.throws(() => require('inspector').waitForDebugger(), re);
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests();
|
Loading…
x
Reference in New Issue
Block a user