GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/inspector_agent.cc Lines: 170 411 41.4 %
Date: 2019-02-01 22:03:38 Branches: 48 182 26.4 %

Line Branch Exec Source
1
#include "inspector_agent.h"
2
3
#include "inspector/main_thread_interface.h"
4
#include "inspector/node_string.h"
5
#include "inspector/tracing_agent.h"
6
#include "inspector/worker_agent.h"
7
#include "inspector/worker_inspector.h"
8
#include "inspector_io.h"
9
#include "node/inspector/protocol/Protocol.h"
10
#include "node_errors.h"
11
#include "node_internals.h"
12
#include "node_process.h"
13
#include "node_url.h"
14
#include "v8-inspector.h"
15
#include "v8-platform.h"
16
17
#include "libplatform/libplatform.h"
18
19
#include <string.h>
20
#include <sstream>
21
#include <unordered_map>
22
#include <vector>
23
24
#ifdef __POSIX__
25
#include <limits.h>  // PTHREAD_STACK_MIN
26
#include <pthread.h>
27
#endif  // __POSIX__
28
29
namespace node {
30
namespace inspector {
31
namespace {
32
33
using node::FatalError;
34
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
// This is just an additional check to make sure start_io_thread_async
55
// is not accidentally re-used or used when uninitialized.
56
static std::atomic_bool start_io_thread_async_initialized { false };
57
58
class StartIoTask : public Task {
59
 public:
60
  explicit StartIoTask(Agent* agent) : agent(agent) {}
61
62
  void Run() override {
63
    agent->StartIoThread();
64
  }
65
66
 private:
67
  Agent* agent;
68
};
69
70
std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
71
                                               Local<Value> value) {
72
  TwoByteValue buffer(isolate, value);
73
  return StringBuffer::create(StringView(*buffer, buffer.length()));
74
}
75
76
// Called on the main thread.
77
void StartIoThreadAsyncCallback(uv_async_t* handle) {
78
  static_cast<Agent*>(handle->data)->StartIoThread();
79
}
80
81
void StartIoInterrupt(Isolate* isolate, void* agent) {
82
  static_cast<Agent*>(agent)->StartIoThread();
83
}
84
85
86
#ifdef __POSIX__
87
static void StartIoThreadWakeup(int signo) {
88
  uv_sem_post(&start_io_thread_semaphore);
89
}
90
91
164
inline void* StartIoThreadMain(void* unused) {
92
  for (;;) {
93
164
    uv_sem_wait(&start_io_thread_semaphore);
94
    CHECK(start_io_thread_async_initialized);
95
    Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
96
    if (agent != nullptr)
97
      agent->RequestIoThreadStart();
98
  }
99
  return nullptr;
100
}
101
102
164
static int StartDebugSignalHandler() {
103
  // Start a watchdog thread for calling v8::Debug::DebugBreak() because
104
  // it's not safe to call directly from the signal handler, it can
105
  // deadlock with the thread it interrupts.
106
164
  CHECK_EQ(0, uv_sem_init(&start_io_thread_semaphore, 0));
107
  pthread_attr_t attr;
108
164
  CHECK_EQ(0, pthread_attr_init(&attr));
109
  // Don't shrink the thread's stack on FreeBSD.  Said platform decided to
110
  // follow the pthreads specification to the letter rather than in spirit:
111
  // https://lists.freebsd.org/pipermail/freebsd-current/2014-March/048885.html
112
#ifndef __FreeBSD__
113
164
  CHECK_EQ(0, pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN));
114
#endif  // __FreeBSD__
115
164
  CHECK_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
116
  sigset_t sigmask;
117
  // Mask all signals.
118
164
  sigfillset(&sigmask);
119
  sigset_t savemask;
120
164
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &savemask));
121
164
  sigmask = savemask;
122
  pthread_t thread;
123
  const int err = pthread_create(&thread, &attr,
124
164
                                 StartIoThreadMain, nullptr);
125
  // Restore original mask
126
164
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
127
164
  CHECK_EQ(0, pthread_attr_destroy(&attr));
128
164
  if (err != 0) {
129
    fprintf(stderr, "node[%u]: pthread_create: %s\n",
130
            uv_os_getpid(), strerror(err));
131
    fflush(stderr);
132
    // Leave SIGUSR1 blocked.  We don't install a signal handler,
133
    // receiving the signal would terminate the process.
134
    return -err;
135
  }
