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