1 #include "inspector_agent.h"
2 
3 #include "inspector_io.h"
4 #include "inspector/main_thread_interface.h"
5 #include "inspector/node_string.h"
6 #include "inspector/tracing_agent.h"
7 #include "inspector/worker_agent.h"
8 #include "inspector/worker_inspector.h"
9 #include "node/inspector/protocol/Protocol.h"
10 #include "node_internals.h"
11 #include "node_url.h"
12 #include "v8-inspector.h"
13 #include "v8-platform.h"
14 
15 #include "libplatform/libplatform.h"
16 
17 #include <algorithm>
18 #include <string.h>
19 #include <sstream>
20 #include <unordered_map>
21 #include <vector>
22 
23 #ifdef __POSIX__
24 #include <limits.h>  // PTHREAD_STACK_MIN
25 #include <pthread.h>
26 #endif  // __POSIX__
27 
28 namespace node {
29 namespace inspector {
30 namespace {
31 
32 using node::FatalError;
33 
34 using v8::Array;
35 using v8::Context;
36 using v8::Function;
37 using v8::HandleScope;
38 using v8::Isolate;
39 using v8::Local;
40 using v8::Message;
41 using v8::Object;
42 using v8::String;
43 using v8::Task;
44 using v8::TaskRunner;
45 using v8::Value;
46 
47 using v8_inspector::StringBuffer;
48 using v8_inspector::StringView;
49 using v8_inspector::V8Inspector;
50 using v8_inspector::V8InspectorClient;
51 
52 static uv_sem_t start_io_thread_semaphore;
53 static uv_async_t start_io_thread_async;
54 
55 class StartIoTask : public Task {
56  public:
StartIoTask(Agent * agent)57   explicit StartIoTask(Agent* agent) : agent(agent) {}
58 
Run()59   void Run() override {
60     agent->StartIoThread();
61   }
62 
63  private:
64   Agent* agent;
65 };
66 
ToProtocolString(Isolate * isolate,Local<Value> value)67 std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
68                                                Local<Value> value) {
69   TwoByteValue buffer(isolate, value);
70   return StringBuffer::create(StringView(*buffer, buffer.length()));
71 }
72 
73 // Called on the main thread.
StartIoThreadAsyncCallback(uv_async_t * handle)74 void StartIoThreadAsyncCallback(uv_async_t* handle) {
75   static_cast<Agent*>(handle->data)->StartIoThread();
76 }
77 
StartIoInterrupt(Isolate * isolate,void * agent)78 void StartIoInterrupt(Isolate* isolate, void* agent) {
79   static_cast<Agent*>(agent)->StartIoThread();
80 }
81 
82 
83 #ifdef __POSIX__
StartIoThreadWakeup(int signo)84 static void StartIoThreadWakeup(int signo) {
85   uv_sem_post(&start_io_thread_semaphore);
86 }
87 
StartIoThreadMain(void * unused)88 inline void* StartIoThreadMain(void* unused) {
89   for (;;) {
90     uv_sem_wait(&start_io_thread_semaphore);
91     Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
92     if (agent != nullptr)
93       agent->RequestIoThreadStart();
94   }
95   return nullptr;
96 }
97 
StartDebugSignalHandler()98 static int StartDebugSignalHandler() {
99   // Start a watchdog thread for calling v8::Debug::DebugBreak() because
100   // it's not safe to call directly from the signal handler, it can
101   // deadlock with the thread it interrupts.
102   CHECK_EQ(0, uv_sem_init(&start_io_thread_semaphore, 0));
103   pthread_attr_t attr;
104   CHECK_EQ(0, pthread_attr_init(&attr));
105 #if defined(PTHREAD_STACK_MIN) && !defined(__FreeBSD__)
106   // PTHREAD_STACK_MIN is 2 KB with musl libc, which is too small to safely
107   // receive signals. PTHREAD_STACK_MIN + MINSIGSTKSZ is 8 KB on arm64, which
108   // is the musl architecture with the biggest MINSIGSTKSZ so let's use that
109   // as a lower bound and let's quadruple it just in case. The goal is to avoid
110   // creating a big 2 or 4 MB address space gap (problematic on 32 bits
111   // because of fragmentation), not squeeze out every last byte.
112   // Omitted on FreeBSD because it doesn't seem to like small stacks.
113   const size_t stack_size = std::max(static_cast<size_t>(4 * 8192),
114                                      static_cast<size_t>(PTHREAD_STACK_MIN));
115   CHECK_EQ(0, pthread_attr_setstacksize(&attr, stack_size));
116 #endif  // defined(PTHREAD_STACK_MIN) && !defined(__FreeBSD__)
117   CHECK_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
118   sigset_t sigmask;
119   // Mask all signals.
120   sigfillset(&sigmask);
121   sigset_t savemask;
122   CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &savemask));
123   sigmask = savemask;
124   pthread_t thread;
125   const int err = pthread_create(&thread, &attr,
126                                  StartIoThreadMain, nullptr);
127   // Restore original mask
128   CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
129   CHECK_EQ(0, pthread_attr_destroy(&attr));
130   if (err != 0) {
131     fprintf(stderr, "node[%u]: pthread_create: %s\n",
132             uv_os_getpid(), strerror(err));
133     fflush(stderr);
134     // Leave SIGUSR1 blocked.  We don't install a signal handler,
135     // receiving the signal would terminate the process.
136     return -err;
137   }
138   RegisterSignalHandler(SIGUSR1, StartIoThreadWakeup);
139   // Unblock SIGUSR1.  A pending SIGUSR1 signal will now be delivered.
140   sigemptyset(&sigmask);
141   sigaddset(&sigmask, SIGUSR1);
142   CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr));
143   return 0;
144 }
145 #endif  // __POSIX__
146 
147 
148 #ifdef _WIN32
StartIoThreadProc(void * arg)149 DWORD WINAPI StartIoThreadProc(void* arg) {
150   Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
151   if (agent != nullptr)
152     agent->RequestIoThreadStart();
153   return 0;
154 }
155 
GetDebugSignalHandlerMappingName(DWORD pid,wchar_t * buf,size_t buf_len)156 static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf,
157                                             size_t buf_len) {
158   return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid);
159 }
160 
StartDebugSignalHandler()161 static int StartDebugSignalHandler() {
162   wchar_t mapping_name[32];
163   HANDLE mapping_handle;
164   DWORD pid;
165   LPTHREAD_START_ROUTINE* handler;
166 
167   pid = uv_os_getpid();
168 
169   if (GetDebugSignalHandlerMappingName(pid,
170                                        mapping_name,
171                                        arraysize(mapping_name)) < 0) {
172     return -1;
173   }
174 
175   mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE,
176                                       nullptr,
177                                       PAGE_READWRITE,
178                                       0,
179                                       sizeof *handler,
180                                       mapping_name);
181   if (mapping_handle == nullptr) {
182     return -1;
183   }
184 
185   handler = reinterpret_cast<LPTHREAD_START_ROUTINE*>(
186       MapViewOfFile(mapping_handle,
187                     FILE_MAP_ALL_ACCESS,
188                     0,
189                     0,
190                     sizeof *handler));
191   if (handler == nullptr) {
192     CloseHandle(mapping_handle);
193     return -1;
194   }
195 
196   *handler = StartIoThreadProc;
197 
198   UnmapViewOfFile(static_cast<void*>(handler));
199 
200   return 0;
201 }
202 #endif  // _WIN32
203 
204 
205 const int CONTEXT_GROUP_ID = 1;
206 
GetWorkerLabel(node::Environment * env)207 std::string GetWorkerLabel(node::Environment* env) {
208   std::ostringstream result;
209   result << "Worker[" << env->thread_id() << "]";
210   return result.str();
211 }
212 
213 class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
214                           public protocol::FrontendChannel {
215  public:
ChannelImpl(Environment * env,const std::unique_ptr<V8Inspector> & inspector,std::shared_ptr<WorkerManager> worker_manager,std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)216   explicit ChannelImpl(Environment* env,
217                        const std::unique_ptr<V8Inspector>& inspector,
218                        std::shared_ptr<WorkerManager> worker_manager,
219                        std::unique_ptr<InspectorSessionDelegate> delegate,
220                        bool prevent_shutdown)
221                        : delegate_(std::move(delegate)),
222                          prevent_shutdown_(prevent_shutdown) {
223     session_ = inspector->connect(1, this, StringView());
224     node_dispatcher_.reset(new protocol::UberDispatcher(this));
225     tracing_agent_.reset(new protocol::TracingAgent(env));
226     tracing_agent_->Wire(node_dispatcher_.get());
227     worker_agent_.reset(new protocol::WorkerAgent(worker_manager));
228     worker_agent_->Wire(node_dispatcher_.get());
229   }
230 
~ChannelImpl()231   virtual ~ChannelImpl() {
232     tracing_agent_->disable();
233     tracing_agent_.reset();  // Dispose before the dispatchers
234     worker_agent_->disable();
235     worker_agent_.reset();  // Dispose before the dispatchers
236   }
237 
dispatchProtocolMessage(const StringView & message)238   std::string dispatchProtocolMessage(const StringView& message) {
239     std::string raw_message = protocol::StringUtil::StringViewToUtf8(message);
240     std::unique_ptr<protocol::DictionaryValue> value =
241         protocol::DictionaryValue::cast(protocol::StringUtil::parseMessage(
242             raw_message, false));
243     int call_id;
244     std::string method;
245     node_dispatcher_->parseCommand(value.get(), &call_id, &method);
246     if (v8_inspector::V8InspectorSession::canDispatchMethod(
247             Utf8ToStringView(method)->string())) {
248       session_->dispatchProtocolMessage(message);
249     } else {
250       node_dispatcher_->dispatch(call_id, method, std::move(value),
251                                  raw_message);
252     }
253     return method;
254   }
255 
schedulePauseOnNextStatement(const std::string & reason)256   void schedulePauseOnNextStatement(const std::string& reason) {
257     std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
258     session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
259   }
260 
preventShutdown()261   bool preventShutdown() {
262     return prevent_shutdown_;
263   }
264 
265  private:
sendResponse(int callId,std::unique_ptr<v8_inspector::StringBuffer> message)266   void sendResponse(
267       int callId,
268       std::unique_ptr<v8_inspector::StringBuffer> message) override {
269     sendMessageToFrontend(message->string());
270   }
271 
sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message)272   void sendNotification(
273       std::unique_ptr<v8_inspector::StringBuffer> message) override {
274     sendMessageToFrontend(message->string());
275   }
276 
flushProtocolNotifications()277   void flushProtocolNotifications() override { }
278 
sendMessageToFrontend(const StringView & message)279   void sendMessageToFrontend(const StringView& message) {
280     delegate_->SendMessageToFrontend(message);
281   }
282 
sendMessageToFrontend(const std::string & message)283   void sendMessageToFrontend(const std::string& message) {
284     sendMessageToFrontend(Utf8ToStringView(message)->string());
285   }
286 
287   using Serializable = protocol::Serializable;
288 
sendProtocolResponse(int callId,std::unique_ptr<Serializable> message)289   void sendProtocolResponse(int callId,
290                             std::unique_ptr<Serializable> message) override {
291     sendMessageToFrontend(message->serializeToJSON());
292   }
sendProtocolNotification(std::unique_ptr<Serializable> message)293   void sendProtocolNotification(
294       std::unique_ptr<Serializable> message) override {
295     sendMessageToFrontend(message->serializeToJSON());
296   }
297 
fallThrough(int callId,const std::string & method,const std::string & message)298   void fallThrough(int callId,
299                    const std::string& method,
300                    const std::string& message) override {
301     DCHECK(false);
302   }
303 
304   std::unique_ptr<protocol::TracingAgent> tracing_agent_;
305   std::unique_ptr<protocol::WorkerAgent> worker_agent_;
306   std::unique_ptr<InspectorSessionDelegate> delegate_;
307   std::unique_ptr<v8_inspector::V8InspectorSession> session_;
308   std::unique_ptr<protocol::UberDispatcher> node_dispatcher_;
309   bool prevent_shutdown_;
310 };
311 
312 class InspectorTimer {
313  public:
InspectorTimer(uv_loop_t * loop,double interval_s,V8InspectorClient::TimerCallback callback,void * data)314   InspectorTimer(uv_loop_t* loop,
315                  double interval_s,
316                  V8InspectorClient::TimerCallback callback,
317                  void* data) : timer_(),
318                                callback_(callback),
319                                data_(data) {
320     uv_timer_init(loop, &timer_);
321     int64_t interval_ms = 1000 * interval_s;
322     uv_timer_start(&timer_, OnTimer, interval_ms, interval_ms);
323   }
324 
325   InspectorTimer(const InspectorTimer&) = delete;
326 
Stop()327   void Stop() {
328     uv_timer_stop(&timer_);
329     uv_close(reinterpret_cast<uv_handle_t*>(&timer_), TimerClosedCb);
330   }
331 
332  private:
OnTimer(uv_timer_t * uvtimer)333   static void OnTimer(uv_timer_t* uvtimer) {
334     InspectorTimer* timer = node::ContainerOf(&InspectorTimer::timer_, uvtimer);
335     timer->callback_(timer->data_);
336   }
337 
TimerClosedCb(uv_handle_t * uvtimer)338   static void TimerClosedCb(uv_handle_t* uvtimer) {
339     std::unique_ptr<InspectorTimer> timer(
340         node::ContainerOf(&InspectorTimer::timer_,
341                           reinterpret_cast<uv_timer_t*>(uvtimer)));
342     // Unique_ptr goes out of scope here and pointer is deleted.
343   }
344 
~InspectorTimer()345   ~InspectorTimer() {}
346 
347   uv_timer_t timer_;
348   V8InspectorClient::TimerCallback callback_;
349   void* data_;
350 
351   friend std::unique_ptr<InspectorTimer>::deleter_type;
352 };
353 
354 class InspectorTimerHandle {
355  public:
InspectorTimerHandle(uv_loop_t * loop,double interval_s,V8InspectorClient::TimerCallback callback,void * data)356   InspectorTimerHandle(uv_loop_t* loop, double interval_s,
357                        V8InspectorClient::TimerCallback callback, void* data) {
358     timer_ = new InspectorTimer(loop, interval_s, callback, data);
359   }
360 
361   InspectorTimerHandle(const InspectorTimerHandle&) = delete;
362 
~InspectorTimerHandle()363   ~InspectorTimerHandle() {
364     CHECK_NOT_NULL(timer_);
365     timer_->Stop();
366     timer_ = nullptr;
367   }
368  private:
369   InspectorTimer* timer_;
370 };
371 
372 class SameThreadInspectorSession : public InspectorSession {
373  public:
SameThreadInspectorSession(int session_id,std::shared_ptr<NodeInspectorClient> client)374   SameThreadInspectorSession(
375       int session_id, std::shared_ptr<NodeInspectorClient> client)
376       : session_id_(session_id), client_(client) {}
377   ~SameThreadInspectorSession() override;
378   void Dispatch(const v8_inspector::StringView& message) override;
379 
380  private:
381   int session_id_;
382   std::weak_ptr<NodeInspectorClient> client_;
383 };
384 
NotifyClusterWorkersDebugEnabled(Environment * env)385 void NotifyClusterWorkersDebugEnabled(Environment* env) {
386   Isolate* isolate = env->isolate();
387   HandleScope handle_scope(isolate);
388   auto context = env->context();
389 
390   // Send message to enable debug in cluster workers
391   Local<Object> process_object = env->process_object();
392   Local<Value> emit_fn =
393       process_object->Get(context, FIXED_ONE_BYTE_STRING(isolate, "emit"))
394           .ToLocalChecked();
395   // In case the thread started early during the startup
396   if (!emit_fn->IsFunction())
397     return;
398 
399   Local<Object> message = Object::New(isolate);
400   message->Set(context, FIXED_ONE_BYTE_STRING(isolate, "cmd"),
401                FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED")).FromJust();
402   Local<Value> argv[] = {
403     FIXED_ONE_BYTE_STRING(isolate, "internalMessage"),
404     message
405   };
406   MakeCallback(env->isolate(), process_object, emit_fn.As<Function>(),
407                arraysize(argv), argv, {0, 0});
408 }
409 
410 #ifdef _WIN32
IsFilePath(const std::string & path)411 bool IsFilePath(const std::string& path) {
412   // '\\'
413   if (path.length() > 2 && path[0] == '\\' && path[1] == '\\')
414     return true;
415   // '[A-Z]:[/\\]'
416   if (path.length() < 3)
417     return false;
418   if ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
419     return path[1] == ':' && (path[2] == '/' || path[2] == '\\');
420   return false;
421 }
422 #else
IsFilePath(const std::string & path)423 bool IsFilePath(const std::string& path) {
424   return path.length() && path[0] == '/';
425 }
426 #endif  // __POSIX__
427 
428 }  // namespace
429 
430 class NodeInspectorClient : public V8InspectorClient {
431  public:
NodeInspectorClient(node::Environment * env,bool is_main)432   explicit NodeInspectorClient(node::Environment* env, bool is_main)
433       : env_(env), is_main_(is_main) {
434     client_ = V8Inspector::create(env->isolate(), this);
435     // TODO(bnoordhuis) Make name configurable from src/node.cc.
436     std::string name =
437         is_main_ ? GetHumanReadableProcessName() : GetWorkerLabel(env);
438     ContextInfo info(name);
439     info.is_default = true;
440     contextCreated(env->context(), info);
441   }
442 
runMessageLoopOnPause(int context_group_id)443   void runMessageLoopOnPause(int context_group_id) override {
444     waiting_for_resume_ = true;
445     runMessageLoop();
446   }
447 
waitForIoShutdown()448   void waitForIoShutdown() {
449     waiting_for_io_shutdown_ = true;
450     runMessageLoop();
451   }
452 
waitForFrontend()453   void waitForFrontend() {
454     waiting_for_frontend_ = true;
455     runMessageLoop();
456   }
457 
maxAsyncCallStackDepthChanged(int depth)458   void maxAsyncCallStackDepthChanged(int depth) override {
459     if (auto agent = env_->inspector_agent()) {
460       if (depth == 0) {
461         agent->DisableAsyncHook();
462       } else {
463         agent->EnableAsyncHook();
464       }
465     }
466   }
467 
contextCreated(Local<Context> context,const ContextInfo & info)468   void contextCreated(Local<Context> context, const ContextInfo& info) {
469     auto name_buffer = Utf8ToStringView(info.name);
470     auto origin_buffer = Utf8ToStringView(info.origin);
471     std::unique_ptr<StringBuffer> aux_data_buffer;
472 
473     v8_inspector::V8ContextInfo v8info(
474         context, CONTEXT_GROUP_ID, name_buffer->string());
475     v8info.origin = origin_buffer->string();
476 
477     if (info.is_default) {
478       aux_data_buffer = Utf8ToStringView("{\"isDefault\":true}");
479     } else {
480       aux_data_buffer = Utf8ToStringView("{\"isDefault\":false}");
481     }
482     v8info.auxData = aux_data_buffer->string();
483 
484     client_->contextCreated(v8info);
485   }
486 
contextDestroyed(Local<Context> context)487   void contextDestroyed(Local<Context> context) {
488     client_->contextDestroyed(context);
489   }
490 
quitMessageLoopOnPause()491   void quitMessageLoopOnPause() override {
492     waiting_for_resume_ = false;
493   }
494 
connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)495   int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,
496                       bool prevent_shutdown) {
497     events_dispatched_ = true;
498     int session_id = next_session_id_++;
499     channels_[session_id] =
500         std::make_unique<ChannelImpl>(env_, client_, getWorkerManager(),
501                                       std::move(delegate), prevent_shutdown);
502     return session_id;
503   }
504 
disconnectFrontend(int session_id)505   void disconnectFrontend(int session_id) {
506     events_dispatched_ = true;
507     channels_.erase(session_id);
508   }
509 
dispatchMessageFromFrontend(int session_id,const StringView & message)510   void dispatchMessageFromFrontend(int session_id, const StringView& message) {
511     events_dispatched_ = true;
512     std::string method =
513         channels_[session_id]->dispatchProtocolMessage(message);
514     if (waiting_for_frontend_)
515       waiting_for_frontend_ = method != "Runtime.runIfWaitingForDebugger";
516   }
517 
ensureDefaultContextInGroup(int contextGroupId)518   Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
519     return env_->context();
520   }
521 
installAdditionalCommandLineAPI(Local<Context> context,Local<Object> target)522   void installAdditionalCommandLineAPI(Local<Context> context,
523                                        Local<Object> target) override {
524     Local<Object> console_api = env_->inspector_console_api_object();
525     CHECK(!console_api.IsEmpty());
526 
527     Local<Array> properties =
528         console_api->GetOwnPropertyNames(context).ToLocalChecked();
529     for (uint32_t i = 0; i < properties->Length(); ++i) {
530       Local<Value> key = properties->Get(context, i).ToLocalChecked();
531       target->Set(context,
532                   key,
533                   console_api->Get(context, key).ToLocalChecked()).FromJust();
534     }
535   }
536 
FatalException(Local<Value> error,Local<Message> message)537   void FatalException(Local<Value> error, Local<Message> message) {
538     Isolate* isolate = env_->isolate();
539     Local<Context> context = env_->context();
540 
541     int script_id = message->GetScriptOrigin().ScriptID()->Value();
542 
543     Local<v8::StackTrace> stack_trace = message->GetStackTrace();
544 
545     if (!stack_trace.IsEmpty() && stack_trace->GetFrameCount() > 0 &&
546         script_id == stack_trace->GetFrame(isolate, 0)->GetScriptId()) {
547       script_id = 0;
548     }
549 
550     const uint8_t DETAILS[] = "Uncaught";
551 
552     client_->exceptionThrown(
553         context,
554         StringView(DETAILS, sizeof(DETAILS) - 1),
555         error,
556         ToProtocolString(isolate, message->Get())->string(),
557         ToProtocolString(isolate, message->GetScriptResourceName())->string(),
558         message->GetLineNumber(context).FromMaybe(0),
559         message->GetStartColumn(context).FromMaybe(0),
560         client_->createStackTrace(stack_trace),
561         script_id);
562   }
563 
startRepeatingTimer(double interval_s,TimerCallback callback,void * data)564   void startRepeatingTimer(double interval_s,
565                            TimerCallback callback,
566                            void* data) override {
567     timers_.emplace(std::piecewise_construct, std::make_tuple(data),
568                     std::make_tuple(env_->event_loop(), interval_s, callback,
569                                     data));
570   }
571 
cancelTimer(void * data)572   void cancelTimer(void* data) override {
573     timers_.erase(data);
574   }
575 
576   // Async stack traces instrumentation.
AsyncTaskScheduled(const StringView & task_name,void * task,bool recurring)577   void AsyncTaskScheduled(const StringView& task_name, void* task,
578                           bool recurring) {
579     client_->asyncTaskScheduled(task_name, task, recurring);
580   }
581 
AsyncTaskCanceled(void * task)582   void AsyncTaskCanceled(void* task) {
583     client_->asyncTaskCanceled(task);
584   }
585 
AsyncTaskStarted(void * task)586   void AsyncTaskStarted(void* task) {
587     client_->asyncTaskStarted(task);
588   }
589 
AsyncTaskFinished(void * task)590   void AsyncTaskFinished(void* task) {
591     client_->asyncTaskFinished(task);
592   }
593 
AllAsyncTasksCanceled()594   void AllAsyncTasksCanceled() {
595     client_->allAsyncTasksCanceled();
596   }
597 
schedulePauseOnNextStatement(const std::string & reason)598   void schedulePauseOnNextStatement(const std::string& reason) {
599     for (const auto& id_channel : channels_) {
600       id_channel.second->schedulePauseOnNextStatement(reason);
601     }
602   }
603 
hasConnectedSessions()604   bool hasConnectedSessions() {
605     for (const auto& id_channel : channels_) {
606       // Other sessions are "invisible" more most purposes
607       if (id_channel.second->preventShutdown())
608         return true;
609     }
610     return false;
611   }
612 
getThreadHandle()613   std::shared_ptr<MainThreadHandle> getThreadHandle() {
614     if (interface_ == nullptr) {
615       interface_.reset(new MainThreadInterface(
616           env_->inspector_agent(), env_->event_loop(), env_->isolate(),
617           env_->isolate_data()->platform()));
618     }
619     return interface_->GetHandle();
620   }
621 
getWorkerManager()622   std::shared_ptr<WorkerManager> getWorkerManager() {
623     if (worker_manager_ == nullptr) {
624       worker_manager_ =
625           std::make_shared<WorkerManager>(getThreadHandle());
626     }
627     return worker_manager_;
628   }
629 
IsActive()630   bool IsActive() {
631     return !channels_.empty();
632   }
633 
634  private:
shouldRunMessageLoop()635   bool shouldRunMessageLoop() {
636     if (waiting_for_frontend_)
637       return true;
638     if (waiting_for_io_shutdown_ || waiting_for_resume_)
639       return hasConnectedSessions();
640     return false;
641   }
642 
runMessageLoop()643   void runMessageLoop() {
644     if (running_nested_loop_)
645       return;
646 
647     running_nested_loop_ = true;
648 
649     MultiIsolatePlatform* platform = env_->isolate_data()->platform();
650     while (shouldRunMessageLoop()) {
651       if (interface_ && hasConnectedSessions())
652         interface_->WaitForFrontendEvent();
653       while (platform->FlushForegroundTasks(env_->isolate())) {}
654     }
655     running_nested_loop_ = false;
656   }
657 
currentTimeMS()658   double currentTimeMS() override {
659     return env_->isolate_data()->platform()->CurrentClockTimeMillis();
660   }
661 
resourceNameToUrl(const StringView & resource_name_view)662   std::unique_ptr<StringBuffer> resourceNameToUrl(
663       const StringView& resource_name_view) override {
664     std::string resource_name =
665         protocol::StringUtil::StringViewToUtf8(resource_name_view);
666     if (!IsFilePath(resource_name))
667       return nullptr;
668     node::url::URL url = node::url::URL::FromFilePath(resource_name);
669     // TODO(ak239spb): replace this code with url.href().
670     // Refs: https://github.com/nodejs/node/issues/22610
671     return Utf8ToStringView(url.protocol() + "//" + url.path());
672   }
673 
674   node::Environment* env_;
675   bool is_main_;
676   bool running_nested_loop_ = false;
677   std::unique_ptr<V8Inspector> client_;
678   std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
679   std::unordered_map<void*, InspectorTimerHandle> timers_;
680   int next_session_id_ = 1;
681   bool events_dispatched_ = false;
682   bool waiting_for_resume_ = false;
683   bool waiting_for_frontend_ = false;
684   bool waiting_for_io_shutdown_ = false;
685   // Allows accessing Inspector from non-main threads
686   std::unique_ptr<MainThreadInterface> interface_;
687   std::shared_ptr<WorkerManager> worker_manager_;
688 };
689 
Agent(Environment * env)690 Agent::Agent(Environment* env)
691   : parent_env_(env),
692     debug_options_(env->options()->debug_options) {}
693 
~Agent()694 Agent::~Agent() {
695   if (start_io_thread_async.data == this) {
696     start_io_thread_async.data = nullptr;
697     // This is global, will never get freed
698     uv_close(reinterpret_cast<uv_handle_t*>(&start_io_thread_async), nullptr);
699   }
700 }
701 
Start(const std::string & path,std::shared_ptr<DebugOptions> options,bool is_main)702 bool Agent::Start(const std::string& path,
703                   std::shared_ptr<DebugOptions> options,
704                   bool is_main) {
705   if (options == nullptr) {
706     options = std::make_shared<DebugOptions>();
707   }
708   path_ = path;
709   debug_options_ = options;
710   client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
711   if (parent_env_->is_main_thread()) {
712     CHECK_EQ(0, uv_async_init(parent_env_->event_loop(),
713                               &start_io_thread_async,
714                               StartIoThreadAsyncCallback));
715     uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
716     start_io_thread_async.data = this;
717     // Ignore failure, SIGUSR1 won't work, but that should not block node start.
718     StartDebugSignalHandler();
719   }
720 
721   bool wait_for_connect = options->wait_for_connect();
722   if (parent_handle_) {
723     wait_for_connect = parent_handle_->WaitForConnect();
724     parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
725   } else if (!options->inspector_enabled || !StartIoThread()) {
726     return false;
727   }
728   if (wait_for_connect) {
729     HandleScope scope(parent_env_->isolate());
730     parent_env_->process_object()->DefineOwnProperty(
731         parent_env_->context(),
732         FIXED_ONE_BYTE_STRING(parent_env_->isolate(), "_breakFirstLine"),
733         True(parent_env_->isolate()),
734         static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum))
735         .FromJust();
736     client_->waitForFrontend();
737   }
738   return true;
739 }
740 
StartIoThread()741 bool Agent::StartIoThread() {
742   if (io_ != nullptr)
743     return true;
744 
745   CHECK_NOT_NULL(client_);
746 
747   io_ = InspectorIo::Start(
748       client_->getThreadHandle(), path_, debug_options_);
749   if (io_ == nullptr) {
750     return false;
751   }
752   NotifyClusterWorkersDebugEnabled(parent_env_);
753   return true;
754 }
755 
Stop()756 void Agent::Stop() {
757   io_.reset();
758 }
759 
Connect(std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)760 std::unique_ptr<InspectorSession> Agent::Connect(
761     std::unique_ptr<InspectorSessionDelegate> delegate,
762     bool prevent_shutdown) {
763   CHECK_NOT_NULL(client_);
764   int session_id = client_->connectFrontend(std::move(delegate),
765                                             prevent_shutdown);
766   return std::unique_ptr<InspectorSession>(
767       new SameThreadInspectorSession(session_id, client_));
768 }
769 
WaitForDisconnect()770 void Agent::WaitForDisconnect() {
771   CHECK_NOT_NULL(client_);
772   bool is_worker = parent_handle_ != nullptr;
773   parent_handle_.reset();
774   if (client_->hasConnectedSessions() && !is_worker) {
775     fprintf(stderr, "Waiting for the debugger to disconnect...\n");
776     fflush(stderr);
777   }
778   // TODO(addaleax): Maybe this should use an at-exit hook for the Environment
779   // or something similar?
780   client_->contextDestroyed(parent_env_->context());
781   if (io_ != nullptr) {
782     io_->StopAcceptingNewConnections();
783     client_->waitForIoShutdown();
784   }
785 }
786 
FatalException(Local<Value> error,Local<Message> message)787 void Agent::FatalException(Local<Value> error, Local<Message> message) {
788   if (!IsListening())
789     return;
790   client_->FatalException(error, message);
791   WaitForDisconnect();
792 }
793 
PauseOnNextJavascriptStatement(const std::string & reason)794 void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
795   client_->schedulePauseOnNextStatement(reason);
796 }
797 
RegisterAsyncHook(Isolate * isolate,Local<Function> enable_function,Local<Function> disable_function)798 void Agent::RegisterAsyncHook(Isolate* isolate,
799                               Local<Function> enable_function,
800                               Local<Function> disable_function) {
801   enable_async_hook_function_.Reset(isolate, enable_function);
802   disable_async_hook_function_.Reset(isolate, disable_function);
803   if (pending_enable_async_hook_) {
804     CHECK(!pending_disable_async_hook_);
805     pending_enable_async_hook_ = false;
806     EnableAsyncHook();
807   } else if (pending_disable_async_hook_) {
808     CHECK(!pending_enable_async_hook_);
809     pending_disable_async_hook_ = false;
810     DisableAsyncHook();
811   }
812 }
813 
EnableAsyncHook()814 void Agent::EnableAsyncHook() {
815   if (!enable_async_hook_function_.IsEmpty()) {
816     ToggleAsyncHook(parent_env_->isolate(), enable_async_hook_function_);
817   } else if (pending_disable_async_hook_) {
818     CHECK(!pending_enable_async_hook_);
819     pending_disable_async_hook_ = false;
820   } else {
821     pending_enable_async_hook_ = true;
822   }
823 }
824 
DisableAsyncHook()825 void Agent::DisableAsyncHook() {
826   if (!disable_async_hook_function_.IsEmpty()) {
827     ToggleAsyncHook(parent_env_->isolate(), disable_async_hook_function_);
828   } else if (pending_enable_async_hook_) {
829     CHECK(!pending_disable_async_hook_);
830     pending_enable_async_hook_ = false;
831   } else {
832     pending_disable_async_hook_ = true;
833   }
834 }
835 
ToggleAsyncHook(Isolate * isolate,const node::Persistent<Function> & fn)836 void Agent::ToggleAsyncHook(Isolate* isolate,
837                             const node::Persistent<Function>& fn) {
838   HandleScope handle_scope(isolate);
839   CHECK(!fn.IsEmpty());
840   auto context = parent_env_->context();
841   auto result = fn.Get(isolate)->Call(context, Undefined(isolate), 0, nullptr);
842   if (result.IsEmpty()) {
843     FatalError(
844         "node::inspector::Agent::ToggleAsyncHook",
845         "Cannot toggle Inspector's AsyncHook, please report this.");
846   }
847 }
848 
AsyncTaskScheduled(const StringView & task_name,void * task,bool recurring)849 void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
850                                bool recurring) {
851   client_->AsyncTaskScheduled(task_name, task, recurring);
852 }
853 
AsyncTaskCanceled(void * task)854 void Agent::AsyncTaskCanceled(void* task) {
855   client_->AsyncTaskCanceled(task);
856 }
857 
AsyncTaskStarted(void * task)858 void Agent::AsyncTaskStarted(void* task) {
859   client_->AsyncTaskStarted(task);
860 }
861 
AsyncTaskFinished(void * task)862 void Agent::AsyncTaskFinished(void* task) {
863   client_->AsyncTaskFinished(task);
864 }
865 
AllAsyncTasksCanceled()866 void Agent::AllAsyncTasksCanceled() {
867   client_->AllAsyncTasksCanceled();
868 }
869 
RequestIoThreadStart()870 void Agent::RequestIoThreadStart() {
871   // We need to attempt to interrupt V8 flow (in case Node is running
872   // continuous JS code) and to wake up libuv thread (in case Node is waiting
873   // for IO events)
874   uv_async_send(&start_io_thread_async);
875   Isolate* isolate = parent_env_->isolate();
876   v8::Platform* platform = parent_env_->isolate_data()->platform();
877   std::shared_ptr<TaskRunner> taskrunner =
878     platform->GetForegroundTaskRunner(isolate);
879   taskrunner->PostTask(std::make_unique<StartIoTask>(this));
880   isolate->RequestInterrupt(StartIoInterrupt, this);
881   uv_async_send(&start_io_thread_async);
882 }
883 
ContextCreated(Local<Context> context,const ContextInfo & info)884 void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
885   if (client_ == nullptr)  // This happens for a main context
886     return;
887   client_->contextCreated(context, info);
888 }
889 
WillWaitForConnect()890 bool Agent::WillWaitForConnect() {
891   if (debug_options_->wait_for_connect())
892     return true;
893   if (parent_handle_)
894     return parent_handle_->WaitForConnect();
895   return false;
896 }
897 
IsActive()898 bool Agent::IsActive() {
899   if (client_ == nullptr)
900     return false;
901   return io_ != nullptr || client_->IsActive();
902 }
903 
AddWorkerInspector(int thread_id,const std::string & url,Agent * agent)904 void Agent::AddWorkerInspector(int thread_id,
905                                const std::string& url,
906                                Agent* agent) {
907   CHECK_NOT_NULL(client_);
908   agent->parent_handle_ =
909       client_->getWorkerManager()->NewParentHandle(thread_id, url);
910 }
911 
WaitForConnect()912 void Agent::WaitForConnect() {
913   CHECK_NOT_NULL(client_);
914   client_->waitForFrontend();
915 }
916 
GetWorkerManager()917 std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
918   CHECK_NOT_NULL(client_);
919   return client_->getWorkerManager();
920 }
921 
~SameThreadInspectorSession()922 SameThreadInspectorSession::~SameThreadInspectorSession() {
923   auto client = client_.lock();
924   if (client)
925     client->disconnectFrontend(session_id_);
926 }
927 
Dispatch(const v8_inspector::StringView & message)928 void SameThreadInspectorSession::Dispatch(
929     const v8_inspector::StringView& message) {
930   auto client = client_.lock();
931   if (client)
932     client->dispatchMessageFromFrontend(session_id_, message);
933 }
934 
935 
936 
937 }  // namespace inspector
938 }  // namespace node
939