136
164
  RegisterSignalHandler(SIGUSR1, StartIoThreadWakeup);
137
  // Unblock SIGUSR1.  A pending SIGUSR1 signal will now be delivered.
138
164
  sigemptyset(&sigmask);
139
164
  sigaddset(&sigmask, SIGUSR1);
140
164
  CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr));
141
164
  return 0;
142
}
143
#endif  // __POSIX__
144
145
146
#ifdef _WIN32
147
DWORD WINAPI StartIoThreadProc(void* arg) {
148
  CHECK(start_io_thread_async_initialized);
149
  Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
150
  if (agent != nullptr)
151
    agent->RequestIoThreadStart();
152
  return 0;
153
}
154
155
static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf,
156
                                            size_t buf_len) {
157
  return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid);
158
}
159
160
static int StartDebugSignalHandler() {
161
  wchar_t mapping_name[32];
162
  HANDLE mapping_handle;
163
  DWORD pid;
164
  LPTHREAD_START_ROUTINE* handler;
165
166
  pid = uv_os_getpid();
167
168
  if (GetDebugSignalHandlerMappingName(pid,
169
                                       mapping_name,
170
                                       arraysize(mapping_name)) < 0) {
171
    return -1;
172
  }
173
174
  mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE,
175
                                      nullptr,
176
                                      PAGE_READWRITE,
177
                                      0,
178
                                      sizeof *handler,
179
                                      mapping_name);
180
  if (mapping_handle == nullptr) {
181
    return -1;
182
  }
183
184
  handler = reinterpret_cast<LPTHREAD_START_ROUTINE*>(
185
      MapViewOfFile(mapping_handle,
186
                    FILE_MAP_ALL_ACCESS,
187
                    0,
188
                    0,
189
                    sizeof *handler));
190
  if (handler == nullptr) {
191
    CloseHandle(mapping_handle);
192
    return -1;
193
  }
194
195
  *handler = StartIoThreadProc;
196
197
  UnmapViewOfFile(static_cast<void*>(handler));
198
199
  return 0;
