Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1490164

Browse filesBrowse files
Eugene OstroukhovMylesBorins
authored andcommitted
inspector: allow concurrent inspector sessions
This change enables concurrent inspector sessions, through WebSocket interface as well as JS interface, in any combination. PR-URL: #20137 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 14188b1 commit 1490164
Copy full SHA for 1490164
Expand file treeCollapse file tree

15 files changed

+290
-254
lines changed
Open diff view settings
Collapse file

‎src/inspector_agent.cc‎

Copy file name to clipboardExpand all lines: src/inspector_agent.cc
+70-66Lines changed: 70 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ const int CONTEXT_GROUP_ID = 1;
189189

190190
class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
191191
public:
192-
explicit ChannelImpl(V8Inspector* inspector,
193-
InspectorSessionDelegate* delegate)
194-
: delegate_(delegate) {
192+
explicit ChannelImpl(const std::unique_ptr<V8Inspector>& inspector,
193+
std::unique_ptr<InspectorSessionDelegate> delegate)
194+
: delegate_(std::move(delegate)) {
195195
session_ = inspector->connect(1, this, StringView());
196196
}
197197

@@ -201,19 +201,11 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
201201
session_->dispatchProtocolMessage(message);
202202
}
203203

204-
bool waitForFrontendMessage() {
205-
return delegate_->WaitForFrontendMessageWhilePaused();
206-
}
207-
208204
void schedulePauseOnNextStatement(const std::string& reason) {
209205
std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
210206
session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
211207
}
212208

213-
InspectorSessionDelegate* delegate() {
214-
return delegate_;
215-
}
216-
217209
private:
218210
void sendResponse(
219211
int callId,
@@ -232,7 +224,7 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
232224
delegate_->SendMessageToFrontend(message);
233225
}
234226

235-
InspectorSessionDelegate* const delegate_;
227+
std::unique_ptr<InspectorSessionDelegate> delegate_;
236228
std::unique_ptr<v8_inspector::V8InspectorSession> session_;
237229
};
238230

