Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
inspector_agent.cc
Go to the documentation of this file.
1 #include "inspector_agent.h"
2 
3 #include "inspector_io.h"
4 #include "env.h"
5 #include "env-inl.h"
6 #include "node.h"
7 #include "v8-inspector.h"
8 #include "v8-platform.h"
9 #include "util.h"
10 #include "zlib.h"
11 
12 #include "libplatform/libplatform.h"
13 
14 #include <string.h>
15 #include <sstream>
16 #include <unordered_map>
17 #include <vector>
18 
19 #ifdef __POSIX__
20 #include <unistd.h> // setuid, getuid
21 #endif // __POSIX__
22 
23 namespace node {
24 namespace inspector {
25 namespace {
26 
27 using node::FatalError;
28 
29 using v8::Array;
30 using v8::Boolean;
31 using v8::Context;
32 using v8::External;
33 using v8::Function;
34 using v8::FunctionCallbackInfo;
35 using v8::HandleScope;
36 using v8::Integer;
37 using v8::Isolate;
38 using v8::Local;
39 using v8::Maybe;
40 using v8::MaybeLocal;
41 using v8::Name;
42 using v8::NewStringType;
43 using v8::Object;
44 using v8::Persistent;
45 using v8::String;
46 using v8::Undefined;
47 using v8::Value;
48 
49 using v8_inspector::StringBuffer;
50 using v8_inspector::StringView;
51 using v8_inspector::V8Inspector;
52 using v8_inspector::V8InspectorClient;
53 
54 static uv_sem_t start_io_thread_semaphore;
55 static uv_async_t start_io_thread_async;
56 
57 class StartIoTask : public v8::Task {
58  public:
59  explicit StartIoTask(Agent* agent) : agent(agent) {}
60 
61  void Run() override {
62  agent->StartIoThread(false);
63  }
64 
65  private:
66  Agent* agent;
67 };
68 
69 std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
70  Local<Value> value) {
71  TwoByteValue buffer(isolate, value);
72  return StringBuffer::create(StringView(*buffer, buffer.length()));
73 }
74 
75 // Called on the main thread.
76 void StartIoThreadAsyncCallback(uv_async_t* handle) {
77  static_cast<Agent*>(handle->data)->StartIoThread(false);
78 }
79 
80 void StartIoInterrupt(Isolate* isolate, void* agent) {
81  static_cast<Agent*>(agent)->StartIoThread(false);
82 }
83 
84 
85 #ifdef __POSIX__
86 static void StartIoThreadWakeup(int signo) {
87  uv_sem_post(&start_io_thread_semaphore);
88 }
89 
90 inline void* StartIoThreadMain(void* unused) {
91  for (;;) {
92  uv_sem_wait(&start_io_thread_semaphore);
93  Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
94  if (agent != nullptr)
95  agent->RequestIoThreadStart();
96  }
97  return nullptr;
98 }
99 
100 static int StartDebugSignalHandler() {
101  // Start a watchdog thread for calling v8::Debug::DebugBreak() because
102  // it's not safe to call directly from the signal handler, it can
103  // deadlock with the thread it interrupts.
104  CHECK_EQ(0, uv_sem_init(&start_io_thread_semaphore, 0));
105  pthread_attr_t attr;
106  CHECK_EQ(0, pthread_attr_init(&attr));
107  // Don't shrink the thread's stack on FreeBSD. Said platform decided to
108  // follow the pthreads specification to the letter rather than in spirit:
109  // https://lists.freebsd.org/pipermail/freebsd-current/2014-March/048885.html
110 #ifndef __FreeBSD__
111  CHECK_EQ(0, pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN));
112 #endif // __FreeBSD__
113  CHECK_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
114  sigset_t sigmask;
115  // Mask all signals.
116  sigfillset(&sigmask);
117  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &sigmask));
118  pthread_t thread;
119  const int err = pthread_create(&thread, &attr,
120  StartIoThreadMain, nullptr);
121  // Restore original mask
122  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
123  CHECK_EQ(0, pthread_attr_destroy(&attr));
124  if (err != 0) {
125  fprintf(stderr, "node[%d]: pthread_create: %s\n", getpid(), strerror(err));
126  fflush(stderr);
127  // Leave SIGUSR1 blocked. We don't install a signal handler,
128  // receiving the signal would terminate the process.
129  return -err;
130  }
131  RegisterSignalHandler(SIGUSR1, StartIoThreadWakeup);
132  // Unblock SIGUSR1. A pending SIGUSR1 signal will now be delivered.
133  sigemptyset(&sigmask);
134  sigaddset(&sigmask, SIGUSR1);
135  CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr));
136  return 0;
137 }
138 #endif // __POSIX__
139 
140 
141 #ifdef _WIN32
142 DWORD WINAPI StartIoThreadProc(void* arg) {
143  Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
144  if (agent != nullptr)
145  agent->RequestIoThreadStart();
146  return 0;
147 }
148 
149 static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf,
150  size_t buf_len) {
151  return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid);
152 }
153 
154 static int StartDebugSignalHandler() {
155  wchar_t mapping_name[32];
156  HANDLE mapping_handle;
157  DWORD pid;
158  LPTHREAD_START_ROUTINE* handler;
159 
160  pid = GetCurrentProcessId();
161 
162  if (GetDebugSignalHandlerMappingName(pid,
163  mapping_name,
164  arraysize(mapping_name)) < 0) {
165  return -1;
166  }
167 
168  mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE,
169  nullptr,
170  PAGE_READWRITE,
171  0,
172  sizeof *handler,
173  mapping_name);
174  if (mapping_handle == nullptr) {
175  return -1;
176  }
177 
178  handler = reinterpret_cast<LPTHREAD_START_ROUTINE*>(
179  MapViewOfFile(mapping_handle,
180  FILE_MAP_ALL_ACCESS,
181  0,
182  0,
183  sizeof *handler));
184  if (handler == nullptr) {
185  CloseHandle(mapping_handle);
186  return -1;
187  }
188 
189  *handler = StartIoThreadProc;
190 
191  UnmapViewOfFile(static_cast<void*>(handler));
192 
193  return 0;
194 }
195 #endif // _WIN32
196 
197 class JsBindingsSessionDelegate : public InspectorSessionDelegate {
198  public:
199  JsBindingsSessionDelegate(Environment* env,
200  Local<Object> session,
201  Local<Object> receiver,
202  Local<Function> callback)
203  : env_(env),
204  session_(env->isolate(), session),
205  receiver_(env->isolate(), receiver),
206  callback_(env->isolate(), callback) {
207  session_.SetWeak(this, JsBindingsSessionDelegate::Release,
208  v8::WeakCallbackType::kParameter);
209  }
210 
211  ~JsBindingsSessionDelegate() override {
212  session_.Reset();
213  receiver_.Reset();
214  callback_.Reset();
215  }
216 
217  bool WaitForFrontendMessageWhilePaused() override {
218  return false;
219  }
220 
221  void SendMessageToFrontend(const v8_inspector::StringView& message) override {
222  Isolate* isolate = env_->isolate();
223  v8::HandleScope handle_scope(isolate);
224  Context::Scope context_scope(env_->context());
225  MaybeLocal<String> v8string =
226  String::NewFromTwoByte(isolate, message.characters16(),
227  NewStringType::kNormal, message.length());
228  Local<Value> argument = v8string.ToLocalChecked().As<Value>();
229  Local<Function> callback = callback_.Get(isolate);
230  Local<Object> receiver = receiver_.Get(isolate);
231  callback->Call(env_->context(), receiver, 1, &argument)
232  .FromMaybe(Local<Value>());
233  }
234 
235  void Disconnect() {
236  Agent* agent = env_->inspector_agent();
237  if (agent->delegate() == this)
238  agent->Disconnect();
239  }
240 
241  private:
242  static void Release(
243  const v8::WeakCallbackInfo<JsBindingsSessionDelegate>& info) {
244  info.SetSecondPassCallback(ReleaseSecondPass);
245  info.GetParameter()->session_.Reset();
246  }
247 
248  static void ReleaseSecondPass(
249  const v8::WeakCallbackInfo<JsBindingsSessionDelegate>& info) {
250  JsBindingsSessionDelegate* delegate = info.GetParameter();
251  delegate->Disconnect();
252  delete delegate;
253  }
254 
255  Environment* env_;
256  Persistent<Object> session_;
257  Persistent<Object> receiver_;
258  Persistent<Function> callback_;
259 };
260 
261 void SetDelegate(Environment* env, Local<Object> inspector,
262  JsBindingsSessionDelegate* delegate) {
263  inspector->SetPrivate(env->context(),
264  env->inspector_delegate_private_symbol(),
265  v8::External::New(env->isolate(), delegate));
266 }
267 
268 Maybe<JsBindingsSessionDelegate*> GetDelegate(
269  const FunctionCallbackInfo<Value>& info) {
270  Environment* env = Environment::GetCurrent(info);
271  Local<Value> delegate;
272  MaybeLocal<Value> maybe_delegate =
273  info.This()->GetPrivate(env->context(),
274  env->inspector_delegate_private_symbol());
275 
276  if (maybe_delegate.ToLocal(&delegate)) {
277  CHECK(delegate->IsExternal());
278  void* value = delegate.As<External>()->Value();
279  if (value != nullptr) {
280  return v8::Just(static_cast<JsBindingsSessionDelegate*>(value));
281  }
282  }
283  env->ThrowError("Inspector is not connected");
284  return v8::Nothing<JsBindingsSessionDelegate*>();
285 }
286 
287 void Dispatch(const FunctionCallbackInfo<Value>& info) {
288  Environment* env = Environment::GetCurrent(info);
289  if (!info[0]->IsString()) {
290  env->ThrowError("Inspector message must be a string");
291  return;
292  }
293  Maybe<JsBindingsSessionDelegate*> maybe_delegate = GetDelegate(info);
294  if (maybe_delegate.IsNothing())
295  return;
296  Agent* inspector = env->inspector_agent();
297  CHECK_EQ(maybe_delegate.ToChecked(), inspector->delegate());
298  inspector->Dispatch(ToProtocolString(env->isolate(), info[0])->string());
299 }
300 
301 void Disconnect(const FunctionCallbackInfo<Value>& info) {
302  Environment* env = Environment::GetCurrent(info);
303  Maybe<JsBindingsSessionDelegate*> delegate = GetDelegate(info);
304  if (delegate.IsNothing()) {
305  return;
306  }
307  delegate.ToChecked()->Disconnect();
308  SetDelegate(env, info.This(), nullptr);
309  delete delegate.ToChecked();
310 }
311 
312 void ConnectJSBindingsSession(const FunctionCallbackInfo<Value>& info) {
313  Environment* env = Environment::GetCurrent(info);
314  if (!info[0]->IsFunction()) {
315  env->ThrowError("Message callback is required");
316  return;
317  }
318  Agent* inspector = env->inspector_agent();
319  if (inspector->delegate() != nullptr) {
320  env->ThrowError("Session is already attached");
321  return;
322  }
323  Local<Object> session = Object::New(env->isolate());
324  env->SetMethod(session, "dispatch", Dispatch);
325  env->SetMethod(session, "disconnect", Disconnect);
326  info.GetReturnValue().Set(session);
327 
328  JsBindingsSessionDelegate* delegate =
329  new JsBindingsSessionDelegate(env, session, info.Holder(),
330  info[0].As<Function>());
331  inspector->Connect(delegate);
332  SetDelegate(env, session, delegate);
333 }
334 
335 void InspectorConsoleCall(const v8::FunctionCallbackInfo<Value>& info) {
336  Isolate* isolate = info.GetIsolate();
337  HandleScope handle_scope(isolate);
338  Local<Context> context = isolate->GetCurrentContext();
339  CHECK_LT(2, info.Length());
340  std::vector<Local<Value>> call_args;
341  for (int i = 3; i < info.Length(); ++i) {
342  call_args.push_back(info[i]);
343  }
344  Environment* env = Environment::GetCurrent(isolate);
345  if (env->inspector_agent()->enabled()) {
346  Local<Value> inspector_method = info[0];
347  CHECK(inspector_method->IsFunction());
348  Local<Value> config_value = info[2];
349  CHECK(config_value->IsObject());
350  Local<Object> config_object = config_value.As<Object>();
351  Local<String> in_call_key = FIXED_ONE_BYTE_STRING(isolate, "in_call");
352  if (!config_object->Has(context, in_call_key).FromMaybe(false)) {
353  CHECK(config_object->Set(context,
354  in_call_key,
355  v8::True(isolate)).FromJust());
356  CHECK(!inspector_method.As<Function>()->Call(context,
357  info.Holder(),
358  call_args.size(),
359  call_args.data()).IsEmpty());
360  }
361  CHECK(config_object->Delete(context, in_call_key).FromJust());
362  }
363 
364  Local<Value> node_method = info[1];
365  CHECK(node_method->IsFunction());
366  node_method.As<Function>()->Call(context,
367  info.Holder(),
368  call_args.size(),
369  call_args.data()).FromMaybe(Local<Value>());
370 }
371 
372 void CallAndPauseOnStart(
373  const v8::FunctionCallbackInfo<v8::Value>& args) {
374  Environment* env = Environment::GetCurrent(args);
375  CHECK_GT(args.Length(), 1);
376  CHECK(args[0]->IsFunction());
377  std::vector<v8::Local<v8::Value>> call_args;
378  for (int i = 2; i < args.Length(); i++) {
379  call_args.push_back(args[i]);
380  }
381 
382  env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start");
383  v8::MaybeLocal<v8::Value> retval =
384  args[0].As<v8::Function>()->Call(env->context(), args[1],
385  call_args.size(), call_args.data());
386  if (!retval.IsEmpty()) {
387  args.GetReturnValue().Set(retval.ToLocalChecked());
388  }
389 }
390 
391 // Used in NodeInspectorClient::currentTimeMS() below.
392 const int NANOS_PER_MSEC = 1000000;
393 const int CONTEXT_GROUP_ID = 1;
394 
395 class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
396  public:
397  explicit ChannelImpl(V8Inspector* inspector,
398  InspectorSessionDelegate* delegate)
399  : delegate_(delegate) {
400  session_ = inspector->connect(1, this, StringView());
401  }
402 
403  virtual ~ChannelImpl() {}
404 
405  void dispatchProtocolMessage(const StringView& message) {
406  session_->dispatchProtocolMessage(message);
407  }
408 
409  bool waitForFrontendMessage() {
410  return delegate_->WaitForFrontendMessageWhilePaused();
411  }
412 
413  void schedulePauseOnNextStatement(const std::string& reason) {
414  std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
415  session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
416  }
417 
418  InspectorSessionDelegate* delegate() {
419  return delegate_;
420  }
421 
422  private:
423  void sendResponse(
424  int callId,
425  std::unique_ptr<v8_inspector::StringBuffer> message) override {
426  sendMessageToFrontend(message->string());
427  }
428 
429  void sendNotification(
430  std::unique_ptr<v8_inspector::StringBuffer> message) override {
431  sendMessageToFrontend(message->string());
432  }
433 
434  void flushProtocolNotifications() override { }
435 
436  void sendMessageToFrontend(const StringView& message) {
437  delegate_->SendMessageToFrontend(message);
438  }
439 
440  InspectorSessionDelegate* const delegate_;
441  std::unique_ptr<v8_inspector::V8InspectorSession> session_;
442 };
443 
444 class InspectorTimer {
445  public:
446  InspectorTimer(uv_loop_t* loop,
447  double interval_s,
448  V8InspectorClient::TimerCallback callback,
449  void* data) : timer_(),
450  callback_(callback),
451  data_(data) {
452  uv_timer_init(loop, &timer_);
453  int64_t interval_ms = 1000 * interval_s;
454  uv_timer_start(&timer_, OnTimer, interval_ms, interval_ms);
455  }
456 
457  InspectorTimer(const InspectorTimer&) = delete;
458 
459  void Stop() {
460  uv_timer_stop(&timer_);
461  uv_close(reinterpret_cast<uv_handle_t*>(&timer_), TimerClosedCb);
462  }
463 
464  private:
465  static void OnTimer(uv_timer_t* uvtimer) {
466  InspectorTimer* timer = node::ContainerOf(&InspectorTimer::timer_, uvtimer);
467  timer->callback_(timer->data_);
468  }
469 
470  static void TimerClosedCb(uv_handle_t* uvtimer) {
471  InspectorTimer* timer =
472  node::ContainerOf(&InspectorTimer::timer_,
473  reinterpret_cast<uv_timer_t*>(uvtimer));
474  delete timer;
475  }
476 
477  ~InspectorTimer() {}
478 
479  uv_timer_t timer_;
480  V8InspectorClient::TimerCallback callback_;
481  void* data_;
482 };
483 
484 class InspectorTimerHandle {
485  public:
486  InspectorTimerHandle(uv_loop_t* loop, double interval_s,
487  V8InspectorClient::TimerCallback callback, void* data) {
488  timer_ = new InspectorTimer(loop, interval_s, callback, data);
489  }
490 
491  InspectorTimerHandle(const InspectorTimerHandle&) = delete;
492 
493  ~InspectorTimerHandle() {
494  CHECK_NE(timer_, nullptr);
495  timer_->Stop();
496  timer_ = nullptr;
497  }
498  private:
499  InspectorTimer* timer_;
500 };
501 } // namespace
502 
503 class NodeInspectorClient : public V8InspectorClient {
504  public:
505  NodeInspectorClient(node::Environment* env, node::NodePlatform* platform)
506  : env_(env), platform_(platform), terminated_(false),
507  running_nested_loop_(false) {
508  client_ = V8Inspector::create(env->isolate(), this);
509  contextCreated(env->context(), "Node.js Main Context");
510  }
511 
512  void runMessageLoopOnPause(int context_group_id) override {
513  CHECK_NE(channel_, nullptr);
514  if (running_nested_loop_)
515  return;
516  terminated_ = false;
517  running_nested_loop_ = true;
518  while (!terminated_ && channel_->waitForFrontendMessage()) {
519  platform_->FlushForegroundTasksInternal();
520  }
521  terminated_ = false;
522  running_nested_loop_ = false;
523  }
524 
525  double currentTimeMS() override {
526  return uv_hrtime() * 1.0 / NANOS_PER_MSEC;
527  }
528 
529  void contextCreated(Local<Context> context, const std::string& name) {
530  std::unique_ptr<StringBuffer> name_buffer = Utf8ToStringView(name);
531  v8_inspector::V8ContextInfo info(context, CONTEXT_GROUP_ID,
532  name_buffer->string());
533  client_->contextCreated(info);
534  }
535 
536  void contextDestroyed(Local<Context> context) {
537  client_->contextDestroyed(context);
538  }
539 
540  void quitMessageLoopOnPause() override {
541  terminated_ = true;
542  }
543 
545  CHECK_EQ(channel_, nullptr);
546  channel_ = std::unique_ptr<ChannelImpl>(
547  new ChannelImpl(client_.get(), delegate));
548  }
549 
551  quitMessageLoopOnPause();
552  channel_.reset();
553  }
554 
555  void dispatchMessageFromFrontend(const StringView& message) {
556  CHECK_NE(channel_, nullptr);
557  channel_->dispatchProtocolMessage(message);
558  }
559 
560  Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
561  return env_->context();
562  }
563 
564  void installAdditionalCommandLineAPI(Local<Context> context,
565  Local<Object> target) override {
566  Local<Object> console_api = env_->inspector_console_api_object();
567 
568  Local<Array> properties =
569  console_api->GetOwnPropertyNames(context).ToLocalChecked();
570  for (uint32_t i = 0; i < properties->Length(); ++i) {
571  Local<Value> key = properties->Get(context, i).ToLocalChecked();
572  target->Set(context,
573  key,
574  console_api->Get(context, key).ToLocalChecked()).FromJust();
575  }
576  }
577 
578  void FatalException(Local<Value> error, Local<v8::Message> message) {
579  Local<Context> context = env_->context();
580 
581  int script_id = message->GetScriptOrigin().ScriptID()->Value();
582 
583  Local<v8::StackTrace> stack_trace = message->GetStackTrace();
584 
585  if (!stack_trace.IsEmpty() &&
586  stack_trace->GetFrameCount() > 0 &&
587  script_id == stack_trace->GetFrame(0)->GetScriptId()) {
588  script_id = 0;
589  }
590 
591  const uint8_t DETAILS[] = "Uncaught";
592 
593  Isolate* isolate = context->GetIsolate();
594 
595  client_->exceptionThrown(
596  context,
597  StringView(DETAILS, sizeof(DETAILS) - 1),
598  error,
599  ToProtocolString(isolate, message->Get())->string(),
600  ToProtocolString(isolate, message->GetScriptResourceName())->string(),
601  message->GetLineNumber(context).FromMaybe(0),
602  message->GetStartColumn(context).FromMaybe(0),
603  client_->createStackTrace(stack_trace),
604  script_id);
605  }
606 
607  ChannelImpl* channel() {
608  return channel_.get();
609  }
610 
611  void startRepeatingTimer(double interval_s,
612  TimerCallback callback,
613  void* data) override {
614  timers_.emplace(std::piecewise_construct, std::make_tuple(data),
615  std::make_tuple(env_->event_loop(), interval_s, callback,
616  data));
617  }
618 
619  void cancelTimer(void* data) override {
620  timers_.erase(data);
621  }
622 
623  // Async stack traces instrumentation.
624  void AsyncTaskScheduled(const StringView& task_name, void* task,
625  bool recurring) {
626  client_->asyncTaskScheduled(task_name, task, recurring);
627  }
628 
629  void AsyncTaskCanceled(void* task) {
630  client_->asyncTaskCanceled(task);
631  }
632 
633  void AsyncTaskStarted(void* task) {
634  client_->asyncTaskStarted(task);
635  }
636 
637  void AsyncTaskFinished(void* task) {
638  client_->asyncTaskFinished(task);
639  }
640 
642  client_->allAsyncTasksCanceled();
643  }
644 
645  private:
646  node::Environment* env_;
647  node::NodePlatform* platform_;
648  bool terminated_;
649  bool running_nested_loop_;
650  std::unique_ptr<V8Inspector> client_;
651  std::unique_ptr<ChannelImpl> channel_;
652  std::unordered_map<void*, InspectorTimerHandle> timers_;
653 };
654 
655 Agent::Agent(Environment* env) : parent_env_(env),
656  client_(nullptr),
657  platform_(nullptr),
658  enabled_(false),
659  next_context_number_(1) {}
660 
661 // Destructor needs to be defined here in implementation file as the header
662 // does not have full definition of some classes.
664 }
665 
666 bool Agent::Start(node::NodePlatform* platform, const char* path,
667  const DebugOptions& options) {
668  path_ = path == nullptr ? "" : path;
669  debug_options_ = options;
670  client_ =
671  std::unique_ptr<NodeInspectorClient>(
672  new NodeInspectorClient(parent_env_, platform));
673  platform_ = platform;
674  CHECK_EQ(0, uv_async_init(uv_default_loop(),
675  &start_io_thread_async,
676  StartIoThreadAsyncCallback));
677  start_io_thread_async.data = this;
678  uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
679 
680  // Ignore failure, SIGUSR1 won't work, but that should not block node start.
681  StartDebugSignalHandler();
682  if (options.inspector_enabled()) {
683  // This will return false if listen failed on the inspector port.
684  return StartIoThread(options.wait_for_connect());
685  }
686  return true;
687 }
688 
689 bool Agent::StartIoThread(bool wait_for_connect) {
690  if (io_ != nullptr)
691  return true;
692 
693  CHECK_NE(client_, nullptr);
694 
695  enabled_ = true;
696  io_ = std::unique_ptr<InspectorIo>(
697  new InspectorIo(parent_env_, platform_, path_, debug_options_,
698  wait_for_connect));
699  if (!io_->Start()) {
700  client_.reset();
701  return false;
702  }
703 
704  v8::Isolate* isolate = parent_env_->isolate();
705  HandleScope handle_scope(isolate);
706 
707  // Enable tracking of async stack traces
708  if (!enable_async_hook_function_.IsEmpty()) {
709  Local<Function> enable_fn = enable_async_hook_function_.Get(isolate);
710  auto context = parent_env_->context();
711  auto result = enable_fn->Call(context, Undefined(isolate), 0, nullptr);
712  if (result.IsEmpty()) {
713  FatalError(
714  "node::InspectorAgent::StartIoThread",
715  "Cannot enable Inspector's AsyncHook, please report this.");
716  }
717  }
718 
719  // Send message to enable debug in workers
720  Local<Object> process_object = parent_env_->process_object();
721  Local<Value> emit_fn =
722  process_object->Get(FIXED_ONE_BYTE_STRING(isolate, "emit"));
723  // In case the thread started early during the startup
724  if (!emit_fn->IsFunction())
725  return true;
726 
727  Local<Object> message = Object::New(isolate);
728  message->Set(FIXED_ONE_BYTE_STRING(isolate, "cmd"),
729  FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED"));
730  Local<Value> argv[] = {
731  FIXED_ONE_BYTE_STRING(isolate, "internalMessage"),
732  message
733  };
734  MakeCallback(parent_env_->isolate(), process_object, emit_fn.As<Function>(),
735  arraysize(argv), argv, {0, 0});
736 
737  return true;
738 }
739 
740 static void AddCommandLineAPI(
741  const FunctionCallbackInfo<Value>& info) {
742  auto env = Environment::GetCurrent(info);
743  Local<Context> context = env->context();
744 
745  if (info.Length() != 2 || !info[0]->IsString()) {
746  return env->ThrowTypeError("inspector.addCommandLineAPI takes "
747  "exactly 2 arguments: a string and a value.");
748  }
749 
750  Local<Object> console_api = env->inspector_console_api_object();
751  console_api->Set(context, info[0], info[1]).FromJust();
752 }
753 
754 void Agent::Stop() {
755  if (io_ != nullptr) {
756  io_->Stop();
757  io_.reset();
758  enabled_ = false;
759  }
760 
761  v8::Isolate* isolate = parent_env_->isolate();
762  HandleScope handle_scope(isolate);
763 
764  // Disable tracking of async stack traces
765  if (!disable_async_hook_function_.IsEmpty()) {
766  Local<Function> disable_fn = disable_async_hook_function_.Get(isolate);
767  auto result = disable_fn->Call(parent_env_->context(),
768  Undefined(parent_env_->isolate()), 0, nullptr);
769  if (result.IsEmpty()) {
770  FatalError(
771  "node::InspectorAgent::Stop",
772  "Cannot disable Inspector's AsyncHook, please report this.");
773  }
774  }
775 }
776 
778  if (!enabled_) {
779  // Enable tracking of async stack traces
780  v8::Isolate* isolate = parent_env_->isolate();
781  HandleScope handle_scope(isolate);
782  auto context = parent_env_->context();
783  Local<Function> enable_fn = enable_async_hook_function_.Get(isolate);
784  auto result = enable_fn->Call(context, Undefined(isolate), 0, nullptr);
785  if (result.IsEmpty()) {
786  FatalError(
787  "node::InspectorAgent::Connect",
788  "Cannot enable Inspector's AsyncHook, please report this.");
789  }
790  }
791 
792  enabled_ = true;
793  client_->connectFrontend(delegate);
794 }
795 
797  return io_ && io_->IsConnected();
798 }
799 
801  CHECK_NE(client_, nullptr);
802  client_->contextDestroyed(parent_env_->context());
803  if (io_ != nullptr) {
804  io_->WaitForDisconnect();
805  }
806 }
807 
808 void Agent::FatalException(Local<Value> error, Local<v8::Message> message) {
809  if (!IsStarted())
810  return;
811  client_->FatalException(error, message);
812  WaitForDisconnect();
813 }
814 
815 void Agent::Dispatch(const StringView& message) {
816  CHECK_NE(client_, nullptr);
817  client_->dispatchMessageFromFrontend(message);
818 }
819 
821  CHECK_NE(client_, nullptr);
822  client_->disconnectFrontend();
823 }
824 
826  CHECK_NE(client_, nullptr);
827  client_->runMessageLoopOnPause(CONTEXT_GROUP_ID);
828 }
829 
831  CHECK_NE(client_, nullptr);
832  ChannelImpl* channel = client_->channel();
833  if (channel == nullptr)
834  return nullptr;
835  return channel->delegate();
836 }
837 
838 void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
839  ChannelImpl* channel = client_->channel();
840  if (channel != nullptr)
841  channel->schedulePauseOnNextStatement(reason);
842 }
843 
844 void Agent::RegisterAsyncHook(Isolate* isolate,
845  v8::Local<v8::Function> enable_function,
846  v8::Local<v8::Function> disable_function) {
847  enable_async_hook_function_.Reset(isolate, enable_function);
848  disable_async_hook_function_.Reset(isolate, disable_function);
849 }
850 
851 void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
852  bool recurring) {
853  client_->AsyncTaskScheduled(task_name, task, recurring);
854 }
855 
856 void Agent::AsyncTaskCanceled(void* task) {
857  client_->AsyncTaskCanceled(task);
858 }
859 
860 void Agent::AsyncTaskStarted(void* task) {
861  client_->AsyncTaskStarted(task);
862 }
863 
864 void Agent::AsyncTaskFinished(void* task) {
865  client_->AsyncTaskFinished(task);
866 }
867 
869  client_->AllAsyncTasksCanceled();
870 }
871 
872 void Open(const FunctionCallbackInfo<Value>& args) {
873  Environment* env = Environment::GetCurrent(args);
874  inspector::Agent* agent = env->inspector_agent();
875  bool wait_for_connect = false;
876 
877  if (args.Length() > 0 && args[0]->IsUint32()) {
878  uint32_t port = args[0]->Uint32Value();
879  agent->options().set_port(static_cast<int>(port));
880  }
881 
882  if (args.Length() > 1 && args[1]->IsString()) {
883  node::Utf8Value host(env->isolate(), args[1].As<String>());
884  agent->options().set_host_name(*host);
885  }
886 
887  if (args.Length() > 2 && args[2]->IsBoolean()) {
888  wait_for_connect = args[2]->BooleanValue();
889  }
890 
891  agent->StartIoThread(wait_for_connect);
892 }
893 
894 void Url(const FunctionCallbackInfo<Value>& args) {
895  Environment* env = Environment::GetCurrent(args);
896  inspector::Agent* agent = env->inspector_agent();
897  inspector::InspectorIo* io = agent->io();
898 
899  if (!io) return;
900 
901  std::vector<std::string> ids = io->GetTargetIds();
902 
903  if (ids.empty()) return;
904 
905  std::string url = FormatWsAddress(io->host(), io->port(), ids[0], true);
906  args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str()));
907 }
908 
909 static void* GetAsyncTask(int64_t asyncId) {
910  // The inspector assumes that when other clients use its asyncTask* API,
911  // they use real pointers, or at least something aligned like real pointer.
912  // In general it means that our task_id should always be even.
913  //
914  // On 32bit platforms, the 64bit asyncId would get truncated when converted
915  // to a 32bit pointer. However, the javascript part will never enable
916  // the async_hook on 32bit platforms, therefore the truncation will never
917  // happen in practice.
918  return reinterpret_cast<void*>(asyncId << 1);
919 }
920 
921 template<void (Agent::*asyncTaskFn)(void*)>
922 static void InvokeAsyncTaskFnWithId(const FunctionCallbackInfo<Value>& args) {
923  Environment* env = Environment::GetCurrent(args);
924  CHECK(args[0]->IsNumber());
925  int64_t task_id = args[0]->IntegerValue(env->context()).FromJust();
926  (env->inspector_agent()->*asyncTaskFn)(GetAsyncTask(task_id));
927 }
928 
929 static void AsyncTaskScheduledWrapper(const FunctionCallbackInfo<Value>& args) {
930  Environment* env = Environment::GetCurrent(args);
931 
932  CHECK(args[0]->IsString());
933  Local<String> task_name = args[0].As<String>();
934  String::Value task_name_value(task_name);
935  StringView task_name_view(*task_name_value, task_name_value.length());
936 
937  CHECK(args[1]->IsNumber());
938  int64_t task_id = args[1]->IntegerValue(env->context()).FromJust();
939  void* task = GetAsyncTask(task_id);
940 
941  CHECK(args[2]->IsBoolean());
942  bool recurring = args[2]->BooleanValue(env->context()).FromJust();
943 
944  env->inspector_agent()->AsyncTaskScheduled(task_name_view, task, recurring);
945 }
946 
947 static void RegisterAsyncHookWrapper(const FunctionCallbackInfo<Value>& args) {
948  Environment* env = Environment::GetCurrent(args);
949 
950  CHECK(args[0]->IsFunction());
951  v8::Local<v8::Function> enable_function = args[0].As<Function>();
952  CHECK(args[1]->IsFunction());
953  v8::Local<v8::Function> disable_function = args[1].As<Function>();
954  env->inspector_agent()->RegisterAsyncHook(env->isolate(),
955  enable_function, disable_function);
956 }
957 
958 static void IsEnabled(const FunctionCallbackInfo<Value>& args) {
959  Environment* env = Environment::GetCurrent(args);
960  args.GetReturnValue().Set(env->inspector_agent()->enabled());
961 }
962 
963 // static
964 void Agent::InitInspector(Local<Object> target, Local<Value> unused,
965  Local<Context> context, void* priv) {
966  Environment* env = Environment::GetCurrent(context);
967  {
968  auto obj = Object::New(env->isolate());
969  auto null = Null(env->isolate());
970  CHECK(obj->SetPrototype(context, null).FromJust());
971  env->set_inspector_console_api_object(obj);
972  }
973 
974  Agent* agent = env->inspector_agent();
975  env->SetMethod(target, "consoleCall", InspectorConsoleCall);
976  env->SetMethod(target, "addCommandLineAPI", AddCommandLineAPI);
977  if (agent->debug_options_.wait_for_connect())
978  env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart);
979  env->SetMethod(target, "connect", ConnectJSBindingsSession);
980  env->SetMethod(target, "open", Open);
981  env->SetMethod(target, "url", Url);
982 
983  env->SetMethod(target, "asyncTaskScheduled", AsyncTaskScheduledWrapper);
984  env->SetMethod(target, "asyncTaskCanceled",
985  InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>);
986  env->SetMethod(target, "asyncTaskStarted",
987  InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>);
988  env->SetMethod(target, "asyncTaskFinished",
989  InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>);
990 
991  env->SetMethod(target, "registerAsyncHook", RegisterAsyncHookWrapper);
992  env->SetMethod(target, "isEnabled", IsEnabled);
993 }
994 
996  // We need to attempt to interrupt V8 flow (in case Node is running
997  // continuous JS code) and to wake up libuv thread (in case Node is waiting
998  // for IO events)
999  uv_async_send(&start_io_thread_async);
1000  v8::Isolate* isolate = parent_env_->isolate();
1001  platform_->CallOnForegroundThread(isolate, new StartIoTask(this));
1002  isolate->RequestInterrupt(StartIoInterrupt, this);
1003  uv_async_send(&start_io_thread_async);
1004 }
1005 
1006 void Agent::ContextCreated(Local<Context> context) {
1007  if (client_ == nullptr) // This happens for a main context
1008  return;
1009  std::ostringstream name;
1010  name << "VM Context " << next_context_number_++;
1011  client_->contextCreated(context, name.str());
1012 }
1013 
1014 } // namespace inspector
1015 } // namespace node
1016 
std::unique_ptr< StringBuffer > Utf8ToStringView(const std::string &message)
void AsyncTaskScheduled(const v8_inspector::StringView &taskName, void *task, bool recurring)
unsigned char * buf
Definition: cares_wrap.cc:483
NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector, node::inspector::Agent::InitInspector)
bool inspector_enabled() const
void runMessageLoopOnPause(int context_group_id) override
void set_host_name(std::string host_name)
Environment *const env_
NO_RETURN void FatalError(const char *location, const char *message)
Definition: node.cc:2616
void FatalException(Local< Value > error, Local< v8::Message > message)
union node::cares_wrap::@8::CaresAsyncData::@0 data
hostent * host
Definition: cares_wrap.cc:482
std::string host() const
Definition: inspector_io.h:79
std::string FormatWsAddress(const std::string &host, int port, const std::string &target_id, bool include_protocol)
void set_port(int port)
void Dispatch(const v8_inspector::StringView &message)
void dispatchMessageFromFrontend(const StringView &message)
void installAdditionalCommandLineAPI(Local< Context > context, Local< Object > target) override
NodeInspectorClient(node::Environment *env, node::NodePlatform *platform)
void ContextCreated(v8::Local< v8::Context > context)
void contextDestroyed(Local< Context > context)
void Open(const FunctionCallbackInfo< Value > &args)
void Url(const FunctionCallbackInfo< Value > &args)
bool Start(node::NodePlatform *platform, const char *path, const DebugOptions &options)
void AsyncTaskScheduled(const StringView &task_name, void *task, bool recurring)
void AsyncTaskCanceled(void *task)
void FatalException(v8::Local< v8::Value > error, v8::Local< v8::Message > message)
void AsyncTaskStarted(void *task)
void AsyncTaskFinished(void *task)
void Connect(InspectorSessionDelegate *delegate)
bool StartIoThread(bool wait_for_connect)
ChannelWrap * channel
Definition: cares_wrap.cc:122
std::vector< std::string > GetTargetIds() const
Agent(node::Environment *env)
InspectorSessionDelegate * delegate()
MaybeLocal< Object > New(Isolate *isolate, Local< String > string, enum encoding enc)
Definition: node_buffer.cc:241
bool wait_for_connect() const
MaybeLocal< Value > MakeCallback(Isolate *isolate, Local< Object > recv, Local< Function > callback, int argc, Local< Value > *argv, async_id asyncId, async_id triggerAsyncId)
Definition: async-wrap.cc:802
void cancelTimer(void *data) override
void connectFrontend(InspectorSessionDelegate *delegate)
void startRepeatingTimer(double interval_s, TimerCallback callback, void *data) override
void RegisterAsyncHook(v8::Isolate *isolate, v8::Local< v8::Function > enable_function, v8::Local< v8::Function > disable_function)
Local< Context > ensureDefaultContextInGroup(int contextGroupId) override
static void InitInspector(v8::Local< v8::Object > target, v8::Local< v8::Value > unused, v8::Local< v8::Context > context, void *priv)
void contextCreated(Local< Context > context, const std::string &name)
void PauseOnNextJavascriptStatement(const std::string &reason)
DebugOptions & options()