Skip to content

Commit

Permalink
inspector: supported NodeRuntime domain in worker
Browse files Browse the repository at this point in the history
NodeRuntime domain was introduced to give inspector client way to
fetch captured information before Node process is gone. We need
similar capability for work.

With current protocol inspector client can force worker to wait
on start by passing waitForDebuggerOnStart flag to NodeWorker.enable
method. So client has some time to setup environment, e.g. start
profiler. At the same time there is no way to prevent worker from
being terminated. So we can start capturing profile but we can not
reliably get captured data back.

This PR implemented NodeRuntime.notifyWhenWaitingForDisconnect
method for worker. When NodeRuntime.waitingForDisconnect notification
is enabled, worker will wait for explicit NodeWorker.detach call.

With this PR worker tooling story is nicely aligned with main thread
tooling story. The only difference is that main thread by default is
waiting for disconnect but worker thread is not waiting.

Issue: #27677

PR-URL: #27706
Reviewed-By: Eugene Ostroukhov <eostroukhov@google.com>
  • Loading branch information
alexkozy committed Jun 2, 2019
1 parent e8f3119 commit 7e18c65
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 10 deletions.
5 changes: 5 additions & 0 deletions src/inspector/node_protocol.pdl
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ experimental domain NodeWorker
# Detaches from all running workers and disables attaching to new workers as they are started.
command disable

# Detached from the worker with given sessionId.
command detach
parameters
SessionID sessionId

# Issued when attached to a worker.
event attachedToWorker
parameters
Expand Down
4 changes: 0 additions & 4 deletions src/inspector/runtime_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ void RuntimeAgent::Wire(UberDispatcher* dispatcher) {
}

DispatchResponse RuntimeAgent::notifyWhenWaitingForDisconnect(bool enabled) {
if (!env_->owns_process_state()) {
return DispatchResponse::Error(
"NodeRuntime domain can only be used through main thread sessions");
}
notify_when_waiting_for_disconnect_ = enabled;
return DispatchResponse::OK();
}
Expand Down
5 changes: 5 additions & 0 deletions src/inspector/worker_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ DispatchResponse WorkerAgent::disable() {
return DispatchResponse::OK();
}

DispatchResponse WorkerAgent::detach(const String& sessionId) {
workers_->Detached(sessionId);
return DispatchResponse::OK();
}

void NodeWorkers::WorkerCreated(const std::string& title,
const std::string& url,
bool waiting,
Expand Down
1 change: 1 addition & 0 deletions src/inspector/worker_agent.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class WorkerAgent : public NodeWorker::Backend {

DispatchResponse enable(bool waitForDebuggerOnStart) override;
DispatchResponse disable() override;
DispatchResponse detach(const String& sessionId) override;

private:
std::shared_ptr<NodeWorker::Frontend> frontend_;
Expand Down
18 changes: 12 additions & 6 deletions src/inspector_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -472,8 +472,8 @@ class NodeInspectorClient : public V8InspectorClient {
runMessageLoop();
}

void waitForIoShutdown() {
waiting_for_io_shutdown_ = true;
void waitForSessionsDisconnect() {
waiting_for_sessions_disconnect_ = true;
runMessageLoop();
}

Expand Down Expand Up @@ -548,6 +548,8 @@ class NodeInspectorClient : public V8InspectorClient {
}
contextDestroyed(env_->context());
}
if (waiting_for_sessions_disconnect_ && !is_main_)
waiting_for_sessions_disconnect_ = false;
}

void dispatchMessageFromFrontend(int session_id, const StringView& message) {
Expand Down Expand Up @@ -678,8 +680,9 @@ class NodeInspectorClient : public V8InspectorClient {
bool shouldRunMessageLoop() {
if (waiting_for_frontend_)
return true;
if (waiting_for_io_shutdown_ || waiting_for_resume_)
if (waiting_for_sessions_disconnect_ || waiting_for_resume_) {
return hasConnectedSessions();
}
return false;
}

Expand Down Expand Up @@ -723,7 +726,7 @@ class NodeInspectorClient : public V8InspectorClient {
int next_session_id_ = 1;
bool waiting_for_resume_ = false;
bool waiting_for_frontend_ = false;
bool waiting_for_io_shutdown_ = false;
bool waiting_for_sessions_disconnect_ = false;
// Allows accessing Inspector from non-main threads
std::unique_ptr<MainThreadInterface> interface_;
std::shared_ptr<WorkerManager> worker_manager_;
Expand Down Expand Up @@ -819,11 +822,14 @@ void Agent::WaitForDisconnect() {
fprintf(stderr, "Waiting for the debugger to disconnect...\n");
fflush(stderr);
}
if (!client_->notifyWaitingForDisconnect())
if (!client_->notifyWaitingForDisconnect()) {
client_->contextDestroyed(parent_env_->context());
} else if (is_worker) {
client_->waitForSessionsDisconnect();
}
if (io_ != nullptr) {
io_->StopAcceptingNewConnections();
client_->waitForIoShutdown();
client_->waitForSessionsDisconnect();
}
}

Expand Down
47 changes: 47 additions & 0 deletions test/parallel/test-worker-debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,52 @@ async function testTwoWorkers(session, post) {
await Promise.all([worker1Exited, worker2Exited]);
}

async function testWaitForDisconnectInWorker(session, post) {
console.log('Test NodeRuntime.waitForDisconnect in worker');

const sessionWithoutWaiting = new Session();
sessionWithoutWaiting.connect();
const sessionWithoutWaitingPost = doPost.bind(null, sessionWithoutWaiting);

await sessionWithoutWaitingPost('NodeWorker.enable', {
waitForDebuggerOnStart: true
});
await post('NodeWorker.enable', { waitForDebuggerOnStart: true });

const attached = [
waitForWorkerAttach(session),
waitForWorkerAttach(sessionWithoutWaiting)
];

let worker = null;
const exitPromise = runWorker(2, (w) => worker = w);

const [{ sessionId: sessionId1 }, { sessionId: sessionId2 }] =
await Promise.all(attached);

const workerSession1 = new WorkerSession(session, sessionId1);
const workerSession2 = new WorkerSession(sessionWithoutWaiting, sessionId2);

await workerSession2.post('Runtime.enable');
await workerSession1.post('Runtime.enable');
await workerSession1.post('NodeRuntime.notifyWhenWaitingForDisconnect', {
enabled: true
});
await workerSession1.post('Runtime.runIfWaitingForDebugger');

worker.postMessage('resume');

await waitForEvent(workerSession1, 'NodeRuntime.waitingForDisconnect');
post('NodeWorker.detach', { sessionId: sessionId1 });
await waitForEvent(workerSession2, 'Runtime.executionContextDestroyed');

await exitPromise;

await post('NodeWorker.disable');
await sessionWithoutWaitingPost('NodeWorker.disable');
sessionWithoutWaiting.disconnect();
}

async function test() {
const session = new Session();
session.connect();
Expand All @@ -219,6 +265,7 @@ async function test() {

await testNoWaitOnStart(session, post);
await testTwoWorkers(session, post);
await testWaitForDisconnectInWorker(session, post);

session.disconnect();
console.log('Test done');
Expand Down

0 comments on commit 7e18c65

Please sign in to comment.