200
}
201
#endif  // _WIN32
202
203
204
const int CONTEXT_GROUP_ID = 1;
205
206
std::string GetWorkerLabel(node::Environment* env) {
207
  std::ostringstream result;
208
  result << "Worker[" << env->thread_id() << "]";
209
  return result.str();
210
}
211
212
class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
213
                          public protocol::FrontendChannel {
214
 public:
215
163
  explicit ChannelImpl(Environment* env,
216
                       const std::unique_ptr<V8Inspector>& inspector,
217
                       std::shared_ptr<WorkerManager> worker_manager,
218
                       std::unique_ptr<InspectorSessionDelegate> delegate,
219
                       std::shared_ptr<MainThreadHandle> main_thread_,
220
                       bool prevent_shutdown)
221
163
      : delegate_(std::move(delegate)), prevent_shutdown_(prevent_shutdown) {
222
163
    session_ = inspector->connect(1, this, StringView());
223
163
    node_dispatcher_ = std::make_unique<protocol::UberDispatcher>(this);
224
326
    tracing_agent_ =
225
163
        std::make_unique<protocol::TracingAgent>(env, main_thread_);
226
163
    tracing_agent_->Wire(node_dispatcher_.get());
227
163
    worker_agent_ = std::make_unique<protocol::WorkerAgent>(worker_manager);
228
163
    worker_agent_->Wire(node_dispatcher_.get());
229
163
  }
230
231
489
  virtual ~ChannelImpl() {
232
163
    tracing_agent_->disable();
233
163
    tracing_agent_.reset();  // Dispose before the dispatchers
234
163
    worker_agent_->disable();
235
163
    worker_agent_.reset();  // Dispose before the dispatchers
236
326
  }
237
238
489
  std::string dispatchProtocolMessage(const StringView& message) {
239
489
    std::unique_ptr<protocol::DictionaryValue> parsed;
240
489
    std::string method;
241
    node_dispatcher_->getCommandName(
242
489
        protocol::StringUtil::StringViewToUtf8(message), &method, &parsed);
243
978
    if (v8_inspector::V8InspectorSession::canDispatchMethod(
244
978
            Utf8ToStringView(method)->string())) {
245
489
      session_->dispatchProtocolMessage(message);
246
    } else {
247
      node_dispatcher_->dispatch(std::move(parsed));
248
    }
249
489
    return method;
250
  }
251
252
  void schedulePauseOnNextStatement(const std::string& reason) {
253
    std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
254
    session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
255
  }
256
257
  bool preventShutdown() {
258
    return prevent_shutdown_;
259
  }
260
261
 private:
262
489
  void sendResponse(
263
      int callId,
264
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
265
489
    sendMessageToFrontend(message->string());
266
489
  }
267
268
  void sendNotification(
269
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
270
    sendMessageToFrontend(message->string());
271
  }
272
273
  void flushProtocolNotifications() override { }
274
275
489
  void sendMessageToFrontend(const StringView& message) {
276
489
    delegate_->SendMessageToFrontend(message);
277
489
  }
278
279
  void sendMessageToFrontend(const std::string& message) {
280
    sendMessageToFrontend(Utf8ToStringView(message)->string());
281
  }
282
283
  using Serializable = protocol::Serializable;
284
285
  void sendProtocolResponse(int callId,
286
                            std::unique_ptr<Serializable> message) override {
287
    sendMessageToFrontend(message->serialize());
288
  }
289
  void sendProtocolNotification(
290
      std::unique_ptr<Serializable> message) override {
291
    sendMessageToFrontend(message->serialize());
292
  }
293
294
  std::unique_ptr<protocol::TracingAgent> tracing_agent_;
295
  std::unique_ptr<protocol::WorkerAgent> worker_agent_;
296
  std::unique_ptr<InspectorSessionDelegate> delegate_;
297
  std::unique_ptr<v8_inspector::V8InspectorSession> session_;
298
  std::unique_ptr<protocol::UberDispatcher> node_dispatcher_;
299
  bool prevent_shutdown_;
300
};
301
302
class InspectorTimer {
303
 public:
304
  InspectorTimer(uv_loop_t* loop,
305
                 double interval_s,
306
                 V8InspectorClient::TimerCallback callback,
307
                 void* data) : timer_(),
308
                               callback_(callback),
309
                               data_(data) {
310
    uv_timer_init(loop, &timer_);
311
    int64_t interval_ms = 1000 * interval_s;
312
    uv_timer_start(&timer_, OnTimer, interval_ms, interval_ms);
313
  }
314
315
  InspectorTimer(const InspectorTimer&) = delete;
316
317
  void Stop() {
318
    uv_timer_stop(&timer_);
319
    uv_close(reinterpret_cast<uv_handle_t*>(&timer_), TimerClosedCb);
320
  }
321
322
 private:
323
  static void OnTimer(uv_timer_t* uvtimer) {
324
    InspectorTimer* timer = node::ContainerOf(&InspectorTimer::timer_, uvtimer);
325
    timer->callback_(timer->data_);
326
  }
327
328
  static void TimerClosedCb(uv_handle_t* uvtimer) {
329
    std::unique_ptr<InspectorTimer> timer(
330
        node::ContainerOf(&InspectorTimer::timer_,
331
                          reinterpret_cast<uv_timer_t*>(uvtimer)));
332
    // Unique_ptr goes out of scope here and pointer is deleted.
333
  }
334
335
  ~InspectorTimer() {}
336
337
  uv_timer_t timer_;
338
  V8InspectorClient::TimerCallback callback_;
339
  void* data_;
340
341
  friend std::unique_ptr<InspectorTimer>::deleter_type;
342
};
343
344
class InspectorTimerHandle {
345
 public:
346
  InspectorTimerHandle(uv_loop_t* loop, double interval_s,
347
                       V8InspectorClient::TimerCallback callback, void* data) {
348
    timer_ = new InspectorTimer(loop, interval_s, callback, data);
349
  }
350
351
  InspectorTimerHandle(const InspectorTimerHandle&) = delete;
352
353
  ~InspectorTimerHandle() {
354
    CHECK_NOT_NULL(timer_);
355
    timer_->Stop();
356
    timer_ = nullptr;
357
  }
358
 private:
359
  InspectorTimer* timer_;
360
};
361
362
class SameThreadInspectorSession : public InspectorSession {
363
 public:
364
163
  SameThreadInspectorSession(
365
      int session_id, std::shared_ptr<NodeInspectorClient> client)
366
163
      : session_id_(session_id), client_(client) {}
367
  ~SameThreadInspectorSession() override;
368
  void Dispatch(const v8_inspector::StringView& message) override;
369
370
 private:
371
  int session_id_;
372
  std::weak_ptr<NodeInspectorClient> client_;
373
};
374
375
void NotifyClusterWorkersDebugEnabled(Environment* env) {
376
  Isolate* isolate = env->isolate();
377
  HandleScope handle_scope(isolate);
378
  Local<Context> context = env->context();
379
380
  // Send message to enable debug in cluster workers
381
  Local<Object> message = Object::New(isolate);
382
  message->Set(context, FIXED_ONE_BYTE_STRING(isolate, "cmd"),
383
               FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED")).FromJust();
384
  ProcessEmit(env, "internalMessage", message);
385
}
386
387
#ifdef _WIN32
388
bool IsFilePath(const std::string& path) {
389
  // '\\'
390
  if (path.length() > 2 && path[0] == '\\' && path[1] == '\\')
391
    return true;
392
  // '[A-Z]:[/\\]'
393
  if (path.length() < 3)
394
    return false;
395
  if ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
396
    return path[1] == ':' && (path[2] == '/' || path[2] == '\\');
397
  return false;
398
}
399
#else
400
22678
bool IsFilePath(const std::string& path) {
401

22678
  return path.length() && path[0] == '/';
402
}
403
#endif  // __POSIX__
404
405
}  // namespace
406
407
154
class NodeInspectorClient : public V8InspectorClient {
408
 public:
409
164
  explicit NodeInspectorClient(node::Environment* env, bool is_main)
410
164
      : env_(env), is_main_(is_main) {
411
164
    client_ = V8Inspector::create(env->isolate(), this);
412
    // TODO(bnoordhuis) Make name configurable from src/node.cc.
413
    std::string name =
414
164
        is_main_ ? GetHumanReadableProcessName() : GetWorkerLabel(env);
415
328
    ContextInfo info(name);
416
164
    info.is_default = true;
417
328
    contextCreated(env->context(), info);
418
164
  }
419
420
  void runMessageLoopOnPause(int context_group_id) override {
421
    waiting_for_resume_ = true;
422
    runMessageLoop();
423
  }
424
425
  void waitForIoShutdown() {
426
    waiting_for_io_shutdown_ = true;
427
    runMessageLoop();
428
  }
429
430
  void waitForFrontend() {
431
    waiting_for_frontend_ = true;
432
    runMessageLoop();
433
  }
434
435
  void maxAsyncCallStackDepthChanged(int depth) override {
436
    if (auto agent = env_->inspector_agent()) {
437
      if (depth == 0) {
438
        agent->DisableAsyncHook();
439
      } else {
440
        agent->EnableAsyncHook();
441
      }
442
    }
443
  }
444
445
166
  void contextCreated(Local<Context> context, const ContextInfo& info) {
446
166
    auto name_buffer = Utf8ToStringView(info.name);
447
332
    auto origin_buffer = Utf8ToStringView(info.origin);
448
332
    std::unique_ptr<StringBuffer> aux_data_buffer;
449
450
    v8_inspector::V8ContextInfo v8info(
451
166
        context, CONTEXT_GROUP_ID, name_buffer->string());
452
166
    v8info.origin = origin_buffer->string();
453
454
166
    if (info.is_default) {
455
164
      aux_data_buffer = Utf8ToStringView("{\"isDefault\":true}");
456
    } else {
457
2
      aux_data_buffer = Utf8ToStringView("{\"isDefault\":false}");
458
    }
459
166
    v8info.auxData = aux_data_buffer->string();
460
461
332
    client_->contextCreated(v8info);
462
166
  }
463
464
  void contextDestroyed(Local<Context> context) {
465
    client_->contextDestroyed(context);
466
  }
467
468
  void quitMessageLoopOnPause() override {
469
    waiting_for_resume_ = false;
470
  }
471
472
163
  int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,
473
                      bool prevent_shutdown) {
474
163
    events_dispatched_ = true;
475
163
    int session_id = next_session_id_++;
476
489
    channels_[session_id] = std::make_unique<ChannelImpl>(env_,
477
                                                          client_,
478
                                                          getWorkerManager(),
479
163
                                                          std::move(delegate),
480
                                                          getThreadHandle(),
481
163
                                                          prevent_shutdown);
482
163
    return session_id;
483
  }
