1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "js_native_api_v8_inspector.h"
17
18 #include "js_native_api_v8.h"
19 #include "inspector_socket_server.h"
20 #include "inspector/main_thread_interface.h"
21 #include "inspector/node_string.h"
22 #include "inspector/runtime_agent.h"
23 #include "inspector/tracing_agent.h"
24 #include "inspector/worker_agent.h"
25 #include "inspector/worker_inspector.h"
26 #include "crypto/crypto_util.h"
27 #include "node/inspector/protocol/Protocol.h"
28 #include "node_errors.h"
29 #include "node_internals.h"
30 #include "node_mutex.h"
31 #include "node_options-inl.h"
32 #include "node_process-inl.h"
33 #include "node_url.h"
34 #include "permission/permission.h"
35 #include "util-inl.h"
36 #include "v8-inspector.h"
37 #include "v8-platform.h"
38
39 #include "libplatform/libplatform.h"
40
41 #ifdef __POSIX__
42 #include <pthread.h>
43 #include <climits> // PTHREAD_STACK_MIN
44 #endif // __POSIX__
45
46 #include <algorithm>
47 #include <cstring>
48 #include <sstream>
49 #include <unordered_map>
50 #include <vector>
51
52 namespace v8impl {
53
54 namespace {
55 using node::ConditionVariable;
56 using node::Mutex;
57 using node::inspector::Utf8ToStringView;
58 using v8_inspector::StringBuffer;
59 using v8_inspector::StringView;
60
61 class MainThreadInterface;
62
63 class Request {
64 public:
65 virtual void Call(MainThreadInterface*) = 0;
66 virtual ~Request() = default;
67 };
68
69 class Deletable {
70 public:
71 virtual ~Deletable() = default;
72 };
73
74 using MessageQueue = std::deque<std::unique_ptr<Request>>;
75
76 class MainThreadHandle : public std::enable_shared_from_this<MainThreadHandle> {
77 public:
MainThreadHandle(MainThreadInterface* main_thread)78 explicit MainThreadHandle(MainThreadInterface* main_thread)
79 : main_thread_(main_thread) {
80 }
~MainThreadHandle()81 ~MainThreadHandle() {
82 Mutex::ScopedLock scoped_lock(block_lock_);
83 CHECK_NULL(main_thread_); // main_thread_ should have called Reset
84 }
85 std::unique_ptr<InspectorSession> Connect(
86 std::unique_ptr<InspectorSessionDelegate> delegate,
87 bool prevent_shutdown);
newObjectId()88 int newObjectId() {
89 return ++next_object_id_;
90 }
91 bool Post(std::unique_ptr<Request> request);
92
93 private:
94 void Reset();
95
96 MainThreadInterface* main_thread_;
97 Mutex block_lock_;
98 int next_session_id_ = 0;
99 std::atomic_int next_object_id_ = {1};
100
101 friend class MainThreadInterface;
102 };
103
104 class MainThreadInterface :
105 public std::enable_shared_from_this<MainThreadInterface> {
106 public:
107 explicit MainThreadInterface(Agent* agent);
108 ~MainThreadInterface();
109
110 void DispatchMessages();
111 void Post(std::unique_ptr<Request> request);
112 bool WaitForFrontendEvent();
113 std::shared_ptr<MainThreadHandle> GetHandle();
inspector_agent()114 Agent* inspector_agent() {
115 return agent_;
116 }
117 void AddObject(int handle, std::unique_ptr<Deletable> object);
118 Deletable* GetObject(int id);
119 Deletable* GetObjectIfExists(int id);
120 void RemoveObject(int handle);
121
122 private:
123 MessageQueue requests_;
124 Mutex requests_lock_; // requests_ live across threads
125 // This queue is to maintain the order of the messages for the cases
126 // when we reenter the DispatchMessages function.
127 MessageQueue dispatching_message_queue_;
128 bool dispatching_messages_ = false;
129 ConditionVariable incoming_message_cond_;
130 // Used from any thread
131 Agent* const agent_;
132 std::shared_ptr<MainThreadHandle> handle_;
133 std::unordered_map<int, std::unique_ptr<Deletable>> managed_objects_;
134 };
135
136 template <typename T>
137 class DeletableWrapper : public Deletable {
138 public:
DeletableWrapper(std::unique_ptr<T> object)139 explicit DeletableWrapper(std::unique_ptr<T> object)
140 : object_(std::move(object)) {}
141 ~DeletableWrapper() override = default;
142
get(MainThreadInterface* thread, int id)143 static T* get(MainThreadInterface* thread, int id) {
144 return
145 static_cast<DeletableWrapper<T>*>(thread->GetObject(id))->object_.get();
146 }
147
148 private:
149 std::unique_ptr<T> object_;
150 };
151
152 template <typename T>
WrapInDeletable(std::unique_ptr<T> object)153 std::unique_ptr<Deletable> WrapInDeletable(std::unique_ptr<T> object) {
154 return std::unique_ptr<DeletableWrapper<T>>(
155 new DeletableWrapper<T>(std::move(object)));
156 }
157
158 template <typename Factory>
159 class CreateObjectRequest : public Request {
160 public:
CreateObjectRequest(int object_id, Factory factory)161 CreateObjectRequest(int object_id, Factory factory)
162 : object_id_(object_id), factory_(std::move(factory)) {}
163
164 void Call(MainThreadInterface* thread) override {
165 thread->AddObject(object_id_, WrapInDeletable(factory_(thread)));
166 }
167
168 private:
169 int object_id_;
170 Factory factory_;
171 };
172
173 template <typename Factory>
NewCreateRequest(int object_id, Factory factory)174 std::unique_ptr<Request> NewCreateRequest(int object_id, Factory factory) {
175 return std::unique_ptr<Request>(
176 new CreateObjectRequest<Factory>(object_id, std::move(factory)));
177 }
178
179 class DeleteRequest : public Request {
180 public:
DeleteRequest(int object_id)181 explicit DeleteRequest(int object_id) : object_id_(object_id) {}
182
183 void Call(MainThreadInterface* thread) override {
184 thread->RemoveObject(object_id_);
185 }
186
187 private:
188 int object_id_;
189 };
190
191 template <typename Target, typename Fn>
192 class CallRequest : public Request {
193 public:
CallRequest(int id, Fn fn)194 CallRequest(int id, Fn fn) : id_(id), fn_(std::move(fn)) {}
195
196 void Call(MainThreadInterface* thread) override {
197 fn_(DeletableWrapper<Target>::get(thread, id_));
198 }
199
200 private:
201 int id_;
202 Fn fn_;
203 };
204
205 template <typename T>
206 class AnotherThreadObjectReference {
207 public:
AnotherThreadObjectReference( std::shared_ptr<MainThreadHandle> thread, int object_id)208 AnotherThreadObjectReference(
209 std::shared_ptr<MainThreadHandle> thread, int object_id)
210 : thread_(thread), object_id_(object_id) {}
211
212 template <typename Factory>
AnotherThreadObjectReference( std::shared_ptr<MainThreadHandle> thread, Factory factory)213 AnotherThreadObjectReference(
214 std::shared_ptr<MainThreadHandle> thread, Factory factory)
215 : AnotherThreadObjectReference(thread, thread->newObjectId()) {
216 thread_->Post(NewCreateRequest(object_id_, std::move(factory)));
217 }
218 AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete;
219
~AnotherThreadObjectReference()220 ~AnotherThreadObjectReference() {
221 // Disappearing thread may cause a memory leak
222 thread_->Post(std::make_unique<DeleteRequest>(object_id_));
223 }
224
225 template <typename Fn>
Call(Fn fn) const226 void Call(Fn fn) const {
227 using Request = CallRequest<T, Fn>;
228 thread_->Post(std::unique_ptr<Request>(
229 new Request(object_id_, std::move(fn))));
230 }
231
232 template <typename Arg>
Call(void (T::*fn)(Arg), Arg argument) const233 void Call(void (T::*fn)(Arg), Arg argument) const {
234 Call(std::bind(Apply<Arg>, std::placeholders::_1, fn, std::move(argument)));
235 }
236
237 private:
238 // This has to use non-const reference to support std::bind with non-copyable
239 // types
240 template <typename Argument>
Apply(T* target, void (T::*fn)(Argument), Argument& argument)241 static void Apply(T* target, void (T::*fn)(Argument),
242 /* NOLINT (runtime/references) */ Argument& argument) {
243 (target->*fn)(std::move(argument));
244 }
245
246 std::shared_ptr<MainThreadHandle> thread_;
247 const int object_id_;
248 };
249
250 class MainThreadSessionState {
251 public:
MainThreadSessionState(MainThreadInterface* thread, bool prevent_shutdown)252 MainThreadSessionState(MainThreadInterface* thread, bool prevent_shutdown)
253 : thread_(thread),
254 prevent_shutdown_(prevent_shutdown) {}
255
Create( MainThreadInterface* thread, bool prevent_shutdown)256 static std::unique_ptr<MainThreadSessionState> Create(
257 MainThreadInterface* thread, bool prevent_shutdown) {
258 return std::make_unique<MainThreadSessionState>(thread, prevent_shutdown);
259 }
260
Connect(std::unique_ptr<InspectorSessionDelegate> delegate)261 void Connect(std::unique_ptr<InspectorSessionDelegate> delegate) {
262 Agent* agent = thread_->inspector_agent();
263 if (agent != nullptr)
264 session_ = agent->Connect(std::move(delegate), prevent_shutdown_);
265 }
266
Dispatch(std::unique_ptr<StringBuffer> message)267 void Dispatch(std::unique_ptr<StringBuffer> message) {
268 session_->Dispatch(message->string());
269 }
270
271 private:
272 MainThreadInterface* thread_;
273 bool prevent_shutdown_;
274 std::unique_ptr<InspectorSession> session_;
275 };
276
277 class CrossThreadInspectorSession : public InspectorSession {
278 public:
CrossThreadInspectorSession( int id, std::shared_ptr<MainThreadHandle> thread, std::unique_ptr<InspectorSessionDelegate> delegate, bool prevent_shutdown)279 CrossThreadInspectorSession(
280 int id,
281 std::shared_ptr<MainThreadHandle> thread,
282 std::unique_ptr<InspectorSessionDelegate> delegate,
283 bool prevent_shutdown)
284 : state_(thread, std::bind(MainThreadSessionState::Create,
285 std::placeholders::_1,
286 prevent_shutdown)) {
287 state_.Call(&MainThreadSessionState::Connect, std::move(delegate));
288 }
289
290 void Dispatch(const StringView& message) override {
291 state_.Call(&MainThreadSessionState::Dispatch,
292 StringBuffer::create(message));
293 }
294
295 private:
296 AnotherThreadObjectReference<MainThreadSessionState> state_;
297 };
298
299 class ThreadSafeDelegate : public InspectorSessionDelegate {
300 public:
ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread, int object_id)301 ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread, int object_id)
302 : thread_(thread), delegate_(thread, object_id) {}
303
304 void SendMessageToFrontend(const v8_inspector::StringView& message) override {
305 delegate_.Call(
306 [m = StringBuffer::create(message)]
307 (InspectorSessionDelegate* delegate) {
308 delegate->SendMessageToFrontend(m->string());
309 });
310 }
311
312 private:
313 std::shared_ptr<MainThreadHandle> thread_;
314 AnotherThreadObjectReference<InspectorSessionDelegate> delegate_;
315 };
316
MainThreadInterface(Agent* agent)317 MainThreadInterface::MainThreadInterface(Agent* agent) : agent_(agent) {}
318
~MainThreadInterface()319 MainThreadInterface::~MainThreadInterface() {
320 if (handle_)
321 handle_->Reset();
322 }
323
Post(std::unique_ptr<Request> request)324 void MainThreadInterface::Post(std::unique_ptr<Request> request) {
325 CHECK_NOT_NULL(agent_);
326 Mutex::ScopedLock scoped_lock(requests_lock_);
327 bool needs_notify = requests_.empty();
328 requests_.push_back(std::move(request));
329 if (needs_notify) {
330 std::weak_ptr<MainThreadInterface> weak_self {shared_from_this()};
331 agent_->env()->RequestInterrupt([weak_self](Environment*) {
332 if (auto iface = weak_self.lock()) iface->DispatchMessages();
333 });
334 }
335 incoming_message_cond_.Broadcast(scoped_lock);
336 }
337
WaitForFrontendEvent()338 bool MainThreadInterface::WaitForFrontendEvent() {
339 // We allow DispatchMessages reentry as we enter the pause. This is important
340 // to support debugging the code invoked by an inspector call, such
341 // as Runtime.evaluate
342 dispatching_messages_ = false;
343 if (dispatching_message_queue_.empty()) {
344 Mutex::ScopedLock scoped_lock(requests_lock_);
345 while (requests_.empty()) incoming_message_cond_.Wait(scoped_lock);
346 }
347 return true;
348 }
349
DispatchMessages()350 void MainThreadInterface::DispatchMessages() {
351 if (dispatching_messages_)
352 return;
353 dispatching_messages_ = true;
354 bool had_messages = false;
355 do {
356 if (dispatching_message_queue_.empty()) {
357 Mutex::ScopedLock scoped_lock(requests_lock_);
358 requests_.swap(dispatching_message_queue_);
359 }
360 had_messages = !dispatching_message_queue_.empty();
361 while (!dispatching_message_queue_.empty()) {
362 MessageQueue::value_type task;
363 std::swap(dispatching_message_queue_.front(), task);
364 dispatching_message_queue_.pop_front();
365
366 v8::SealHandleScope seal_handle_scope(agent_->env()->isolate);
367 task->Call(this);
368 }
369 } while (had_messages);
370 dispatching_messages_ = false;
371 }
372
GetHandle()373 std::shared_ptr<MainThreadHandle> MainThreadInterface::GetHandle() {
374 if (handle_ == nullptr)
375 handle_ = std::make_shared<MainThreadHandle>(this);
376 return handle_;
377 }
378
AddObject(int id, std::unique_ptr<Deletable> object)379 void MainThreadInterface::AddObject(int id,
380 std::unique_ptr<Deletable> object) {
381 CHECK_NOT_NULL(object);
382 managed_objects_[id] = std::move(object);
383 }
384
RemoveObject(int id)385 void MainThreadInterface::RemoveObject(int id) {
386 CHECK_EQ(1, managed_objects_.erase(id));
387 }
388
GetObject(int id)389 Deletable* MainThreadInterface::GetObject(int id) {
390 Deletable* pointer = GetObjectIfExists(id);
391 // This would mean the object is requested after it was disposed, which is
392 // a coding error.
393 CHECK_NOT_NULL(pointer);
394 return pointer;
395 }
396
GetObjectIfExists(int id)397 Deletable* MainThreadInterface::GetObjectIfExists(int id) {
398 auto iterator = managed_objects_.find(id);
399 if (iterator == managed_objects_.end()) {
400 return nullptr;
401 }
402 return iterator->second.get();
403 }
404
Connect( std::unique_ptr<InspectorSessionDelegate> delegate, bool prevent_shutdown)405 std::unique_ptr<InspectorSession> MainThreadHandle::Connect(
406 std::unique_ptr<InspectorSessionDelegate> delegate,
407 bool prevent_shutdown) {
408 return std::unique_ptr<InspectorSession>(
409 new CrossThreadInspectorSession(++next_session_id_,
410 shared_from_this(),
411 std::move(delegate),
412 prevent_shutdown));
413 }
414
Post(std::unique_ptr<Request> request)415 bool MainThreadHandle::Post(std::unique_ptr<Request> request) {
416 Mutex::ScopedLock scoped_lock(block_lock_);
417 if (!main_thread_)
418 return false;
419 main_thread_->Post(std::move(request));
420 return true;
421 }
422
Reset()423 void MainThreadHandle::Reset() {
424 Mutex::ScopedLock scoped_lock(block_lock_);
425 main_thread_ = nullptr;
426 }
427 } // namespace
428
429 namespace {
430 using node::CheckedUvLoopClose;
431 using node::GetHumanReadableProcessName;
432 using node::InspectPublishUid;
433 using node::RegisterSignalHandler;
434 using node::inspector::FormatWsAddress;
435 using node::inspector::InspectorSocketServer;
436
437 namespace crypto = node::crypto;
438 namespace protocol = node::inspector::protocol;
439
440 // kKill closes connections and stops the server, kStop only stops the server
441 enum class TransportAction { kKill, kSendMessage, kStop };
442
ScriptPath(uv_loop_t* loop, const std::string& script_name)443 std::string ScriptPath(uv_loop_t* loop, const std::string& script_name) {
444 std::string script_path;
445
446 if (!script_name.empty()) {
447 uv_fs_t req;
448 req.ptr = nullptr;
449 if (0 == uv_fs_realpath(loop, &req, script_name.c_str(), nullptr)) {
450 CHECK_NOT_NULL(req.ptr);
451 script_path = std::string(static_cast<char*>(req.ptr));
452 }
453 uv_fs_req_cleanup(&req);
454 }
455
456 return script_path;
457 }
458
459 // UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
460 // Used ver 4 - with numbers
GenerateID()461 std::string GenerateID() {
462 uint16_t buffer[8];
463 CHECK(crypto::CSPRNG(buffer, sizeof(buffer)).is_ok());
464
465 char uuid[256];
466 snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
467 buffer[0], // time_low
468 buffer[1], // time_mid
469 buffer[2], // time_low
470 (buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version
471 (buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low
472 buffer[5], // node
473 buffer[6],
474 buffer[7]);
475 return uuid;
476 }
477
478 class RequestToServer {
479 public:
RequestToServer(TransportAction action, int session_id, std::unique_ptr<v8_inspector::StringBuffer> message)480 RequestToServer(TransportAction action,
481 int session_id,
482 std::unique_ptr<v8_inspector::StringBuffer> message)
483 : action_(action),
484 session_id_(session_id),
485 message_(std::move(message)) {}
486
Dispatch(InspectorSocketServer* server) const487 void Dispatch(InspectorSocketServer* server) const {
488 switch (action_) {
489 case TransportAction::kKill:
490 server->TerminateConnections();
491 [[fallthrough]];
492 case TransportAction::kStop:
493 server->Stop();
494 break;
495 case TransportAction::kSendMessage:
496 server->Send(
497 session_id_,
498 protocol::StringUtil::StringViewToUtf8(message_->string()));
499 break;
500 }
501 }
502
503 private:
504 TransportAction action_;
505 int session_id_;
506 std::unique_ptr<v8_inspector::StringBuffer> message_;
507 };
508
509 class RequestQueue;
510
511 class RequestQueueData {
512 public:
513 using MessageQueue = std::deque<RequestToServer>;
514
RequestQueueData(uv_loop_t* loop)515 explicit RequestQueueData(uv_loop_t* loop)
516 : handle_(std::make_shared<RequestQueue>(this)) {
517 int err = uv_async_init(loop, &async_, [](uv_async_t* async) {
518 RequestQueueData* wrapper =
519 node::ContainerOf(&RequestQueueData::async_, async);
520 wrapper->DoDispatch();
521 });
522 CHECK_EQ(0, err);
523 }
524
525 static void CloseAndFree(RequestQueueData* queue);
526
Post(int session_id, TransportAction action, std::unique_ptr<StringBuffer> message)527 void Post(int session_id,
528 TransportAction action,
529 std::unique_ptr<StringBuffer> message) {
530 Mutex::ScopedLock scoped_lock(state_lock_);
531 bool notify = messages_.empty();
532 messages_.emplace_back(action, session_id, std::move(message));
533 if (notify) {
534 CHECK_EQ(0, uv_async_send(&async_));
535 incoming_message_cond_.Broadcast(scoped_lock);
536 }
537 }
538
Wait()539 void Wait() {
540 Mutex::ScopedLock scoped_lock(state_lock_);
541 if (messages_.empty()) {
542 incoming_message_cond_.Wait(scoped_lock);
543 }
544 }
545
SetServer(InspectorSocketServer* server)546 void SetServer(InspectorSocketServer* server) {
547 server_ = server;
548 }
549
handle()550 std::shared_ptr<RequestQueue> handle() {
551 return handle_;
552 }
553
554 private:
555 ~RequestQueueData() = default;
556
GetMessages()557 MessageQueue GetMessages() {
558 Mutex::ScopedLock scoped_lock(state_lock_);
559 MessageQueue messages;
560 messages_.swap(messages);
561 return messages;
562 }
563
DoDispatch()564 void DoDispatch() {
565 if (server_ == nullptr)
566 return;
567 for (const auto& request : GetMessages()) {
568 request.Dispatch(server_);
569 }
570 }
571
572 std::shared_ptr<RequestQueue> handle_;
573 uv_async_t async_;
574 InspectorSocketServer* server_ = nullptr;
575 MessageQueue messages_;
576 Mutex state_lock_; // Locked before mutating the queue.
577 ConditionVariable incoming_message_cond_;
578 };
579
580 class RequestQueue {
581 public:
RequestQueue(RequestQueueData* data)582 explicit RequestQueue(RequestQueueData* data) : data_(data) {}
583
Reset()584 void Reset() {
585 Mutex::ScopedLock scoped_lock(lock_);
586 data_ = nullptr;
587 }
588
Post(int session_id, TransportAction action, std::unique_ptr<StringBuffer> message)589 void Post(int session_id,
590 TransportAction action,
591 std::unique_ptr<StringBuffer> message) {
592 Mutex::ScopedLock scoped_lock(lock_);
593 if (data_ != nullptr)
594 data_->Post(session_id, action, std::move(message));
595 }
596
Expired()597 bool Expired() {
598 Mutex::ScopedLock scoped_lock(lock_);
599 return data_ == nullptr;
600 }
601
602 private:
603 RequestQueueData* data_;
604 Mutex lock_;
605 };
606
607 class IoSessionDelegate : public InspectorSessionDelegate {
608 public:
IoSessionDelegate(std::shared_ptr<RequestQueue> queue, int id)609 explicit IoSessionDelegate(std::shared_ptr<RequestQueue> queue, int id)
610 : request_queue_(queue), id_(id) { }
611 void SendMessageToFrontend(const v8_inspector::StringView& message) override {
612 request_queue_->Post(id_, TransportAction::kSendMessage,
613 StringBuffer::create(message));
614 }
615
616 private:
617 std::shared_ptr<RequestQueue> request_queue_;
618 int id_;
619 };
620
621 // Passed to InspectorSocketServer to handle WS inspector protocol events,
622 // mostly session start, message received, and session end.
623 class InspectorIoDelegate: public node::inspector::SocketServerDelegate {
624 public:
625 InspectorIoDelegate(std::shared_ptr<RequestQueueData> queue,
626 std::shared_ptr<MainThreadHandle> main_thread,
627 const std::string& target_id,
628 const std::string& script_path,
629 const std::string& script_name);
630 ~InspectorIoDelegate() override = default;
631
632 void StartSession(int session_id, const std::string& target_id) override;
633 void MessageReceived(int session_id, const std::string& message) override;
634 void EndSession(int session_id) override;
635
636 std::vector<std::string> GetTargetIds() override;
637 std::string GetTargetTitle(const std::string& id) override;
638 std::string GetTargetUrl(const std::string& id) override;
639 void AssignServer(InspectorSocketServer* server) override {
640 request_queue_->SetServer(server);
641 }
642
643 private:
644 std::shared_ptr<RequestQueueData> request_queue_;
645 std::shared_ptr<MainThreadHandle> main_thread_;
646 std::unordered_map<int, std::unique_ptr<InspectorSession>> sessions_;
647 const std::string script_name_;
648 const std::string script_path_;
649 const std::string target_id_;
650 };
651
InspectorIoDelegate( std::shared_ptr<RequestQueueData> queue, std::shared_ptr<MainThreadHandle> main_thread, const std::string& target_id, const std::string& script_path, const std::string& script_name)652 InspectorIoDelegate::InspectorIoDelegate(
653 std::shared_ptr<RequestQueueData> queue,
654 std::shared_ptr<MainThreadHandle> main_thread,
655 const std::string& target_id,
656 const std::string& script_path,
657 const std::string& script_name)
658 : request_queue_(queue), main_thread_(main_thread),
659 script_name_(script_name), script_path_(script_path),
660 target_id_(target_id) {}
661
StartSession(int session_id, const std::string& target_id)662 void InspectorIoDelegate::StartSession(int session_id,
663 const std::string& target_id) {
664 auto session = main_thread_->Connect(
665 std::unique_ptr<InspectorSessionDelegate>(
666 new IoSessionDelegate(request_queue_->handle(), session_id)), true);
667 if (session) {
668 sessions_[session_id] = std::move(session);
669 fprintf(stderr, "Debugger attached.\n");
670 }
671 }
672
MessageReceived(int session_id, const std::string& message)673 void InspectorIoDelegate::MessageReceived(int session_id,
674 const std::string& message) {
675 auto session = sessions_.find(session_id);
676 if (session != sessions_.end())
677 session->second->Dispatch(Utf8ToStringView(message)->string());
678 }
679
EndSession(int session_id)680 void InspectorIoDelegate::EndSession(int session_id) {
681 sessions_.erase(session_id);
682 }
683
GetTargetIds()684 std::vector<std::string> InspectorIoDelegate::GetTargetIds() {
685 return { target_id_ };
686 }
687
GetTargetTitle(const std::string& id)688 std::string InspectorIoDelegate::GetTargetTitle(const std::string& id) {
689 return script_name_.empty() ? GetHumanReadableProcessName() : script_name_;
690 }
691
GetTargetUrl(const std::string& id)692 std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) {
693 return "file://" + script_path_;
694 }
695
696 // static
CloseAndFree(RequestQueueData* queue)697 void RequestQueueData::CloseAndFree(RequestQueueData* queue) {
698 queue->handle_->Reset();
699 queue->handle_.reset();
700 uv_close(reinterpret_cast<uv_handle_t*>(&queue->async_),
701 [](uv_handle_t* handle) {
702 uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
703 RequestQueueData* wrapper =
704 node::ContainerOf(&RequestQueueData::async_, async);
705 delete wrapper;
706 });
707 }
708 } // namespace
709
710 class InspectorIo {
711 public:
712 // Start the inspector agent thread, waiting for it to initialize
713 // bool Start();
714 // Returns empty pointer if thread was not started
715 static std::unique_ptr<InspectorIo> Start(
716 std::shared_ptr<MainThreadHandle> main_thread,
717 const std::string& path,
718 std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
719 const node::InspectPublishUid& inspect_publish_uid);
720
721 // Will block till the transport thread shuts down
722 ~InspectorIo();
723
724 void StopAcceptingNewConnections();
725 std::string GetWsUrl() const;
726
727 private:
728 InspectorIo(std::shared_ptr<MainThreadHandle> handle,
729 const std::string& path,
730 std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
731 const node::InspectPublishUid& inspect_publish_uid);
732
733 // Wrapper for agent->ThreadMain()
734 static void ThreadMain(void* agent);
735
736 // Runs a uv_loop_t
737 void ThreadMain();
738
739 // This is a thread-safe object that will post async tasks. It lives as long
740 // as an Inspector object lives (almost as long as an Isolate).
741 std::shared_ptr<MainThreadHandle> main_thread_;
742 // Used to post on a frontend interface thread, lives while the server is
743 // running
744 std::shared_ptr<RequestQueue> request_queue_;
745 std::shared_ptr<ExclusiveAccess<HostPort>> host_port_;
746 node::InspectPublishUid inspect_publish_uid_;
747
748 // The IO thread runs its own uv_loop to implement the TCP server off
749 // the main thread.
750 uv_thread_t thread_;
751
752 // For setting up interthread communications
753 Mutex thread_start_lock_;
754 node::ConditionVariable thread_start_condition_;
755 std::string script_name_;
756 // May be accessed from any thread
757 const std::string id_;
758 };
759
760 // static
Start( std::shared_ptr<MainThreadHandle> main_thread, const std::string& path, std::shared_ptr<ExclusiveAccess<HostPort>> host_port, const InspectPublishUid& inspect_publish_uid)761 std::unique_ptr<InspectorIo> InspectorIo::Start(
762 std::shared_ptr<MainThreadHandle> main_thread,
763 const std::string& path,
764 std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
765 const InspectPublishUid& inspect_publish_uid) {
766 auto io = std::unique_ptr<InspectorIo>(
767 new InspectorIo(main_thread,
768 path,
769 host_port,
770 inspect_publish_uid));
771 if (io->request_queue_->Expired()) { // Thread is not running
772 return nullptr;
773 }
774 return io;
775 }
776
InspectorIo(std::shared_ptr<MainThreadHandle> main_thread, const std::string& path, std::shared_ptr<ExclusiveAccess<HostPort>> host_port, const InspectPublishUid& inspect_publish_uid)777 InspectorIo::InspectorIo(std::shared_ptr<MainThreadHandle> main_thread,
778 const std::string& path,
779 std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
780 const InspectPublishUid& inspect_publish_uid)
781 : main_thread_(main_thread),
782 host_port_(host_port),
783 inspect_publish_uid_(inspect_publish_uid),
784 thread_(),
785 script_name_(path),
786 id_(GenerateID()) {
787 Mutex::ScopedLock scoped_lock(thread_start_lock_);
788 CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain, this), 0);
789 thread_start_condition_.Wait(scoped_lock);
790 }
791
~InspectorIo()792 InspectorIo::~InspectorIo() {
793 request_queue_->Post(0, TransportAction::kKill, nullptr);
794 int err = uv_thread_join(&thread_);
795 CHECK_EQ(err, 0);
796 }
797
StopAcceptingNewConnections()798 void InspectorIo::StopAcceptingNewConnections() {
799 request_queue_->Post(0, TransportAction::kStop, nullptr);
800 }
801
802 // static
ThreadMain(void* io)803 void InspectorIo::ThreadMain(void* io) {
804 static_cast<InspectorIo*>(io)->ThreadMain();
805 }
806
ThreadMain()807 void InspectorIo::ThreadMain() {
808 uv_loop_t loop;
809 loop.data = nullptr;
810 int err = uv_loop_init(&loop);
811 CHECK_EQ(err, 0);
812 std::shared_ptr<RequestQueueData> queue(new RequestQueueData(&loop),
813 RequestQueueData::CloseAndFree);
814 std::string script_path = ScriptPath(&loop, script_name_);
815 std::unique_ptr<InspectorIoDelegate> delegate(
816 new InspectorIoDelegate(queue, main_thread_, id_,
817 script_path, script_name_));
818 std::string host;
819 int port;
820 int pid;
821 {
822 ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
823 host = host_port->host();
824 port = host_port->port();
825 pid = host_port->pid();
826 }
827 InspectorSocketServer server(std::move(delegate),
828 &loop,
829 std::move(host),
830 port,
831 inspect_publish_uid_,
832 stderr,
833 pid);
834 request_queue_ = queue->handle();
835 // Its lifetime is now that of the server delegate
836 queue.reset();
837 {
838 Mutex::ScopedLock scoped_lock(thread_start_lock_);
839 if (server.Start()) {
840 ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
841 host_port->set_port(server.Port());
842 }
843 thread_start_condition_.Broadcast(scoped_lock);
844 }
845 uv_run(&loop, UV_RUN_DEFAULT);
846 CheckedUvLoopClose(&loop);
847 }
848
GetWsUrl() const849 std::string InspectorIo::GetWsUrl() const {
850 ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
851 return FormatWsAddress(host_port->host(), host_port->port(), id_, true);
852 }
853
854 namespace {
855
856 using node::DebugCategory;
857 using node::TwoByteValue;
858
859 using v8::Context;
860 using v8::Function;
861 using v8::HandleScope;
862 using v8::Isolate;
863 using v8::Local;
864 using v8::Message;
865 using v8::Object;
866 using v8::Value;
867
868 using v8_inspector::StringBuffer;
869 using v8_inspector::StringView;
870 using v8_inspector::V8Inspector;
871 using v8_inspector::V8InspectorClient;
872
873 namespace per_process = node::per_process;
874
ToProtocolString(Isolate* isolate, Local<Value> value)875 std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
876 Local<Value> value) {
877 TwoByteValue buffer(isolate, value);
878 return StringBuffer::create(StringView(*buffer, buffer.length()));
879 }
880
881
882 const int CONTEXT_GROUP_ID = 1;
883
GetWorkerLabel(Environment* env)884 std::string GetWorkerLabel(Environment* env) {
885 std::ostringstream result;
886 // TODO: use thread ID as part of worker label.
887 result << "Worker[" << "env->thread_id()" << "]";
888 return result.str();
889 }
890
891 class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
892 public protocol::FrontendChannel {
893 public:
ChannelImpl(const std::unique_ptr<V8Inspector>& inspector, std::unique_ptr<InspectorSessionDelegate> delegate, std::shared_ptr<MainThreadHandle> main_thread_, bool prevent_shutdown)894 explicit ChannelImpl(const std::unique_ptr<V8Inspector>& inspector,
895 std::unique_ptr<InspectorSessionDelegate> delegate,
896 std::shared_ptr<MainThreadHandle> main_thread_,
897 bool prevent_shutdown)
898 : delegate_(std::move(delegate)), prevent_shutdown_(prevent_shutdown),
899 retaining_context_(false) {
900 session_ = inspector->connect(CONTEXT_GROUP_ID,
901 this,
902 StringView(),
903 V8Inspector::ClientTrustLevel::kFullyTrusted);
904 node_dispatcher_ = std::make_unique<protocol::UberDispatcher>(this);
905 runtime_agent_ = std::make_unique<protocol::RuntimeAgent>();
906 runtime_agent_->Wire(node_dispatcher_.get());
907 }
908
909 ~ChannelImpl() override {
910 if (worker_agent_) {
911 worker_agent_->disable();
912 worker_agent_.reset(); // Dispose before the dispatchers
913 }
914 runtime_agent_->disable();
915 runtime_agent_.reset(); // Dispose before the dispatchers
916 }
917
dispatchProtocolMessage(const StringView& message)918 void dispatchProtocolMessage(const StringView& message) {
919 std::string raw_message = protocol::StringUtil::StringViewToUtf8(message);
920 per_process::Debug(DebugCategory::INSPECTOR_SERVER,
921 "[inspector received] %s\n",
922 raw_message);
923 std::unique_ptr<protocol::DictionaryValue> value =
924 protocol::DictionaryValue::cast(
925 protocol::StringUtil::parseJSON(message));
926 int call_id;
927 std::string method;
928 node_dispatcher_->parseCommand(value.get(), &call_id, &method);
929 if (v8_inspector::V8InspectorSession::canDispatchMethod(
930 Utf8ToStringView(method)->string())) {
931 session_->dispatchProtocolMessage(message);
932 } else {
933 node_dispatcher_->dispatch(call_id, method, std::move(value),
934 raw_message);
935 }
936 }
937
schedulePauseOnNextStatement(const std::string& reason)938 void schedulePauseOnNextStatement(const std::string& reason) {
939 std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
940 session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
941 }
942
preventShutdown()943 bool preventShutdown() {
944 return prevent_shutdown_;
945 }
946
notifyWaitingForDisconnect()947 bool notifyWaitingForDisconnect() {
948 retaining_context_ = runtime_agent_->notifyWaitingForDisconnect();
949 return retaining_context_;
950 }
951
retainingContext()952 bool retainingContext() {
953 return retaining_context_;
954 }
955
956 private:
957 void sendResponse(
958 int callId,
959 std::unique_ptr<v8_inspector::StringBuffer> message) override {
960 sendMessageToFrontend(message->string());
961 }
962
963 void sendNotification(
964 std::unique_ptr<v8_inspector::StringBuffer> message) override {
965 sendMessageToFrontend(message->string());
966 }
967
968 void flushProtocolNotifications() override { }
969
sendMessageToFrontend(const StringView& message)970 void sendMessageToFrontend(const StringView& message) {
971 if (per_process::enabled_debug_list.enabled(
972 DebugCategory::INSPECTOR_SERVER)) {
973 std::string raw_message = protocol::StringUtil::StringViewToUtf8(message);
974 per_process::Debug(DebugCategory::INSPECTOR_SERVER,
975 "[inspector send] %s\n",
976 raw_message);
977 }
978 delegate_->SendMessageToFrontend(message);
979 }
980
sendMessageToFrontend(const std::string& message)981 void sendMessageToFrontend(const std::string& message) {
982 sendMessageToFrontend(Utf8ToStringView(message)->string());
983 }
984
985 using Serializable = protocol::Serializable;
986
987 void sendProtocolResponse(int callId,
988 std::unique_ptr<Serializable> message) override {
989 sendMessageToFrontend(message->serializeToJSON());
990 }
991 void sendProtocolNotification(
992 std::unique_ptr<Serializable> message) override {
993 sendMessageToFrontend(message->serializeToJSON());
994 }
995
996 void fallThrough(int callId,
997 const std::string& method,
998 const std::string& message) override {
999 DCHECK(false);
1000 }
1001
1002 std::unique_ptr<protocol::RuntimeAgent> runtime_agent_;
1003 std::unique_ptr<protocol::WorkerAgent> worker_agent_;
1004 std::unique_ptr<InspectorSessionDelegate> delegate_;
1005 std::unique_ptr<v8_inspector::V8InspectorSession> session_;
1006 std::unique_ptr<protocol::UberDispatcher> node_dispatcher_;
1007 bool prevent_shutdown_;
1008 bool retaining_context_;
1009 };
1010
1011 class SameThreadInspectorSession : public InspectorSession {
1012 public:
SameThreadInspectorSession( int session_id, std::shared_ptr<InspectorClient> client)1013 SameThreadInspectorSession(
1014 int session_id, std::shared_ptr<InspectorClient> client)
1015 : session_id_(session_id), client_(client) {}
1016 ~SameThreadInspectorSession() override;
1017 void Dispatch(const v8_inspector::StringView& message) override;
1018
1019 private:
1020 int session_id_;
1021 std::weak_ptr<InspectorClient> client_;
1022 };
1023
IsFilePath(const std::string& path)1024 bool IsFilePath(const std::string& path) {
1025 return !path.empty() && path[0] == '/';
1026 }
1027
ThrowUninitializedInspectorError(Environment* env)1028 void ThrowUninitializedInspectorError(Environment* env) {
1029 HandleScope scope(env->isolate);
1030
1031 const char* msg = "This Environment was initialized without a V8::Inspector";
1032 Local<Value> exception =
1033 v8::String::NewFromUtf8(env->isolate, msg).ToLocalChecked();
1034
1035 env->isolate->ThrowException(exception);
1036 }
1037
1038 } // namespace
1039
1040 class InspectorClient : public V8InspectorClient {
1041 public:
InspectorClient(Environment* env, bool is_main)1042 explicit InspectorClient(Environment* env, bool is_main)
1043 : env_(env), is_main_(is_main) {
1044 client_ = V8Inspector::create(env->isolate, this);
1045 // TODO(bnoordhuis) Make name configurable from src/node.cc.
1046 std::string name =
1047 is_main_ ? GetHumanReadableProcessName() : GetWorkerLabel(env);
1048 ContextInfo info(name);
1049 info.is_default = true;
1050 contextCreated(env->context(), info);
1051 }
1052
1053 void runMessageLoopOnPause(int context_group_id) override {
1054 waiting_for_resume_ = true;
1055 runMessageLoop();
1056 }
1057
waitForSessionsDisconnect()1058 void waitForSessionsDisconnect() {
1059 waiting_for_sessions_disconnect_ = true;
1060 runMessageLoop();
1061 }
1062
waitForFrontend()1063 void waitForFrontend() {
1064 waiting_for_frontend_ = true;
1065 runMessageLoop();
1066 }
1067
1068 void maxAsyncCallStackDepthChanged(int depth) override {
1069 if (waiting_for_sessions_disconnect_) {
1070 // V8 isolate is mostly done and is only letting Inspector protocol
1071 // clients gather data.
1072 return;
1073 }
1074 }
1075
contextCreated(Local<Context> context, const ContextInfo& info)1076 void contextCreated(Local<Context> context, const ContextInfo& info) {
1077 auto name_buffer = Utf8ToStringView(info.name);
1078 auto origin_buffer = Utf8ToStringView(info.origin);
1079 std::unique_ptr<StringBuffer> aux_data_buffer;
1080
1081 v8_inspector::V8ContextInfo v8info(
1082 context, CONTEXT_GROUP_ID, name_buffer->string());
1083 v8info.origin = origin_buffer->string();
1084
1085 if (info.is_default) {
1086 aux_data_buffer = Utf8ToStringView("{\"isDefault\":true}");
1087 } else {
1088 aux_data_buffer = Utf8ToStringView("{\"isDefault\":false}");
1089 }
1090 v8info.auxData = aux_data_buffer->string();
1091
1092 client_->contextCreated(v8info);
1093 }
1094
contextDestroyed(Local<Context> context)1095 void contextDestroyed(Local<Context> context) {
1096 client_->contextDestroyed(context);
1097 }
1098
1099 void quitMessageLoopOnPause() override {
1100 waiting_for_resume_ = false;
1101 }
1102
1103 void runIfWaitingForDebugger(int context_group_id) override {
1104 waiting_for_frontend_ = false;
1105 }
1106
connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate, bool prevent_shutdown)1107 int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,
1108 bool prevent_shutdown) {
1109 int session_id = next_session_id_++;
1110 channels_[session_id] = std::make_unique<ChannelImpl>(client_,
1111 std::move(delegate),
1112 getThreadHandle(),
1113 prevent_shutdown);
1114 return session_id;
1115 }
1116
disconnectFrontend(int session_id)1117 void disconnectFrontend(int session_id) {
1118 auto it = channels_.find(session_id);
1119 if (it == channels_.end())
1120 return;
1121 bool retaining_context = it->second->retainingContext();
1122 channels_.erase(it);
1123 if (retaining_context) {
1124 for (const auto& id_channel : channels_) {
1125 if (id_channel.second->retainingContext())
1126 return;
1127 }
1128 contextDestroyed(env_->context());
1129 }
1130 if (waiting_for_sessions_disconnect_ && !is_main_)
1131 waiting_for_sessions_disconnect_ = false;
1132 }
1133
dispatchMessageFromFrontend(int session_id, const StringView& message)1134 void dispatchMessageFromFrontend(int session_id, const StringView& message) {
1135 channels_[session_id]->dispatchProtocolMessage(message);
1136 }
1137
1138 Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
1139 return env_->context();
1140 }
1141
ReportUncaughtException(Local<Value> error, Local<Message> message)1142 void ReportUncaughtException(Local<Value> error, Local<Message> message) {
1143 Isolate* isolate = env_->isolate;
1144 Local<Context> context = env_->context();
1145
1146 int script_id = message->GetScriptOrigin().ScriptId();
1147
1148 Local<v8::StackTrace> stack_trace = message->GetStackTrace();
1149
1150 if (!stack_trace.IsEmpty() && stack_trace->GetFrameCount() > 0 &&
1151 script_id == stack_trace->GetFrame(isolate, 0)->GetScriptId()) {
1152 script_id = 0;
1153 }
1154
1155 const uint8_t DETAILS[] = "Uncaught";
1156
1157 client_->exceptionThrown(
1158 context,
1159 StringView(DETAILS, sizeof(DETAILS) - 1),
1160 error,
1161 ToProtocolString(isolate, message->Get())->string(),
1162 ToProtocolString(isolate, message->GetScriptResourceName())->string(),
1163 message->GetLineNumber(context).FromMaybe(0),
1164 message->GetStartColumn(context).FromMaybe(0),
1165 client_->createStackTrace(stack_trace),
1166 script_id);
1167 }
1168
1169 void startRepeatingTimer(double interval_s,
1170 TimerCallback callback,
1171 void* data) override {
1172 // TODO: implement this for supporting heap profiler.
1173 }
1174
1175 void cancelTimer(void* data) override {
1176 // TODO: implement this for supporting heap profiler.
1177 }
1178
1179 // Async stack traces instrumentation.
AsyncTaskScheduled(const StringView& task_name, void* task, bool recurring)1180 void AsyncTaskScheduled(const StringView& task_name, void* task,
1181 bool recurring) {
1182 client_->asyncTaskScheduled(task_name, task, recurring);
1183 }
1184
AsyncTaskCanceled(void* task)1185 void AsyncTaskCanceled(void* task) {
1186 client_->asyncTaskCanceled(task);
1187 }
1188
AsyncTaskStarted(void* task)1189 void AsyncTaskStarted(void* task) {
1190 client_->asyncTaskStarted(task);
1191 }
1192
AsyncTaskFinished(void* task)1193 void AsyncTaskFinished(void* task) {
1194 client_->asyncTaskFinished(task);
1195 }
1196
AllAsyncTasksCanceled()1197 void AllAsyncTasksCanceled() {
1198 client_->allAsyncTasksCanceled();
1199 }
1200
schedulePauseOnNextStatement(const std::string& reason)1201 void schedulePauseOnNextStatement(const std::string& reason) {
1202 for (const auto& id_channel : channels_) {
1203 id_channel.second->schedulePauseOnNextStatement(reason);
1204 }
1205 }
1206
hasConnectedSessions()1207 bool hasConnectedSessions() {
1208 for (const auto& id_channel : channels_) {
1209 // Other sessions are "invisible" more most purposes
1210 if (id_channel.second->preventShutdown())
1211 return true;
1212 }
1213 return false;
1214 }
1215
notifyWaitingForDisconnect()1216 bool notifyWaitingForDisconnect() {
1217 bool retaining_context = false;
1218 for (const auto& id_channel : channels_) {
1219 if (id_channel.second->notifyWaitingForDisconnect())
1220 retaining_context = true;
1221 }
1222 return retaining_context;
1223 }
1224
getThreadHandle()1225 std::shared_ptr<MainThreadHandle> getThreadHandle() {
1226 if (!interface_) {
1227 interface_ = std::make_shared<MainThreadInterface>(
1228 env_->inspector_agent());
1229 }
1230 return interface_->GetHandle();
1231 }
1232
IsActive()1233 bool IsActive() {
1234 return !channels_.empty();
1235 }
1236
1237 private:
shouldRunMessageLoop()1238 bool shouldRunMessageLoop() {
1239 if (waiting_for_frontend_)
1240 return true;
1241 if (waiting_for_sessions_disconnect_ || waiting_for_resume_) {
1242 return hasConnectedSessions();
1243 }
1244 return false;
1245 }
1246
runMessageLoop()1247 void runMessageLoop() {
1248 if (running_nested_loop_)
1249 return;
1250
1251 running_nested_loop_ = true;
1252
1253 while (shouldRunMessageLoop()) {
1254 if (interface_) interface_->WaitForFrontendEvent();
1255 env_->RunAndClearInterrupts();
1256 }
1257 running_nested_loop_ = false;
1258 }
1259
1260 double currentTimeMS() override {
1261 return env_->platform()->CurrentClockTimeMillis();
1262 }
1263
1264 std::unique_ptr<StringBuffer> resourceNameToUrl(
1265 const StringView& resource_name_view) override {
1266 std::string resource_name =
1267 protocol::StringUtil::StringViewToUtf8(resource_name_view);
1268 if (!IsFilePath(resource_name))
1269 return nullptr;
1270
1271 std::string url = node::url::FromFilePath(resource_name);
1272 return Utf8ToStringView(url);
1273 }
1274
1275 Environment* env_;
1276 bool is_main_;
1277 bool running_nested_loop_ = false;
1278 std::unique_ptr<V8Inspector> client_;
1279 std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
1280 int next_session_id_ = 1;
1281 bool waiting_for_resume_ = false;
1282 bool waiting_for_frontend_ = false;
1283 bool waiting_for_sessions_disconnect_ = false;
1284 // Allows accessing Inspector from non-main threads
1285 std::shared_ptr<MainThreadInterface> interface_;
1286 };
1287
Agent(Environment* env)1288 Agent::Agent(Environment* env)
1289 : parent_env_(env) {}
1290
1291 Agent::~Agent() = default;
1292
Start(const std::string& path, std::shared_ptr<ExclusiveAccess<HostPort>> host_port, bool is_main, bool wait_for_connect)1293 bool Agent::Start(const std::string& path,
1294 std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
1295 bool is_main,
1296 bool wait_for_connect) {
1297 path_ = path;
1298 CHECK_NOT_NULL(host_port);
1299 host_port_ = host_port;
1300
1301 client_ = std::make_shared<InspectorClient>(parent_env_, is_main);
1302
1303 if (!StartIoThread()) {
1304 return false;
1305 }
1306
1307 if (wait_for_connect) {
1308 client_->waitForFrontend();
1309 }
1310 return true;
1311 }
1312
StartIoThread()1313 bool Agent::StartIoThread() {
1314 if (io_ != nullptr)
1315 return true;
1316
1317 if (!client_) {
1318 ThrowUninitializedInspectorError(parent_env_);
1319 return false;
1320 }
1321
1322 CHECK_NOT_NULL(client_);
1323
1324 io_ = InspectorIo::Start(client_->getThreadHandle(),
1325 path_,
1326 host_port_,
1327 { false, true });
1328 if (io_ == nullptr) {
1329 return false;
1330 }
1331 return true;
1332 }
1333
Stop()1334 void Agent::Stop() {
1335 io_.reset();
1336 }
1337
Connect( std::unique_ptr<InspectorSessionDelegate> delegate, bool prevent_shutdown)1338 std::unique_ptr<InspectorSession> Agent::Connect(
1339 std::unique_ptr<InspectorSessionDelegate> delegate,
1340 bool prevent_shutdown) {
1341 if (!client_) {
1342 ThrowUninitializedInspectorError(parent_env_);
1343 return std::unique_ptr<InspectorSession>{};
1344 }
1345
1346 CHECK_NOT_NULL(client_);
1347
1348 int session_id = client_->connectFrontend(std::move(delegate),
1349 prevent_shutdown);
1350 return std::unique_ptr<InspectorSession>(
1351 new SameThreadInspectorSession(session_id, client_));
1352 }
1353
WaitForDisconnect()1354 void Agent::WaitForDisconnect() {
1355 if (!client_) {
1356 ThrowUninitializedInspectorError(parent_env_);
1357 return;
1358 }
1359
1360 CHECK_NOT_NULL(client_);
1361 if (client_->hasConnectedSessions()) {
1362 fprintf(stderr, "Waiting for the debugger to disconnect...\n");
1363 fflush(stderr);
1364 }
1365 if (!client_->notifyWaitingForDisconnect()) {
1366 client_->contextDestroyed(parent_env_->context());
1367 }
1368 if (io_ != nullptr) {
1369 io_->StopAcceptingNewConnections();
1370 client_->waitForSessionsDisconnect();
1371 }
1372 }
1373
ReportUncaughtException(Local<Value> error, Local<Message> message)1374 void Agent::ReportUncaughtException(Local<Value> error,
1375 Local<Message> message) {
1376 if (!IsListening())
1377 return;
1378 client_->ReportUncaughtException(error, message);
1379 WaitForDisconnect();
1380 }
1381
PauseOnNextJavascriptStatement(const std::string& reason)1382 void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
1383 client_->schedulePauseOnNextStatement(reason);
1384 }
1385
AsyncTaskScheduled(const StringView& task_name, void* task, bool recurring)1386 void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
1387 bool recurring) {
1388 client_->AsyncTaskScheduled(task_name, task, recurring);
1389 }
1390
AsyncTaskCanceled(void* task)1391 void Agent::AsyncTaskCanceled(void* task) {
1392 client_->AsyncTaskCanceled(task);
1393 }
1394
AsyncTaskStarted(void* task)1395 void Agent::AsyncTaskStarted(void* task) {
1396 client_->AsyncTaskStarted(task);
1397 }
1398
AsyncTaskFinished(void* task)1399 void Agent::AsyncTaskFinished(void* task) {
1400 client_->AsyncTaskFinished(task);
1401 }
1402
AllAsyncTasksCanceled()1403 void Agent::AllAsyncTasksCanceled() {
1404 client_->AllAsyncTasksCanceled();
1405 }
1406
ContextCreated(Local<Context> context, const ContextInfo& info)1407 void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
1408 if (client_ == nullptr) // This happens for a main context
1409 return;
1410 client_->contextCreated(context, info);
1411 }
1412
IsActive()1413 bool Agent::IsActive() {
1414 if (client_ == nullptr)
1415 return false;
1416 return io_ != nullptr || client_->IsActive();
1417 }
1418
WaitForConnect()1419 void Agent::WaitForConnect() {
1420 if (!client_) {
1421 ThrowUninitializedInspectorError(parent_env_);
1422 return;
1423 }
1424
1425 CHECK_NOT_NULL(client_);
1426 client_->waitForFrontend();
1427 }
1428
GetWsUrl() const1429 std::string Agent::GetWsUrl() const {
1430 if (io_ == nullptr)
1431 return "";
1432 return io_->GetWsUrl();
1433 }
1434
~SameThreadInspectorSession()1435 SameThreadInspectorSession::~SameThreadInspectorSession() {
1436 auto client = client_.lock();
1437 if (client)
1438 client->disconnectFrontend(session_id_);
1439 }
1440
Dispatch( const v8_inspector::StringView& message)1441 void SameThreadInspectorSession::Dispatch(
1442 const v8_inspector::StringView& message) {
1443 auto client = client_.lock();
1444 if (client)
1445 client->dispatchMessageFromFrontend(session_id_, message);
1446 }
1447
1448 } // namespace v8impl
1449