-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
inspector: added NodeRuntime domain #27600
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
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 |
---|---|---|
|
@@ -92,3 +92,16 @@ experimental domain NodeWorker | |
# Identifier of a session which sends a message. | ||
SessionID sessionId | ||
string message | ||
|
||
# Support for inspecting node process state. | ||
experimental domain NodeRuntime | ||
# Enable the `NodeRuntime.waitingForDisconnect`. | ||
command notifyWhenWaitingForDisconnect | ||
parameters | ||
boolean enabled | ||
|
||
# This event is fired instead of `Runtime.executionContextDestroyed` when | ||
# enabled. | ||
# It is fired when the Node process finished all code execution and is | ||
# waiting for all frontends to disconnect. | ||
event waitingForDisconnect | ||
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. I believe it needs to be communecated clearly on the purpose of this notification vs the 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. I updated comment. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#include "runtime_agent.h" | ||
|
||
#include "env-inl.h" | ||
#include "inspector_agent.h" | ||
|
||
namespace node { | ||
namespace inspector { | ||
namespace protocol { | ||
|
||
RuntimeAgent::RuntimeAgent(Environment* env) | ||
: notify_when_waiting_for_disconnect_(false), env_(env) {} | ||
|
||
void RuntimeAgent::Wire(UberDispatcher* dispatcher) { | ||
frontend_ = std::make_unique<NodeRuntime::Frontend>(dispatcher->channel()); | ||
NodeRuntime::Dispatcher::wire(dispatcher, this); | ||
} | ||
|
||
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(); | ||
} | ||
|
||
bool RuntimeAgent::notifyWaitingForDisconnect() { | ||
if (notify_when_waiting_for_disconnect_) { | ||
frontend_->waitingForDisconnect(); | ||
return true; | ||
} | ||
return false; | ||
} | ||
} // namespace protocol | ||
} // namespace inspector | ||
} // namespace node |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#ifndef SRC_INSPECTOR_RUNTIME_AGENT_H_ | ||
#define SRC_INSPECTOR_RUNTIME_AGENT_H_ | ||
|
||
#include "node/inspector/protocol/NodeRuntime.h" | ||
#include "v8.h" | ||
|
||
namespace node { | ||
class Environment; | ||
|
||
namespace inspector { | ||
namespace protocol { | ||
|
||
class RuntimeAgent : public NodeRuntime::Backend { | ||
public: | ||
explicit RuntimeAgent(Environment* env); | ||
|
||
void Wire(UberDispatcher* dispatcher); | ||
|
||
DispatchResponse notifyWhenWaitingForDisconnect(bool enabled) override; | ||
|
||
bool notifyWaitingForDisconnect(); | ||
|
||
private: | ||
std::shared_ptr<NodeRuntime::Frontend> frontend_; | ||
bool notify_when_waiting_for_disconnect_; | ||
Environment* env_; | ||
}; | ||
} // namespace protocol | ||
} // namespace inspector | ||
} // namespace node | ||
|
||
#endif // SRC_INSPECTOR_RUNTIME_AGENT_H_ |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
#include "inspector/main_thread_interface.h" | ||
#include "inspector/node_string.h" | ||
#include "inspector/runtime_agent.h" | ||
#include "inspector/tracing_agent.h" | ||
#include "inspector/worker_agent.h" | ||
#include "inspector/worker_inspector.h" | ||
|
@@ -221,21 +222,26 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, | |
std::unique_ptr<InspectorSessionDelegate> delegate, | ||
std::shared_ptr<MainThreadHandle> main_thread_, | ||
bool prevent_shutdown) | ||
: delegate_(std::move(delegate)), prevent_shutdown_(prevent_shutdown) { | ||
: delegate_(std::move(delegate)), prevent_shutdown_(prevent_shutdown), | ||
retaining_context_(false) { | ||
session_ = inspector->connect(1, this, StringView()); | ||
node_dispatcher_ = std::make_unique<protocol::UberDispatcher>(this); | ||
tracing_agent_ = | ||
std::make_unique<protocol::TracingAgent>(env, main_thread_); | ||
tracing_agent_->Wire(node_dispatcher_.get()); | ||
worker_agent_ = std::make_unique<protocol::WorkerAgent>(worker_manager); | ||
worker_agent_->Wire(node_dispatcher_.get()); | ||
runtime_agent_ = std::make_unique<protocol::RuntimeAgent>(env); | ||
runtime_agent_->Wire(node_dispatcher_.get()); | ||
} | ||
|
||
~ChannelImpl() override { | ||
tracing_agent_->disable(); | ||
tracing_agent_.reset(); // Dispose before the dispatchers | ||
worker_agent_->disable(); | ||
worker_agent_.reset(); // Dispose before the dispatchers | ||
runtime_agent_->disable(); | ||
runtime_agent_.reset(); // Dispose before the dispatchers | ||
} | ||
|
||
void dispatchProtocolMessage(const StringView& message) { | ||
|
@@ -264,6 +270,15 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, | |
return prevent_shutdown_; | ||
} | ||
|
||
bool notifyWaitingForDisconnect() { | ||
retaining_context_ = runtime_agent_->notifyWaitingForDisconnect(); | ||
return retaining_context_; | ||
} | ||
|
||
bool retainingContext() { | ||
return retaining_context_; | ||
} | ||
|
||
private: | ||
void sendResponse( | ||
int callId, | ||
|
@@ -303,12 +318,14 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, | |
DCHECK(false); | ||
} | ||
|
||
std::unique_ptr<protocol::RuntimeAgent> runtime_agent_; | ||
std::unique_ptr<protocol::TracingAgent> tracing_agent_; | ||
std::unique_ptr<protocol::WorkerAgent> worker_agent_; | ||
std::unique_ptr<InspectorSessionDelegate> delegate_; | ||
std::unique_ptr<v8_inspector::V8InspectorSession> session_; | ||
std::unique_ptr<protocol::UberDispatcher> node_dispatcher_; | ||
bool prevent_shutdown_; | ||
bool retaining_context_; | ||
}; | ||
|
||
class InspectorTimer { | ||
|
@@ -511,7 +528,18 @@ class NodeInspectorClient : public V8InspectorClient { | |
} | ||
|
||
void disconnectFrontend(int session_id) { | ||
channels_.erase(session_id); | ||
auto it = channels_.find(session_id); | ||
if (it == channels_.end()) | ||
return; | ||
bool retaining_context = it->second->retainingContext(); | ||
channels_.erase(it); | ||
if (retaining_context) { | ||
for (const auto& id_channel : channels_) { | ||
if (id_channel.second->retainingContext()) | ||
return; | ||
} | ||
contextDestroyed(env_->context()); | ||
} | ||
} | ||
|
||
void dispatchMessageFromFrontend(int session_id, const StringView& message) { | ||
|
@@ -608,6 +636,15 @@ class NodeInspectorClient : public V8InspectorClient { | |
return false; | ||
} | ||
|
||
bool notifyWaitingForDisconnect() { | ||
bool retaining_context = false; | ||
for (const auto& id_channel : channels_) { | ||
if (id_channel.second->notifyWaitingForDisconnect()) | ||
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. It looks like this notification will only get sent to the sessions that requested it. Others will get nothing, not even an I suggest sending other sessions 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. It is not possible to send Based on how much hacks we need to add, how many clients there are in a wild right now, what is probability of using new client and old client at the same time I believe that we can just send nothing here. Protocol definition stated exactly this. 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. I tried to adopt solution that you proposed and it seems like it works! |
||
retaining_context = true; | ||
} | ||
return retaining_context; | ||
} | ||
|
||
std::shared_ptr<MainThreadHandle> getThreadHandle() { | ||
if (interface_ == nullptr) { | ||
interface_.reset(new MainThreadInterface( | ||
|
@@ -774,9 +811,8 @@ void Agent::WaitForDisconnect() { | |
fprintf(stderr, "Waiting for the debugger to disconnect...\n"); | ||
fflush(stderr); | ||
} | ||
// TODO(addaleax): Maybe this should use an at-exit hook for the Environment | ||
// or something similar? | ||
client_->contextDestroyed(parent_env_->context()); | ||
if (!client_->notifyWaitingForDisconnect()) | ||
client_->contextDestroyed(parent_env_->context()); | ||
if (io_ != nullptr) { | ||
io_->StopAcceptingNewConnections(); | ||
client_->waitForIoShutdown(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Flags: --expose-internals | ||
'use strict'; | ||
const common = require('../common'); | ||
|
||
common.skipIfInspectorDisabled(); | ||
|
||
const assert = require('assert'); | ||
const { NodeInstance } = require('../common/inspector-helper.js'); | ||
|
||
function mainContextDestroyed(notification) { | ||
return notification.method === 'Runtime.executionContextDestroyed' && | ||
notification.params.executionContextId === 1; | ||
} | ||
|
||
async function runTest() { | ||
const child = new NodeInstance(['--inspect-brk=0', '-e', 'process.exit(55)']); | ||
const session = await child.connectInspectorSession(); | ||
const oldStyleSession = await child.connectInspectorSession(); | ||
await oldStyleSession.send([ | ||
{ method: 'Runtime.enable' }]); | ||
await session.send([ | ||
{ method: 'Runtime.enable' }, | ||
{ method: 'NodeRuntime.notifyWhenWaitingForDisconnect', | ||
params: { enabled: true } }, | ||
{ method: 'Runtime.runIfWaitingForDebugger' }]); | ||
await session.waitForNotification((notification) => { | ||
return notification.method === 'NodeRuntime.waitingForDisconnect'; | ||
}); | ||
const receivedExecutionContextDestroyed = | ||
session.unprocessedNotifications().some(mainContextDestroyed); | ||
if (receivedExecutionContextDestroyed) { | ||
assert.fail('When NodeRuntime enabled, ' + | ||
'Runtime.executionContextDestroyed should not be sent'); | ||
} | ||
const { result: { value } } = await session.send({ | ||
method: 'Runtime.evaluate', params: { expression: '42' } | ||
}); | ||
assert.strictEqual(value, 42); | ||
await session.disconnect(); | ||
await oldStyleSession.waitForNotification(mainContextDestroyed); | ||
await oldStyleSession.disconnect(); | ||
assert.strictEqual((await child.expectShutdown()).exitCode, 55); | ||
} | ||
|
||
runTest(); |
Uh oh!
There was an error while loading. Please reload this page.