484
485
163
  void disconnectFrontend(int session_id) {
486
163
    events_dispatched_ = true;
487
163
    channels_.erase(session_id);
488
163
  }
489
490
489
  void dispatchMessageFromFrontend(int session_id, const StringView& message) {
491
489
    events_dispatched_ = true;
492
    std::string method =
493
489
        channels_[session_id]->dispatchProtocolMessage(message);
494
489
    if (waiting_for_frontend_)
495
      waiting_for_frontend_ = method != "Runtime.runIfWaitingForDebugger";
496
489
  }
497
498
  Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
499
    return env_->context();
500
  }
501
502
  void installAdditionalCommandLineAPI(Local<Context> context,
503
                                       Local<Object> target) override {
504
    Local<Function> installer = env_->inspector_console_extension_installer();
505
    if (!installer.IsEmpty()) {
506
      Local<Value> argv[] = {target};
507
      // If there is an exception, proceed in JS land
508
      USE(installer->Call(context, target, arraysize(argv), argv));
509
    }
510
  }
511
512
  void FatalException(Local<Value> error, Local<Message> message) {
513
    Isolate* isolate = env_->isolate();
514
    Local<Context> context = env_->context();
515
516
    int script_id = message->GetScriptOrigin().ScriptID()->Value();
517
518
    Local<v8::StackTrace> stack_trace = message->GetStackTrace();
519
520
    if (!stack_trace.IsEmpty() && stack_trace->GetFrameCount() > 0 &&
521
        script_id == stack_trace->GetFrame(isolate, 0)->GetScriptId()) {
522
      script_id = 0;
523
    }
524
525
    const uint8_t DETAILS[] = "Uncaught";
526
527
    client_->exceptionThrown(
528
        context,
529
        StringView(DETAILS, sizeof(DETAILS) - 1),
530
        error,
531
        ToProtocolString(isolate, message->Get())->string(),
532
        ToProtocolString(isolate, message->GetScriptResourceName())->string(),
533
        message->GetLineNumber(context).FromMaybe(0),
534
        message->GetStartColumn(context).FromMaybe(0),
535
        client_->createStackTrace(stack_trace),
536
        script_id);
537
  }
