Skip to content

Commit

Permalink
inspector: workers debugging
Browse files Browse the repository at this point in the history
Introduce a NodeTarget inspector domain modelled after ChromeDevTools
Target domain. It notifies inspector frontend attached to a main V8
isolate when workers are starting and allows passing messages to
inspectors on their isolates. All inspector functionality is enabled on
worker isolates.

Backport-PR-URL: #22954
PR-URL: #21364
Reviewed-By: Aleksei Koziatinskii <ak239spb@gmail.com>
Reviewed-By: Jan Krems <jan.krems@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
  • Loading branch information
eugeneo authored and targos committed Sep 25, 2018
1 parent c29e5ac commit 16f7f52
Show file tree
Hide file tree
Showing 11 changed files with 775 additions and 10 deletions.
6 changes: 6 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -510,14 +510,18 @@
'src/inspector_socket.cc',
'src/inspector_socket_server.cc',
'src/inspector/main_thread_interface.cc',
'src/inspector/worker_inspector.cc',
'src/inspector/node_string.cc',
'src/inspector/worker_agent.cc',
'src/inspector/tracing_agent.cc',
'src/inspector_agent.h',
'src/inspector_io.h',
'src/inspector_socket.h',
'src/inspector_socket_server.h',
'src/inspector/main_thread_interface.h',
'src/inspector/worker_inspector.h',
'src/inspector/node_string.h',
'src/inspector/worker_agent.h',
'src/inspector/tracing_agent.h',
'<@(node_inspector_generated_sources)'
],
Expand Down Expand Up @@ -1067,6 +1071,8 @@
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Forward.h',
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Protocol.cpp',
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Protocol.h',
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/NodeWorker.cpp',
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/NodeWorker.h',
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/NodeTracing.cpp',
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/NodeTracing.h',
],
Expand Down
55 changes: 55 additions & 0 deletions src/inspector/node_protocol.pdl
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,58 @@ experimental domain NodeTracing
# Signals that tracing is stopped and there is no trace buffers pending flush, all data were
# delivered via dataCollected events.
event tracingComplete

# Support for sending messages to Node worker Inspector instances.
experimental domain NodeWorker

type WorkerID extends string

# Unique identifier of attached debugging session.
type SessionID extends string

type WorkerInfo extends object
properties
WorkerID workerId
string type
string title
string url

# Sends protocol message over session with given id.
command sendMessageToWorker
parameters
string message
# Identifier of the session.
SessionID sessionId

# Instructs the inspector to attach to running workers. Will also attach to new workers
# as they start
command enable
parameters
# Whether to new workers should be paused until the frontend sends `Runtime.runIfWaitingForDebugger`
# message to run them.
boolean waitForDebuggerOnStart

# Detaches from all running workers and disables attaching to new workers as they are started.
command disable

# Issued when attached to a worker.
event attachedToWorker
parameters
# Identifier assigned to the session used to send/receive messages.
SessionID sessionId
WorkerInfo workerInfo
boolean waitingForDebugger

# Issued when detached from the worker.
event detachedFromWorker
parameters
# Detached session identifier.
SessionID sessionId