@@ -300,8 +292,7 @@ class InspectorTimerHandle {
300292
class NodeInspectorClient : public V8InspectorClient {
301293
public:
302294
NodeInspectorClient(node::Environment* env, node::NodePlatform* platform)
303-
: env_(env), platform_(platform), terminated_(false),
304-
running_nested_loop_(false) {
295+
: env_(env), platform_(platform) {
305296
client_ = V8Inspector::create(env->isolate(), this);
306297
// TODO(bnoordhuis) Make name configurable from src/node.cc.
307298
ContextInfo info(GetHumanReadableProcessName());
@@ -310,18 +301,28 @@ class NodeInspectorClient : public V8InspectorClient {
310301
}
311302

312303
void runMessageLoopOnPause(int context_group_id) override {
313-
CHECK_NE(channel_, nullptr);
304+
runMessageLoop(false);
305+
}
306+
307+
void runMessageLoop(bool ignore_terminated) {
314308
if (running_nested_loop_)
315309
return;
316310
terminated_ = false;
317311
running_nested_loop_ = true;
318-
while (!terminated_ && channel_->waitForFrontendMessage()) {
312+
while ((ignore_terminated || !terminated_) && waitForFrontendEvent()) {
319313
while (platform_->FlushForegroundTasks(env_->isolate())) {}
320314
}
321315
terminated_ = false;
322316
running_nested_loop_ = false;
323317
}
324318

319+
bool waitForFrontendEvent() {
320+
InspectorIo* io = env_->inspector_agent()->io();
321+
if (io == nullptr)
322+
return false;
323+
return io->WaitForFrontendEvent();
324+
}
325+
325326
double currentTimeMS() override {
326327
return uv_hrtime() * 1.0 / NANOS_PER_MSEC;
327328
}
@@ -363,20 +364,22 @@ class NodeInspectorClient : public V8InspectorClient {
363364
terminated_ = true;
364365
}
365366

366-
void connectFrontend(InspectorSessionDelegate* delegate) {
367-
CHECK_EQ(channel_, nullptr);
368-
channel_ = std::unique_ptr<ChannelImpl>(
369-
new ChannelImpl(client_.get(), delegate));
367+
int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate) {
368+
events_dispatched_ = true;
369+
int session_id = next_session_id_++;
370+
channels_[session_id] =
371+
std::make_unique<ChannelImpl>(client_, std::move(delegate));
372+
return session_id;
370373
}
371374

372-
void disconnectFrontend() {
373-
quitMessageLoopOnPause();
374-
channel_.reset();
375+
void disconnectFrontend(int session_id) {
376+
events_dispatched_ = true;
377+
channels_.erase(session_id);
375378
}
376379

377-
void dispatchMessageFromFrontend(const StringView& message) {
378-
CHECK_NE(channel_, nullptr);
379-
channel_->dispatchProtocolMessage(message);
380+
void dispatchMessageFromFrontend(int session_id, const StringView& message) {
381+
events_dispatched_ = true;
382+
channels_[session_id]->dispatchProtocolMessage(message);
380383
}
381384

382385
Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
@@ -426,10 +429,6 @@ class NodeInspectorClient : public V8InspectorClient {
426429
script_id);
427430
}
428431

429-
ChannelImpl* channel() {
430-
return channel_.get();
431-
}
432-
433432
void startRepeatingTimer(double interval_s,
434433
TimerCallback callback,
435434
void* data) override {
@@ -464,20 +463,31 @@ class NodeInspectorClient : public V8InspectorClient {
464463
client_->allAsyncTasksCanceled();
465464
}
466465

466+
void schedulePauseOnNextStatement(const std::string& reason) {
467+
for (const auto& id_channel : channels_) {
468+
id_channel.second->schedulePauseOnNextStatement(reason);
469+
}
470+
}
471+
472+
bool hasConnectedSessions() {
473+
return !channels_.empty();
474+
}
475+
467476
private:
468477
node::Environment* env_;
469478
node::NodePlatform* platform_;
470-
bool terminated_;
471-
bool running_nested_loop_;
479+
bool terminated_ = false;
480+
bool running_nested_loop_ = false;
472481
std::unique_ptr<V8Inspector> client_;
473-
std::unique_ptr<ChannelImpl> channel_;
482+
std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
474483
std::unordered_map<void*, InspectorTimerHandle> timers_;
484+
int next_session_id_ = 1;
485+
bool events_dispatched_ = false;
475486
};
476487

477488
Agent::Agent(Environment* env) : parent_env_(env),
478489
client_(nullptr),
479490
platform_(nullptr),
480-
enabled_(false),
481491
pending_enable_async_hook_(false),
482492
pending_disable_async_hook_(false) {}
483493

@@ -491,7 +501,7 @@ bool Agent::Start(node::NodePlatform* platform, const char* path,
491501
path_ = path == nullptr ? "" : path;
492502
debug_options_ = options;
493503
client_ =
494-
std::unique_ptr<NodeInspectorClient>(
504+
std::shared_ptr<NodeInspectorClient>(
495505
new NodeInspectorClient(parent_env_, platform));
496506
platform_ = platform;
497507
CHECK_EQ(0, uv_async_init(uv_default_loop(),
@@ -515,7 +525,6 @@ bool Agent::StartIoThread(bool wait_for_connect) {
515525

516526
CHECK_NE(client_, nullptr);
517527

518-
enabled_ = true;
519528
io_ = std::unique_ptr<InspectorIo>(
520529
new InspectorIo(parent_env_, platform_, path_, debug_options_,
521530
wait_for_connect));
@@ -554,20 +563,25 @@ void Agent::Stop() {
554563
if (io_ != nullptr) {
555564
io_->Stop();
556565
io_.reset();
557-
enabled_ = false;
558566
}
559567
}
560568

561-
void Agent::Connect(InspectorSessionDelegate* delegate) {
562-
enabled_ = true;
563-
client_->connectFrontend(delegate);
569+
std::unique_ptr<InspectorSession> Agent::Connect(
570+
std::unique_ptr<InspectorSessionDelegate> delegate) {
571+
int session_id = client_->connectFrontend(std::move(delegate));
572+
return std::make_unique<InspectorSession>(session_id, client_);
564573
}
565574

566575
void Agent::WaitForDisconnect() {
567576
CHECK_NE(client_, nullptr);
568577
client_->contextDestroyed(parent_env_->context());
569578
if (io_ != nullptr) {
570579
io_->WaitForDisconnect();
580+
// There is a bug in V8 Inspector (https://crbug.com/834056) that
581+
// calls V8InspectorClient::quitMessageLoopOnPause when a session
582+
// disconnects. We are using this flag to ignore those calls so the message
583+
// loop is spinning as long as there's a reason to expect inspector messages
584+
client_->runMessageLoop(true);
571585
}
572586
}
573587

@@ -578,33 +592,8 @@ void Agent::FatalException(Local<Value> error, Local<v8::Message> message) {
578592
WaitForDisconnect();
579593
}
580594

581-
void Agent::Dispatch(const StringView& message) {
582-
CHECK_NE(client_, nullptr);
583-
client_->dispatchMessageFromFrontend(message);
584-
}
585-
586-
void Agent::Disconnect() {
587-
CHECK_NE(client_, nullptr);
588-
client_->disconnectFrontend();
589-
}
590-
591-
void Agent::RunMessageLoop() {
592-
CHECK_NE(client_, nullptr);
593-
client_->runMessageLoopOnPause(CONTEXT_GROUP_ID);
594-
}
595-
596-
InspectorSessionDelegate* Agent::delegate() {
597-
CHECK_NE(client_, nullptr);
598-
ChannelImpl* channel = client_->channel();
599-
if (channel == nullptr)
600-
return nullptr;
601-
return channel->delegate();
602-
}
603-
604595
void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
605-
ChannelImpl* channel = client_->channel();
606-
if (channel != nullptr)
607-
channel->schedulePauseOnNextStatement(reason);
596+
client_->schedulePauseOnNextStatement(reason);
608597
}
609598

610599
void Agent::RegisterAsyncHook(Isolate* isolate,
@@ -699,5 +688,20 @@ bool Agent::IsWaitingForConnect() {
699688
return debug_options_.wait_for_connect();
700689
}
701690

691+
bool Agent::HasConnectedSessions() {
692+
return client_->hasConnectedSessions();
693+
}
694+
695+
InspectorSession::InspectorSession(int session_id,
696+
std::shared_ptr<NodeInspectorClient> client)
697+
: session_id_(session_id), client_(client) {}
698+
699+
InspectorSession::~InspectorSession() {
700+
client_->disconnectFrontend(session_id_);
701+
}
702+
703+
void InspectorSession::Dispatch(const StringView& message) {
704+
client_->dispatchMessageFromFrontend(session_id_, message);
705+
}
702706
} // namespace inspector
703707
} // namespace node
Collapse file

‎src/inspector_agent.h‎

Copy file name to clipboardExpand all lines: src/inspector_agent.h
+22-18Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,26 @@ class Environment;
2323
struct ContextInfo;
2424

2525
namespace inspector {
26+
class InspectorIo;
27+
class NodeInspectorClient;
28+
29+
class InspectorSession {
30+
public:
31+
InspectorSession(int session_id, std::shared_ptr<NodeInspectorClient> client);
32+
~InspectorSession();
33+
void Dispatch(const v8_inspector::StringView& message);
34+
private:
35+
int session_id_;
36+
std::shared_ptr<NodeInspectorClient> client_;
37+
};
2638

2739
class InspectorSessionDelegate {
2840
public:
2941
virtual ~InspectorSessionDelegate() = default;
30-
virtual bool WaitForFrontendMessageWhilePaused() = 0;
3142
virtual void SendMessageToFrontend(const v8_inspector::StringView& message)
3243
= 0;
3344
};
3445

35-
class InspectorIo;
36-
class NodeInspectorClient;
37-
3846
class Agent {
3947
public:
4048
explicit Agent(node::Environment* env);
@@ -66,19 +74,19 @@ class Agent {
6674
void RegisterAsyncHook(v8::Isolate* isolate,
6775
v8::Local<v8::Function> enable_function,
6876
v8::Local<v8::Function> disable_function);
77+
void EnableAsyncHook();
78+
void DisableAsyncHook();
6979

70-
// These methods are called by the WS protocol and JS binding to create
71-
// inspector sessions. The inspector responds by using the delegate to send
72-
// messages back.
73-
void Connect(InspectorSessionDelegate* delegate);
74-
void Disconnect();
75-
void Dispatch(const v8_inspector::StringView& message);
76-
InspectorSessionDelegate* delegate();
80+
// Called by the WS protocol and JS binding to create inspector sessions.
81+
// The inspector responds by using the delegate to send messages back.
82+
std::unique_ptr<InspectorSession> Connect(
83+
std::unique_ptr<InspectorSessionDelegate> delegate);
7784

78-
void RunMessageLoop();
79-
bool enabled() { return enabled_; }
8085
void PauseOnNextJavascriptStatement(const std::string& reason);
8186

87+
// Returns true as long as there is at least one connected session.
88+
bool HasConnectedSessions();
89+
8290
InspectorIo* io() {
8391
return io_.get();
8492
}
@@ -92,18 +100,14 @@ class Agent {
92100
DebugOptions& options() { return debug_options_; }
93101
void ContextCreated(v8::Local<v8::Context> context, const ContextInfo& info);
94102

95-
void EnableAsyncHook();
96-
void DisableAsyncHook();
97-
98103
private:
99104
void ToggleAsyncHook(v8::Isolate* isolate,
100105
const Persistent<v8::Function>& fn);
101106

102107
node::Environment* parent_env_;
103-
std::unique_ptr<NodeInspectorClient> client_;
108+
std::shared_ptr<NodeInspectorClient> client_;
104109
std::unique_ptr<InspectorIo> io_;
105110
v8::Platform* platform_;
106-
bool enabled_;
107111
std::string path_;
108112
DebugOptions debug_options_;
109113

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.