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