• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Call(MainThreadInterface * thread)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 
Call(MainThreadInterface * thread)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 
Call(MainThreadInterface * thread)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 
Dispatch(const StringView & message)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 
SendMessageToFrontend(const v8_inspector::StringView & message)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) { }
SendMessageToFrontend(const v8_inspector::StringView & message)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;
AssignServer(InspectorSocketServer * server)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     node_dispatcher_ = std::make_unique<protocol::UberDispatcher>(this);
904     runtime_agent_ = std::make_unique<protocol::RuntimeAgent>();
905     runtime_agent_->Wire(node_dispatcher_.get());
906   }
907 
~ChannelImpl()908   ~ChannelImpl() override {
909     if (worker_agent_) {
910       worker_agent_->disable();
911       worker_agent_.reset();  // Dispose before the dispatchers
912     }
913     runtime_agent_->disable();
914     runtime_agent_.reset();  // Dispose before the dispatchers
915   }
916 
dispatchProtocolMessage(const StringView & message)917   void dispatchProtocolMessage(const StringView& message) {
918     std::string raw_message = protocol::StringUtil::StringViewToUtf8(message);
919     per_process::Debug(DebugCategory::INSPECTOR_SERVER,
920                        "[inspector received] %s\n",
921                        raw_message);
922     std::unique_ptr<protocol::DictionaryValue> value =
923         protocol::DictionaryValue::cast(
924             protocol::StringUtil::parseJSON(message));
925     int call_id;
926     std::string method;
927     node_dispatcher_->parseCommand(value.get(), &call_id, &method);
928     if (v8_inspector::V8InspectorSession::canDispatchMethod(
929             Utf8ToStringView(method)->string())) {
930       session_->dispatchProtocolMessage(message);
931     } else {
932       node_dispatcher_->dispatch(call_id, method, std::move(value),
933                                  raw_message);
934     }
935   }
936 
schedulePauseOnNextStatement(const std::string & reason)937   void schedulePauseOnNextStatement(const std::string& reason) {
938     std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
939     session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
940   }
941 
preventShutdown()942   bool preventShutdown() {
943     return prevent_shutdown_;
944   }
945 
notifyWaitingForDisconnect()946   bool notifyWaitingForDisconnect() {
947     retaining_context_ = runtime_agent_->notifyWaitingForDisconnect();
948     return retaining_context_;
949   }
950 
retainingContext()951   bool retainingContext() {
952     return retaining_context_;
953   }
954 
955  private:
sendResponse(int callId,std::unique_ptr<v8_inspector::StringBuffer> message)956   void sendResponse(
957       int callId,
958       std::unique_ptr<v8_inspector::StringBuffer> message) override {
959     sendMessageToFrontend(message->string());
960   }
961 
sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message)962   void sendNotification(
963       std::unique_ptr<v8_inspector::StringBuffer> message) override {
964     sendMessageToFrontend(message->string());
965   }
966 
flushProtocolNotifications()967   void flushProtocolNotifications() override { }
968 
sendMessageToFrontend(const StringView & message)969   void sendMessageToFrontend(const StringView& message) {
970     if (per_process::enabled_debug_list.enabled(
971             DebugCategory::INSPECTOR_SERVER)) {
972       std::string raw_message = protocol::StringUtil::StringViewToUtf8(message);
973       per_process::Debug(DebugCategory::INSPECTOR_SERVER,
974                          "[inspector send] %s\n",
975                          raw_message);
976     }
977     delegate_->SendMessageToFrontend(message);
978   }
979 
sendMessageToFrontend(const std::string & message)980   void sendMessageToFrontend(const std::string& message) {
981     sendMessageToFrontend(Utf8ToStringView(message)->string());
982   }
983 
984   using Serializable = protocol::Serializable;
985 
sendProtocolResponse(int callId,std::unique_ptr<Serializable> message)986   void sendProtocolResponse(int callId,
987                             std::unique_ptr<Serializable> message) override {
988     sendMessageToFrontend(message->serializeToJSON());
989   }
sendProtocolNotification(std::unique_ptr<Serializable> message)990   void sendProtocolNotification(
991       std::unique_ptr<Serializable> message) override {
992     sendMessageToFrontend(message->serializeToJSON());
993   }
994 
fallThrough(int callId,const std::string & method,const std::string & message)995   void fallThrough(int callId,
996                    const std::string& method,
997                    const std::string& message) override {
998     DCHECK(false);
999   }
1000 
1001   std::unique_ptr<protocol::RuntimeAgent> runtime_agent_;
1002   std::unique_ptr<protocol::WorkerAgent> worker_agent_;
1003   std::unique_ptr<InspectorSessionDelegate> delegate_;
1004   std::unique_ptr<v8_inspector::V8InspectorSession> session_;
1005   std::unique_ptr<protocol::UberDispatcher> node_dispatcher_;
1006   bool prevent_shutdown_;
1007   bool retaining_context_;
1008 };
1009 
1010 class SameThreadInspectorSession : public InspectorSession {
1011  public:
SameThreadInspectorSession(int session_id,std::shared_ptr<InspectorClient> client)1012   SameThreadInspectorSession(
1013       int session_id, std::shared_ptr<InspectorClient> client)
1014       : session_id_(session_id), client_(client) {}
1015   ~SameThreadInspectorSession() override;
1016   void Dispatch(const v8_inspector::StringView& message) override;
1017 
1018  private:
1019   int session_id_;
1020   std::weak_ptr<InspectorClient> client_;
1021 };
1022 
IsFilePath(const std::string & path)1023 bool IsFilePath(const std::string& path) {
1024   return !path.empty() && path[0] == '/';
1025 }
1026 
ThrowUninitializedInspectorError(Environment * env)1027 void ThrowUninitializedInspectorError(Environment* env) {
1028   HandleScope scope(env->isolate);
1029 
1030   const char* msg = "This Environment was initialized without a V8::Inspector";
1031   Local<Value> exception =
1032     v8::String::NewFromUtf8(env->isolate, msg).ToLocalChecked();
1033 
1034   env->isolate->ThrowException(exception);
1035 }
1036 
1037 }  // namespace
1038 
1039 class InspectorClient : public V8InspectorClient {
1040  public:
InspectorClient(Environment * env,bool is_main)1041   explicit InspectorClient(Environment* env, bool is_main)
1042       : env_(env), is_main_(is_main) {
1043     client_ = V8Inspector::create(env->isolate, this);
1044     // TODO(bnoordhuis) Make name configurable from src/node.cc.
1045     std::string name =
1046         is_main_ ? GetHumanReadableProcessName() : GetWorkerLabel(env);
1047     ContextInfo info(name);
1048     info.is_default = true;
1049     contextCreated(env->context(), info);
1050   }
1051 
runMessageLoopOnPause(int context_group_id)1052   void runMessageLoopOnPause(int context_group_id) override {
1053     waiting_for_resume_ = true;
1054     runMessageLoop();
1055   }
1056 
waitForSessionsDisconnect()1057   void waitForSessionsDisconnect() {
1058     waiting_for_sessions_disconnect_ = true;
1059     runMessageLoop();
1060   }
1061 
waitForFrontend()1062   void waitForFrontend() {
1063     waiting_for_frontend_ = true;
1064     runMessageLoop();
1065   }
1066 
maxAsyncCallStackDepthChanged(int depth)1067   void maxAsyncCallStackDepthChanged(int depth) override {
1068     if (waiting_for_sessions_disconnect_) {
1069       // V8 isolate is mostly done and is only letting Inspector protocol
1070       // clients gather data.
1071       return;
1072     }
1073   }
1074 
contextCreated(Local<Context> context,const ContextInfo & info)1075   void contextCreated(Local<Context> context, const ContextInfo& info) {
1076     auto name_buffer = Utf8ToStringView(info.name);
1077     auto origin_buffer = Utf8ToStringView(info.origin);
1078     std::unique_ptr<StringBuffer> aux_data_buffer;
1079 
1080     v8_inspector::V8ContextInfo v8info(
1081         context, CONTEXT_GROUP_ID, name_buffer->string());
1082     v8info.origin = origin_buffer->string();
1083 
1084     if (info.is_default) {
1085       aux_data_buffer = Utf8ToStringView("{\"isDefault\":true}");
1086     } else {
1087       aux_data_buffer = Utf8ToStringView("{\"isDefault\":false}");
1088     }
1089     v8info.auxData = aux_data_buffer->string();
1090 
1091     client_->contextCreated(v8info);
1092   }
1093 
contextDestroyed(Local<Context> context)1094   void contextDestroyed(Local<Context> context) {
1095     client_->contextDestroyed(context);
1096   }
1097 
quitMessageLoopOnPause()1098   void quitMessageLoopOnPause() override {
1099     waiting_for_resume_ = false;
1100   }
1101 
runIfWaitingForDebugger(int context_group_id)1102   void runIfWaitingForDebugger(int context_group_id) override {
1103     waiting_for_frontend_ = false;
1104   }
1105 
connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)1106   int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,
1107                       bool prevent_shutdown) {
1108     int session_id = next_session_id_++;
1109     channels_[session_id] = std::make_unique<ChannelImpl>(client_,
1110                                                           std::move(delegate),
1111                                                           getThreadHandle(),
1112                                                           prevent_shutdown);
1113     return session_id;
1114   }
1115 
disconnectFrontend(int session_id)1116   void disconnectFrontend(int session_id) {
1117     auto it = channels_.find(session_id);
1118     if (it == channels_.end())
1119       return;
1120     bool retaining_context = it->second->retainingContext();
1121     channels_.erase(it);
1122     if (retaining_context) {
1123       for (const auto& id_channel : channels_) {
1124         if (id_channel.second->retainingContext())
1125           return;
1126       }
1127       contextDestroyed(env_->context());
1128     }
1129     if (waiting_for_sessions_disconnect_ && !is_main_)
1130       waiting_for_sessions_disconnect_ = false;
1131   }
1132 
dispatchMessageFromFrontend(int session_id,const StringView & message)1133   void dispatchMessageFromFrontend(int session_id, const StringView& message) {
1134     channels_[session_id]->dispatchProtocolMessage(message);
1135   }
1136 
ensureDefaultContextInGroup(int contextGroupId)1137   Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
1138     return env_->context();
1139   }
1140 
ReportUncaughtException(Local<Value> error,Local<Message> message)1141   void ReportUncaughtException(Local<Value> error, Local<Message> message) {
1142     Isolate* isolate = env_->isolate;
1143     Local<Context> context = env_->context();
1144 
1145     int script_id = message->GetScriptOrigin().ScriptId();
1146 
1147     Local<v8::StackTrace> stack_trace = message->GetStackTrace();
1148 
1149     if (!stack_trace.IsEmpty() && stack_trace->GetFrameCount() > 0 &&
1150         script_id == stack_trace->GetFrame(isolate, 0)->GetScriptId()) {
1151       script_id = 0;
1152     }
1153 
1154     const uint8_t DETAILS[] = "Uncaught";
1155 
1156     client_->exceptionThrown(
1157         context,
1158         StringView(DETAILS, sizeof(DETAILS) - 1),
1159         error,
1160         ToProtocolString(isolate, message->Get())->string(),
1161         ToProtocolString(isolate, message->GetScriptResourceName())->string(),
1162         message->GetLineNumber(context).FromMaybe(0),
1163         message->GetStartColumn(context).FromMaybe(0),
1164         client_->createStackTrace(stack_trace),
1165         script_id);
1166   }
1167 
startRepeatingTimer(double interval_s,TimerCallback callback,void * data)1168   void startRepeatingTimer(double interval_s,
1169                            TimerCallback callback,
1170                            void* data) override {
1171     // TODO: implement this for supporting heap profiler.
1172   }
1173 
cancelTimer(void * data)1174   void cancelTimer(void* data) override {
1175     // TODO: implement this for supporting heap profiler.
1176   }
1177 
1178   // Async stack traces instrumentation.
AsyncTaskScheduled(const StringView & task_name,void * task,bool recurring)1179   void AsyncTaskScheduled(const StringView& task_name, void* task,
1180                           bool recurring) {
1181     client_->asyncTaskScheduled(task_name, task, recurring);
1182   }
1183 
AsyncTaskCanceled(void * task)1184   void AsyncTaskCanceled(void* task) {
1185     client_->asyncTaskCanceled(task);
1186   }
1187 
AsyncTaskStarted(void * task)1188   void AsyncTaskStarted(void* task) {
1189     client_->asyncTaskStarted(task);
1190   }
1191 
AsyncTaskFinished(void * task)1192   void AsyncTaskFinished(void* task) {
1193     client_->asyncTaskFinished(task);
1194   }
1195 
AllAsyncTasksCanceled()1196   void AllAsyncTasksCanceled() {
1197     client_->allAsyncTasksCanceled();
1198   }
1199 
schedulePauseOnNextStatement(const std::string & reason)1200   void schedulePauseOnNextStatement(const std::string& reason) {
1201     for (const auto& id_channel : channels_) {
1202       id_channel.second->schedulePauseOnNextStatement(reason);
1203     }
1204   }
1205 
hasConnectedSessions()1206   bool hasConnectedSessions() {
1207     for (const auto& id_channel : channels_) {
1208       // Other sessions are "invisible" more most purposes
1209       if (id_channel.second->preventShutdown())
1210         return true;
1211     }
1212     return false;
1213   }
1214 
notifyWaitingForDisconnect()1215   bool notifyWaitingForDisconnect() {
1216     bool retaining_context = false;
1217     for (const auto& id_channel : channels_) {
1218       if (id_channel.second->notifyWaitingForDisconnect())
1219         retaining_context = true;
1220     }
1221     return retaining_context;
1222   }
1223 
getThreadHandle()1224   std::shared_ptr<MainThreadHandle> getThreadHandle() {
1225     if (!interface_) {
1226       interface_ = std::make_shared<MainThreadInterface>(
1227           env_->inspector_agent());
1228     }
1229     return interface_->GetHandle();
1230   }
1231 
IsActive()1232   bool IsActive() {
1233     return !channels_.empty();
1234   }
1235 
1236  private:
shouldRunMessageLoop()1237   bool shouldRunMessageLoop() {
1238     if (waiting_for_frontend_)
1239       return true;
1240     if (waiting_for_sessions_disconnect_ || waiting_for_resume_) {
1241       return hasConnectedSessions();
1242     }
1243     return false;
1244   }
1245 
runMessageLoop()1246   void runMessageLoop() {
1247     if (running_nested_loop_)
1248       return;
1249 
1250     running_nested_loop_ = true;
1251 
1252     while (shouldRunMessageLoop()) {
1253       if (interface_) interface_->WaitForFrontendEvent();
1254       env_->RunAndClearInterrupts();
1255     }
1256     running_nested_loop_ = false;
1257   }
1258 
currentTimeMS()1259   double currentTimeMS() override {
1260     return env_->platform()->CurrentClockTimeMillis();
1261   }
1262 
resourceNameToUrl(const StringView & resource_name_view)1263   std::unique_ptr<StringBuffer> resourceNameToUrl(
1264       const StringView& resource_name_view) override {
1265     std::string resource_name =
1266         protocol::StringUtil::StringViewToUtf8(resource_name_view);
1267     if (!IsFilePath(resource_name))
1268       return nullptr;
1269 
1270     std::string url = node::url::FromFilePath(resource_name);
1271     return Utf8ToStringView(url);
1272   }
1273 
1274   Environment* env_;
1275   bool is_main_;
1276   bool running_nested_loop_ = false;
1277   std::unique_ptr<V8Inspector> client_;
1278   std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
1279   int next_session_id_ = 1;
1280   bool waiting_for_resume_ = false;
1281   bool waiting_for_frontend_ = false;
1282   bool waiting_for_sessions_disconnect_ = false;
1283   // Allows accessing Inspector from non-main threads
1284   std::shared_ptr<MainThreadInterface> interface_;
1285 };
1286 
Agent(Environment * env)1287 Agent::Agent(Environment* env)
1288     : parent_env_(env) {}
1289 
1290 Agent::~Agent() = default;
1291 
Start(const std::string & path,std::shared_ptr<ExclusiveAccess<HostPort>> host_port,bool is_main,bool wait_for_connect)1292 bool Agent::Start(const std::string& path,
1293                   std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
1294                   bool is_main,
1295                   bool wait_for_connect) {
1296   path_ = path;
1297   CHECK_NOT_NULL(host_port);
1298   host_port_ = host_port;
1299 
1300   client_ = std::make_shared<InspectorClient>(parent_env_, is_main);
1301 
1302   if (!StartIoThread()) {
1303     return false;
1304   }
1305 
1306   if (wait_for_connect) {
1307     client_->waitForFrontend();
1308   }
1309   return true;
1310 }
1311 
StartIoThread()1312 bool Agent::StartIoThread() {
1313   if (io_ != nullptr)
1314     return true;
1315 
1316   if (!client_) {
1317     ThrowUninitializedInspectorError(parent_env_);
1318     return false;
1319   }
1320 
1321   CHECK_NOT_NULL(client_);
1322 
1323   io_ = InspectorIo::Start(client_->getThreadHandle(),
1324                            path_,
1325                            host_port_,
1326                            { false, true });
1327   if (io_ == nullptr) {
1328     return false;
1329   }
1330   return true;
1331 }
1332 
Stop()1333 void Agent::Stop() {
1334   io_.reset();
1335 }
1336 
Connect(std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)1337 std::unique_ptr<InspectorSession> Agent::Connect(
1338     std::unique_ptr<InspectorSessionDelegate> delegate,
1339     bool prevent_shutdown) {
1340   if (!client_) {
1341     ThrowUninitializedInspectorError(parent_env_);
1342     return std::unique_ptr<InspectorSession>{};
1343   }
1344 
1345   CHECK_NOT_NULL(client_);
1346 
1347   int session_id = client_->connectFrontend(std::move(delegate),
1348                                             prevent_shutdown);
1349   return std::unique_ptr<InspectorSession>(
1350       new SameThreadInspectorSession(session_id, client_));
1351 }
1352 
WaitForDisconnect()1353 void Agent::WaitForDisconnect() {
1354   if (!client_) {
1355     ThrowUninitializedInspectorError(parent_env_);
1356     return;
1357   }
1358 
1359   CHECK_NOT_NULL(client_);
1360   if (client_->hasConnectedSessions()) {
1361     fprintf(stderr, "Waiting for the debugger to disconnect...\n");
1362     fflush(stderr);
1363   }
1364   if (!client_->notifyWaitingForDisconnect()) {
1365     client_->contextDestroyed(parent_env_->context());
1366   }
1367   if (io_ != nullptr) {
1368     io_->StopAcceptingNewConnections();
1369     client_->waitForSessionsDisconnect();
1370   }
1371 }
1372 
ReportUncaughtException(Local<Value> error,Local<Message> message)1373 void Agent::ReportUncaughtException(Local<Value> error,
1374                                     Local<Message> message) {
1375   if (!IsListening())
1376     return;
1377   client_->ReportUncaughtException(error, message);
1378   WaitForDisconnect();
1379 }
1380 
PauseOnNextJavascriptStatement(const std::string & reason)1381 void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
1382   client_->schedulePauseOnNextStatement(reason);
1383 }
1384 
AsyncTaskScheduled(const StringView & task_name,void * task,bool recurring)1385 void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
1386                                bool recurring) {
1387   client_->AsyncTaskScheduled(task_name, task, recurring);
1388 }
1389 
AsyncTaskCanceled(void * task)1390 void Agent::AsyncTaskCanceled(void* task) {
1391   client_->AsyncTaskCanceled(task);
1392 }
1393 
AsyncTaskStarted(void * task)1394 void Agent::AsyncTaskStarted(void* task) {
1395   client_->AsyncTaskStarted(task);
1396 }
1397 
AsyncTaskFinished(void * task)1398 void Agent::AsyncTaskFinished(void* task) {
1399   client_->AsyncTaskFinished(task);
1400 }
1401 
AllAsyncTasksCanceled()1402 void Agent::AllAsyncTasksCanceled() {
1403   client_->AllAsyncTasksCanceled();
1404 }
1405 
ContextCreated(Local<Context> context,const ContextInfo & info)1406 void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
1407   if (client_ == nullptr)  // This happens for a main context
1408     return;
1409   client_->contextCreated(context, info);
1410 }
1411 
IsActive()1412 bool Agent::IsActive() {
1413   if (client_ == nullptr)
1414     return false;
1415   return io_ != nullptr || client_->IsActive();
1416 }
1417 
WaitForConnect()1418 void Agent::WaitForConnect() {
1419   if (!client_) {
1420     ThrowUninitializedInspectorError(parent_env_);
1421     return;
1422   }
1423 
1424   CHECK_NOT_NULL(client_);
1425   client_->waitForFrontend();
1426 }
1427 
GetWsUrl() const1428 std::string Agent::GetWsUrl() const {
1429   if (io_ == nullptr)
1430     return "";
1431   return io_->GetWsUrl();
1432 }
1433 
~SameThreadInspectorSession()1434 SameThreadInspectorSession::~SameThreadInspectorSession() {
1435   auto client = client_.lock();
1436   if (client)
1437     client->disconnectFrontend(session_id_);
1438 }
1439 
Dispatch(const v8_inspector::StringView & message)1440 void SameThreadInspectorSession::Dispatch(
1441     const v8_inspector::StringView& message) {
1442   auto client = client_.lock();
1443   if (client)
1444     client->dispatchMessageFromFrontend(session_id_, message);
1445 }
1446 
1447 }  // namespace v8impl
1448