538
539
  void startRepeatingTimer(double interval_s,
540
                           TimerCallback callback,
541
                           void* data) override {
542
    timers_.emplace(std::piecewise_construct, std::make_tuple(data),
543
                    std::make_tuple(env_->event_loop(), interval_s, callback,
544
                                    data));
545
  }
546
547
  void cancelTimer(void* data) override {
548
    timers_.erase(data);
549
  }
550
551
  // Async stack traces instrumentation.
552
  void AsyncTaskScheduled(const StringView& task_name, void* task,
553
                          bool recurring) {
554
    client_->asyncTaskScheduled(task_name, task, recurring);
555
  }
556
557
  void AsyncTaskCanceled(void* task) {
558
    client_->asyncTaskCanceled(task);
559
  }
560
561
  void AsyncTaskStarted(void* task) {
562
    client_->asyncTaskStarted(task);
563
  }
564
565
  void AsyncTaskFinished(void* task) {
566
    client_->asyncTaskFinished(task);
567
  }
568
569
  void AllAsyncTasksCanceled() {
570
    client_->allAsyncTasksCanceled();
571
  }
572
573
  void schedulePauseOnNextStatement(const std::string& reason) {
574
    for (const auto& id_channel : channels_) {
575
      id_channel.second->schedulePauseOnNextStatement(reason);
576
    }
577
  }
578
579
  bool hasConnectedSessions() {
580
    for (const auto& id_channel : channels_) {
581
      // Other sessions are "invisible" more most purposes
582
      if (id_channel.second->preventShutdown())
583
        return true;
584
    }
585
    return false;
586
  }
587
588
326
  std::shared_ptr<MainThreadHandle> getThreadHandle() {
589
326
    if (interface_ == nullptr) {
590
      interface_.reset(new MainThreadInterface(
591
489
          env_->inspector_agent(), env_->event_loop(), env_->isolate(),
592
489
          env_->isolate_data()->platform()));
593
    }
594
326
    return interface_->GetHandle();
595
  }