# Notifies about a new protocol message received from the session
# (session ID is provided in attachedToWorker notification).
event receivedMessageFromWorker
parameters
# Identifier of a session which sends a message.
SessionID sessionId
string message
7 changes: 1 addition & 6 deletions src/inspector/node_protocol_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@
"path": "node_protocol.json",
"package": "src/node/inspector/protocol",
"output": "node/inspector/protocol",
"namespace": ["node", "inspector", "protocol"],
"options": [
{
"domain": "NodeTracing"
}
]
"namespace": ["node", "inspector", "protocol"]
},
"exported": {
"package": "include/inspector",
Expand Down
154 changes: 154 additions & 0 deletions src/inspector/worker_agent.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#include "worker_agent.h"

#include "main_thread_interface.h"
#include "worker_inspector.h"

namespace node {
namespace inspector {
namespace protocol {

class NodeWorkers
: public std::enable_shared_from_this<NodeWorkers> {
public:
explicit NodeWorkers(std::weak_ptr<NodeWorker::Frontend> frontend,
std::shared_ptr<MainThreadHandle> thread)
: frontend_(frontend), thread_(thread) {}
void WorkerCreated(const std::string& title,
const std::string& url,
bool waiting,
std::shared_ptr<MainThreadHandle> target);
void Receive(const std::string& id, const std::string& message);
void Send(const std::string& id, const std::string& message);
void Detached(const std::string& id);

private:
std::weak_ptr<NodeWorker::Frontend> frontend_;
std::shared_ptr<MainThreadHandle> thread_;
std::unordered_map<std::string, std::unique_ptr<InspectorSession>> sessions_;
int next_target_id_ = 0;
};

namespace {
class AgentWorkerInspectorDelegate : public WorkerDelegate {
public:
explicit AgentWorkerInspectorDelegate(std::shared_ptr<NodeWorkers> workers)
: workers_(workers) {}

void WorkerCreated(const std::string& title,
const std::string& url,
bool waiting,
std::shared_ptr<MainThreadHandle> target) override {
workers_->WorkerCreated(title, url, waiting, target);
}

private:
std::shared_ptr<NodeWorkers> workers_;
};

class ParentInspectorSessionDelegate : public InspectorSessionDelegate {
public:
ParentInspectorSessionDelegate(const std::string& id,
std::shared_ptr<NodeWorkers> workers)
: id_(id), workers_(workers) {}

~ParentInspectorSessionDelegate() override {
workers_->Detached(id_);
}

void SendMessageToFrontend(const v8_inspector::StringView& msg) override {
std::string message = protocol::StringUtil::StringViewToUtf8(msg);
workers_->Send(id_, message);
}

private:
std::string id_;
std::shared_ptr<NodeWorkers> workers_;
};

std::unique_ptr<NodeWorker::WorkerInfo> WorkerInfo(const std::string& id,
const std::string& title,
const std::string& url) {
return NodeWorker::WorkerInfo::create()
.setWorkerId(id)
.setTitle(title)
.setUrl(url)
.setType("worker").build();
}
} // namespace

WorkerAgent::WorkerAgent(std::weak_ptr<WorkerManager> manager)
: manager_(manager) {}


void WorkerAgent::Wire(UberDispatcher* dispatcher) {
frontend_.reset(new NodeWorker::Frontend(dispatcher->channel()));
NodeWorker::Dispatcher::wire(dispatcher, this);
auto manager = manager_.lock();
CHECK_NOT_NULL(manager);
workers_ =
std::make_shared<NodeWorkers>(frontend_, manager->MainThread());
}

DispatchResponse WorkerAgent::sendMessageToWorker(const String& message,
const String& sessionId) {
workers_->Receive(sessionId, message);
return DispatchResponse::OK();
}

DispatchResponse WorkerAgent::enable(bool waitForDebuggerOnStart) {
auto manager = manager_.lock();
if (!manager) {
return DispatchResponse::OK();
}
if (!event_handle_) {
std::unique_ptr<AgentWorkerInspectorDelegate> delegate(
new AgentWorkerInspectorDelegate(workers_));
event_handle_ = manager->SetAutoAttach(std::move(delegate));
}
event_handle_->SetWaitOnStart(waitForDebuggerOnStart);
return DispatchResponse::OK();
}

DispatchResponse WorkerAgent::disable() {
event_handle_.reset();
return DispatchResponse::OK();
}

void NodeWorkers::WorkerCreated(const std::string& title,
const std::string& url,
bool waiting,
std::shared_ptr<MainThreadHandle> target) {
auto frontend = frontend_.lock();
if (!frontend)
return;
std::string id = std::to_string(++next_target_id_);
auto delegate = thread_->MakeDelegateThreadSafe(
std::unique_ptr<InspectorSessionDelegate>(
new ParentInspectorSessionDelegate(id, shared_from_this())));
sessions_[id] = target->Connect(std::move(delegate), true);
frontend->attachedToWorker(id, WorkerInfo(id, title, url), waiting);
}

void NodeWorkers::Send(const std::string& id, const std::string& message) {
auto frontend = frontend_.lock();
if (frontend)
frontend->receivedMessageFromWorker(id, message);
}

void NodeWorkers::Receive(const std::string& id, const std::string& message) {
auto it = sessions_.find(id);
if (it != sessions_.end())
it->second->Dispatch(Utf8ToStringView(message)->string());
}

void NodeWorkers::Detached(const std::string& id) {
if (sessions_.erase(id) == 0)
return;
auto frontend = frontend_.lock();
if (frontend) {
frontend->detachedFromWorker(id);
}
}
} // namespace protocol
} // namespace inspector
} // namespace node
39 changes: 39 additions & 0 deletions src/inspector/worker_agent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef SRC_INSPECTOR_WORKER_AGENT_H_
#define SRC_INSPECTOR_WORKER_AGENT_H_

#include "node/inspector/protocol/NodeWorker.h"
#include "v8.h"


namespace node {
namespace inspector {
class WorkerManagerEventHandle;
class WorkerManager;

namespace protocol {
class NodeWorkers;

class WorkerAgent : public NodeWorker::Backend {
public:
explicit WorkerAgent(std::weak_ptr<WorkerManager> manager);
~WorkerAgent() override = default;

void Wire(UberDispatcher* dispatcher);

DispatchResponse sendMessageToWorker(const String& message,
const String& sessionId) override;

DispatchResponse enable(bool waitForDebuggerOnStart) override;
DispatchResponse disable() override;

private:
std::shared_ptr<NodeWorker::Frontend> frontend_;
std::weak_ptr<WorkerManager> manager_;
std::unique_ptr<WorkerManagerEventHandle> event_handle_;
std::shared_ptr<NodeWorkers> workers_;
};
} // namespace protocol
} // namespace inspector
} // namespace node

#endif // SRC_INSPECTOR_WORKER_AGENT_H_
Loading

0 comments on commit 16f7f52

Please sign in to comment.