596
597
163
  std::shared_ptr<WorkerManager> getWorkerManager() {
598
163
    if (worker_manager_ == nullptr) {
599
326
      worker_manager_ =
600
163
          std::make_shared<WorkerManager>(getThreadHandle());
601
    }
602
163
    return worker_manager_;
603
  }
604
605
300
  bool IsActive() {
606
300
    return !channels_.empty();
607
  }
608
609
 private:
610
  bool shouldRunMessageLoop() {
611
    if (waiting_for_frontend_)
612
      return true;
613
    if (waiting_for_io_shutdown_ || waiting_for_resume_)
614
      return hasConnectedSessions();
615
    return false;
616
  }
617
618
  void runMessageLoop() {
619
    if (running_nested_loop_)
620
      return;
621
622
    running_nested_loop_ = true;
623
624
    MultiIsolatePlatform* platform = env_->isolate_data()->platform();
625
    while (shouldRunMessageLoop()) {
626
      if (interface_ && hasConnectedSessions())
627
        interface_->WaitForFrontendEvent();
628
      while (platform->FlushForegroundTasks(env_->isolate())) {}
629
    }
630
    running_nested_loop_ = false;
631
  }
632
633
132
  double currentTimeMS() override {
634
132
    return env_->isolate_data()->platform()->CurrentClockTimeMillis();
635
  }
636
637
22678
  std::unique_ptr<StringBuffer> resourceNameToUrl(
638
      const StringView& resource_name_view) override {
639
    std::string resource_name =
640
22678
        protocol::StringUtil::StringViewToUtf8(resource_name_view);
641
22678
    if (!IsFilePath(resource_name))
642
12738
      return nullptr;
643
19880
    node::url::URL url = node::url::URL::FromFilePath(resource_name);
644
    // TODO(ak239spb): replace this code with url.href().
645
    // Refs: https://github.com/nodejs/node/issues/22610
646
32618
    return Utf8ToStringView(url.protocol() + "//" + url.path());
647
  }
648
649
  node::Environment* env_;
650
  bool is_main_;
651
  bool running_nested_loop_ = false;
652
  std::unique_ptr<V8Inspector> client_;
653
  std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
654
  std::unordered_map<void*, InspectorTimerHandle> timers_;
655
  int next_session_id_ = 1;
656
  bool events_dispatched_ = false;
657
  bool waiting_for_resume_ = false;
658
  bool waiting_for_frontend_ = false;
659
  bool waiting_for_io_shutdown_ = false;
660
  // Allows accessing Inspector from non-main threads
661
  std::unique_ptr<MainThreadInterface> interface_;
662
  std::shared_ptr<WorkerManager> worker_manager_;
663
};
664
665
164
Agent::Agent(Environment* env)
666
    : parent_env_(env),
667
328
      debug_options_(env->options()->debug_options()),
668
820
      host_port_(env->inspector_host_port()) {}
669
670
616
Agent::~Agent() {
671
154
  if (start_io_thread_async.data == this) {
672
154
    CHECK(start_io_thread_async_initialized.exchange(false));
673
154
    start_io_thread_async.data = nullptr;
674
    // This is global, will never get freed
675
154
    uv_close(reinterpret_cast<uv_handle_t*>(&start_io_thread_async), nullptr);
676
  }
677
154
}
678
679
164
bool Agent::Start(const std::string& path,
680
                  const DebugOptions& options,
681
                  std::shared_ptr<HostPort> host_port,
682
                  bool is_main) {
683
164
  path_ = path;
684
164
  debug_options_ = options;
685
164
  CHECK_NE(host_port, nullptr);
686
164
  host_port_ = host_port;
687
688
164
  client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
689
164
  if (parent_env_->is_main_thread()) {
690
164
    CHECK_EQ(start_io_thread_async_initialized.exchange(true), false);
691
164
    CHECK_EQ(0, uv_async_init(parent_env_->event_loop(),
692
                              &start_io_thread_async,
693
                              StartIoThreadAsyncCallback));
694
164
    uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
695
164
    start_io_thread_async.data = this;
696
    // Ignore failure, SIGUSR1 won't work, but that should not block node start.
697
164
    StartDebugSignalHandler();
698
  }
699
700
164
  bool wait_for_connect = options.wait_for_connect();
701
164
  if (parent_handle_) {
702
    wait_for_connect = parent_handle_->WaitForConnect();
703
    parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
704

164
  } else if (!options.inspector_enabled || !StartIoThread()) {
705
164
    return false;
706
  }
707
708
  // TODO(joyeecheung): we should not be using process as a global object
709
  // to transport --inspect-brk. Instead, the JS land can get this through
710
  // require('internal/options') since it should be set once CLI parsing
711
  // is done.
712
  if (wait_for_connect) {
713
    HandleScope scope(parent_env_->isolate());
714
    parent_env_->process_object()->DefineOwnProperty(
715
        parent_env_->context(),
716
        FIXED_ONE_BYTE_STRING(parent_env_->isolate(), "_breakFirstLine"),
717
        True(parent_env_->isolate()),
718
        static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum))
719
        .FromJust();
720
    client_->waitForFrontend();
721
  }
722
  return true;
723
}
724
725
bool Agent::StartIoThread() {
726
  if (io_ != nullptr)
727
    return true;
728
729
  CHECK_NOT_NULL(client_);
730
731
  io_ = InspectorIo::Start(client_->getThreadHandle(), path_, host_port_);
732
  if (io_ == nullptr) {
733
    return false;
734
  }
735
  NotifyClusterWorkersDebugEnabled(parent_env_);
736
  return true;
737
}
738
739
void Agent::Stop() {
740
  io_.reset();
741
}
742
743
163
std::unique_ptr<InspectorSession> Agent::Connect(
744
    std::unique_ptr<InspectorSessionDelegate> delegate,
745
    bool prevent_shutdown) {
746
163
  CHECK_NOT_NULL(client_);
747
163
  int session_id = client_->connectFrontend(std::move(delegate),
748
326
                                            prevent_shutdown);
749
  return std::unique_ptr<InspectorSession>(
750
163
      new SameThreadInspectorSession(session_id, client_));
751
}
752
753
void Agent::WaitForDisconnect() {
754
  CHECK_NOT_NULL(client_);
755
  bool is_worker = parent_handle_ != nullptr;
756
  parent_handle_.reset();
757
  if (client_->hasConnectedSessions() && !is_worker) {
758
    fprintf(stderr, "Waiting for the debugger to disconnect...\n");
759
    fflush(stderr);
760
  }
761
  // TODO(addaleax): Maybe this should use an at-exit hook for the Environment
762
  // or something similar?
763
  client_->contextDestroyed(parent_env_->context());
764
  if (io_ != nullptr) {
765
    io_->StopAcceptingNewConnections();
766
    client_->waitForIoShutdown();
767
  }
768
}
769
770
1
void Agent::FatalException(Local<Value> error, Local<Message> message) {
771
1
  if (!IsListening())
772
2
    return;
773
  client_->FatalException(error, message);
774
  WaitForDisconnect();
775
}
776
777
void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
778
  client_->schedulePauseOnNextStatement(reason);
779
}
780
781
164
void Agent::RegisterAsyncHook(Isolate* isolate,
782
                              Local<Function> enable_function,
783
                              Local<Function> disable_function) {
784
164
  enable_async_hook_function_.Reset(isolate, enable_function);
785
164
  disable_async_hook_function_.Reset(isolate, disable_function);
786
164
  if (pending_enable_async_hook_) {
787
    CHECK(!pending_disable_async_hook_);
788
    pending_enable_async_hook_ = false;
789
    EnableAsyncHook();
790
164
  } else if (pending_disable_async_hook_) {
791
    CHECK(!pending_enable_async_hook_);
792
    pending_disable_async_hook_ = false;
793
    DisableAsyncHook();
794
  }
795
164
}
796
797
void Agent::EnableAsyncHook() {
798
  if (!enable_async_hook_function_.IsEmpty()) {
799
    ToggleAsyncHook(parent_env_->isolate(), enable_async_hook_function_);
800
  } else if (pending_disable_async_hook_) {
801
    CHECK(!pending_enable_async_hook_);
802
    pending_disable_async_hook_ = false;
803
  } else {
804
    pending_enable_async_hook_ = true;
805
  }
806
}
807
808
void Agent::DisableAsyncHook() {
809
  if (!disable_async_hook_function_.IsEmpty()) {
810
    ToggleAsyncHook(parent_env_->isolate(), disable_async_hook_function_);
811
  } else if (pending_enable_async_hook_) {
812
    CHECK(!pending_disable_async_hook_);
813
    pending_enable_async_hook_ = false;
814
  } else {
815
    pending_disable_async_hook_ = true;
816
  }
817
}
818
819
void Agent::ToggleAsyncHook(Isolate* isolate,
820
                            const node::Persistent<Function>& fn) {
821
  HandleScope handle_scope(isolate);
822
  CHECK(!fn.IsEmpty());
823
  auto context = parent_env_->context();
824
  auto result = fn.Get(isolate)->Call(context, Undefined(isolate), 0, nullptr);
825
  if (result.IsEmpty()) {
826
    FatalError(
827
        "node::inspector::Agent::ToggleAsyncHook",
828
        "Cannot toggle Inspector's AsyncHook, please report this.");
829
  }
830
}
831
832
void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
833
                               bool recurring) {
834
  client_->AsyncTaskScheduled(task_name, task, recurring);
835
}
836
837
void Agent::AsyncTaskCanceled(void* task) {
838
  client_->AsyncTaskCanceled(task);
839
}
840
841
void Agent::AsyncTaskStarted(void* task) {
842
  client_->AsyncTaskStarted(task);
843
}
844
845
void Agent::AsyncTaskFinished(void* task) {
846
  client_->AsyncTaskFinished(task);
847
}
848
849
void Agent::AllAsyncTasksCanceled() {
850
  client_->AllAsyncTasksCanceled();
851
}
852
853
void Agent::RequestIoThreadStart() {
854
  // We need to attempt to interrupt V8 flow (in case Node is running
855
  // continuous JS code) and to wake up libuv thread (in case Node is waiting
856
  // for IO events)
857
  CHECK(start_io_thread_async_initialized);
858
  uv_async_send(&start_io_thread_async);
859
  Isolate* isolate = parent_env_->isolate();
860
  v8::Platform* platform = parent_env_->isolate_data()->platform();
861
  std::shared_ptr<TaskRunner> taskrunner =
862
    platform->GetForegroundTaskRunner(isolate);
863
  taskrunner->PostTask(std::make_unique<StartIoTask>(this));
864
  isolate->RequestInterrupt(StartIoInterrupt, this);
865
  CHECK(start_io_thread_async_initialized);
866
  uv_async_send(&start_io_thread_async);
867
}
868
869
166
void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
870
166
  if (client_ == nullptr)  // This happens for a main context
871
330
    return;
872
2
  client_->contextCreated(context, info);
873
}
874
875
164
bool Agent::WillWaitForConnect() {
876
164
  if (debug_options_.wait_for_connect()) return true;
877
164
  if (parent_handle_)
878
    return parent_handle_->WaitForConnect();
879
164
  return false;
880
}
881
882
300
bool Agent::IsActive() {
883
300
  if (client_ == nullptr)
884
    return false;
885

300
  return io_ != nullptr || client_->IsActive();
886
}
887
888
void Agent::AddWorkerInspector(int thread_id,
889
                               const std::string& url,
890
                               Agent* agent) {
891
  CHECK_NOT_NULL(client_);
892
  agent->parent_handle_ =
893
      client_->getWorkerManager()->NewParentHandle(thread_id, url);
894
}
895
896
void Agent::WaitForConnect() {
897
  CHECK_NOT_NULL(client_);
898
  client_->waitForFrontend();
899
}
900
901
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
902
  CHECK_NOT_NULL(client_);
903
  return client_->getWorkerManager();
904
}
905
906
489
SameThreadInspectorSession::~SameThreadInspectorSession() {
907
163
  auto client = client_.lock();
908
163
  if (client)
909
163
    client->disconnectFrontend(session_id_);
910
326
}
911
912
489
void SameThreadInspectorSession::Dispatch(
913
    const v8_inspector::StringView& message) {
914
489
  auto client = client_.lock();
915
489
  if (client)
916
489
    client->dispatchMessageFromFrontend(session_id_, message);
917
489
}
918
919
920
921
}  // namespace inspector
922
}  // namespace node