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: 402 448 89.7 %
Date: 2019-02-13 22:28:58 Branches: 129 180 71.7 %

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
2
class StartIoTask : public Task {
59
 public:
60
1
  explicit StartIoTask(Agent* agent) : agent(agent) {}
61
62
1
  void Run() override {
63
1
    agent->StartIoThread();
64
1
  }
65
66
 private:
67
  Agent* agent;
68
};
69
70
6
std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
71
                                               Local<Value> value) {
72
6
  TwoByteValue buffer(isolate, value);
73
6
  return StringBuffer::create(StringView(*buffer, buffer.length()));
74
}
75
76
// Called on the main thread.
77
1
void StartIoThreadAsyncCallback(uv_async_t* handle) {
78
1
  static_cast<Agent*>(handle->data)->StartIoThread();
79
1
}
80
81
1
void StartIoInterrupt(Isolate* isolate, void* agent) {
82
1
  static_cast<Agent*>(agent)->StartIoThread();
83
1
}
84
85
86
#ifdef __POSIX__
87
1
static void StartIoThreadWakeup(int signo) {
88
1
  uv_sem_post(&start_io_thread_semaphore);
89
1
}
90
91
4267
inline void* StartIoThreadMain(void* unused) {
92
  for (;;) {
93
4267
    uv_sem_wait(&start_io_thread_semaphore);
94
1
    CHECK(start_io_thread_async_initialized);
95
1
    Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
96
1
    if (agent != nullptr)
97
1
      agent->RequestIoThreadStart();
98
1
  }
99
  return nullptr;
100
}
101
102
4266
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
4266
  CHECK_EQ(0, uv_sem_init(&start_io_thread_semaphore, 0));
107
  pthread_attr_t attr;
108
4266
  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
4266
  CHECK_EQ(0, pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN));
114
#endif  // __FreeBSD__
115
4266
  CHECK_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
116
  sigset_t sigmask;
117
  // Mask all signals.
118
4266
  sigfillset(&sigmask);
119
  sigset_t savemask;
120
4266
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &savemask));
121
4266
  sigmask = savemask;
122
  pthread_t thread;
123
  const int err = pthread_create(&thread, &attr,
124
4266
                                 StartIoThreadMain, nullptr);
125
  // Restore original mask
126
4266
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
127
4266
  CHECK_EQ(0, pthread_attr_destroy(&attr));
128
4266
  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
4266
  RegisterSignalHandler(SIGUSR1, StartIoThreadWakeup);
137
  // Unblock SIGUSR1.  A pending SIGUSR1 signal will now be delivered.
138
4266
  sigemptyset(&sigmask);
139
4266
  sigaddset(&sigmask, SIGUSR1);
140
4266
  CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr));
141
4266
  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
151
std::string GetWorkerLabel(node::Environment* env) {
207
151
  std::ostringstream result;
208
151
  result << "Worker[" << env->thread_id() << "]";
209
151
  return result.str();
210
}
211
212
class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
213
                          public protocol::FrontendChannel {
214
 public:
215
4618
  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
4618
      : delegate_(std::move(delegate)), prevent_shutdown_(prevent_shutdown) {
222
4618
    session_ = inspector->connect(1, this, StringView());
223
4618
    node_dispatcher_ = std::make_unique<protocol::UberDispatcher>(this);
224
9236
    tracing_agent_ =
225
4618
        std::make_unique<protocol::TracingAgent>(env, main_thread_);
226
4618
    tracing_agent_->Wire(node_dispatcher_.get());
227
4618
    worker_agent_ = std::make_unique<protocol::WorkerAgent>(worker_manager);
228
4618
    worker_agent_->Wire(node_dispatcher_.get());
229
4618
  }
230
231
13830
  virtual ~ChannelImpl() {
232
4610
    tracing_agent_->disable();
233
4610
    tracing_agent_.reset();  // Dispose before the dispatchers
234
4610
    worker_agent_->disable();
235
4610
    worker_agent_.reset();  // Dispose before the dispatchers
236
9219
  }
237
238
13916
  std::string dispatchProtocolMessage(const StringView& message) {
239
13916
    std::unique_ptr<protocol::DictionaryValue> parsed;
240
13916
    std::string method;
241
    node_dispatcher_->getCommandName(
242
13917
        protocol::StringUtil::StringViewToUtf8(message), &method, &parsed);
243
27835
    if (v8_inspector::V8InspectorSession::canDispatchMethod(
244
27835
            Utf8ToStringView(method)->string())) {
245
13901
      session_->dispatchProtocolMessage(message);
246
    } else {
247
17
      node_dispatcher_->dispatch(std::move(parsed));
248
    }
249
13689
    return method;
250
  }
251
252
27
  void schedulePauseOnNextStatement(const std::string& reason) {
253
27
    std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
254
27
    session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
255
27
  }
256
257
280
  bool preventShutdown() {
258
280
    return prevent_shutdown_;
259
  }
260
261
 private:
262
13899
  void sendResponse(
263
      int callId,
264
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
265
13899
    sendMessageToFrontend(message->string());
266
13674
  }
267
268
3356
  void sendNotification(
269
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
270
3356
    sendMessageToFrontend(message->string());
271
3356
  }
272
273
335
  void flushProtocolNotifications() override { }
274
275
17378
  void sendMessageToFrontend(const StringView& message) {
276
17378
    delegate_->SendMessageToFrontend(message);
277
17152
  }
278
279
123
  void sendMessageToFrontend(const std::string& message) {
280
123
    sendMessageToFrontend(Utf8ToStringView(message)->string());
281
123
  }
282
283
  using Serializable = protocol::Serializable;
284
285
17
  void sendProtocolResponse(int callId,
286
                            std::unique_ptr<Serializable> message) override {
287
17
    sendMessageToFrontend(message->serialize());
288
17
  }
289
106
  void sendProtocolNotification(
290
      std::unique_ptr<Serializable> message) override {
291
106
    sendMessageToFrontend(message->serialize());
292
106
  }
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
4618
  SameThreadInspectorSession(
365
      int session_id, std::shared_ptr<NodeInspectorClient> client)
366
4618
      : 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
73
void NotifyClusterWorkersDebugEnabled(Environment* env) {
376
73
  Isolate* isolate = env->isolate();
377
73
  HandleScope handle_scope(isolate);
378
73
  Local<Context> context = env->context();
379
380
  // Send message to enable debug in cluster workers
381
73
  Local<Object> message = Object::New(isolate);
382
  message->Set(context, FIXED_ONE_BYTE_STRING(isolate, "cmd"),
383
292
               FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED")).FromJust();
384
73
  ProcessEmit(env, "internalMessage", message);
385
73
}
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
416712
bool IsFilePath(const std::string& path) {
401

416712
  return path.length() && path[0] == '/';
402
}
403
#endif  // __POSIX__
404
405
}  // namespace
406
407
4049
class NodeInspectorClient : public V8InspectorClient {
408
 public:
409
4417
  explicit NodeInspectorClient(node::Environment* env, bool is_main)
410
4417
      : env_(env), is_main_(is_main) {
411
4417
    client_ = V8Inspector::create(env->isolate(), this);
412
    // TODO(bnoordhuis) Make name configurable from src/node.cc.
413
    std::string name =
414
4417
        is_main_ ? GetHumanReadableProcessName() : GetWorkerLabel(env);
415
8834
    ContextInfo info(name);
416
4417
    info.is_default = true;
417
8834
    contextCreated(env->context(), info);
418
4417
  }
419
420
35
  void runMessageLoopOnPause(int context_group_id) override {
421
35
    waiting_for_resume_ = true;
422
35
    runMessageLoop();
423
35
  }
424
425
69
  void waitForIoShutdown() {
426
69
    waiting_for_io_shutdown_ = true;
427
69
    runMessageLoop();
428
69
  }
429
430
13
  void waitForFrontend() {
431
13
    waiting_for_frontend_ = true;
432
13
    runMessageLoop();
433
13
  }
434
435
12
  void maxAsyncCallStackDepthChanged(int depth) override {
436
12
    if (auto agent = env_->inspector_agent()) {
437
12
      if (depth == 0) {
438
6
        agent->DisableAsyncHook();
439
      } else {
440
6
        agent->EnableAsyncHook();
441
      }
442
    }
443
12
  }
444
445
8940
  void contextCreated(Local<Context> context, const ContextInfo& info) {
446
8940
    auto name_buffer = Utf8ToStringView(info.name);
447
17880
    auto origin_buffer = Utf8ToStringView(info.origin);
448
17880
    std::unique_ptr<StringBuffer> aux_data_buffer;
449
450
    v8_inspector::V8ContextInfo v8info(
451
8940
        context, CONTEXT_GROUP_ID, name_buffer->string());
452
8940
    v8info.origin = origin_buffer->string();
453
454
8940
    if (info.is_default) {
455
4417
      aux_data_buffer = Utf8ToStringView("{\"isDefault\":true}");
456
    } else {
457
4523
      aux_data_buffer = Utf8ToStringView("{\"isDefault\":false}");
458
    }
459
8940
    v8info.auxData = aux_data_buffer->string();
460
461
17880
    client_->contextCreated(v8info);
462
8940
  }
463
464
224
  void contextDestroyed(Local<Context> context) {
465
224
    client_->contextDestroyed(context);
466
224
  }
467
468
23
  void quitMessageLoopOnPause() override {
469
23
    waiting_for_resume_ = false;
470
23
  }
471
472
4618
  int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,
473
                      bool prevent_shutdown) {
474
4618
    events_dispatched_ = true;
475
4618
    int session_id = next_session_id_++;
476
13854
    channels_[session_id] = std::make_unique<ChannelImpl>(env_,
477
                                                          client_,
478
                                                          getWorkerManager(),
479
4618
                                                          std::move(delegate),
480
                                                          getThreadHandle(),
481
4618
                                                          prevent_shutdown);
482
4618
    return session_id;
483
  }
484
485
4606
  void disconnectFrontend(int session_id) {
486
4606
    events_dispatched_ = true;
487
4606
    channels_.erase(session_id);
488
4605
  }
489
490
13917
  void dispatchMessageFromFrontend(int session_id, const StringView& message) {
491
13917
    events_dispatched_ = true;
492
    std::string method =
493
13917
        channels_[session_id]->dispatchProtocolMessage(message);
494
13690
    if (waiting_for_frontend_)
495
58
      waiting_for_frontend_ = method != "Runtime.runIfWaitingForDebugger";
496
13691
  }
497
498
13
  Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
499
13
    return env_->context();
500
  }
501
502
3
  void installAdditionalCommandLineAPI(Local<Context> context,
503
                                       Local<Object> target) override {
504
3
    Local<Function> installer = env_->inspector_console_extension_installer();
505
3
    if (!installer.IsEmpty()) {
506
6
      Local<Value> argv[] = {target};
507
      // If there is an exception, proceed in JS land
508
6
      USE(installer->Call(context, target, arraysize(argv), argv));
509
    }
510
3
  }
511
512
3
  void FatalException(Local<Value> error, Local<Message> message) {
513
3
    Isolate* isolate = env_->isolate();
514
3
    Local<Context> context = env_->context();
515
516
9
    int script_id = message->GetScriptOrigin().ScriptID()->Value();
517
518
3
    Local<v8::StackTrace> stack_trace = message->GetStackTrace();
519
520


14
    if (!stack_trace.IsEmpty() && stack_trace->GetFrameCount() > 0 &&
521
6
        script_id == stack_trace->GetFrame(isolate, 0)->GetScriptId()) {
522
1
      script_id = 0;
523
    }
524
525
3
    const uint8_t DETAILS[] = "Uncaught";
526
527
3
    client_->exceptionThrown(
528
        context,
529
        StringView(DETAILS, sizeof(DETAILS) - 1),
530
        error,
531
9
        ToProtocolString(isolate, message->Get())->string(),
532
6
        ToProtocolString(isolate, message->GetScriptResourceName())->string(),
533
12
        message->GetLineNumber(context).FromMaybe(0),
534
12
        message->GetStartColumn(context).FromMaybe(0),
535
3
        client_->createStackTrace(stack_trace),
536
18
        script_id);
537
3
  }
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
1
  void AsyncTaskScheduled(const StringView& task_name, void* task,
553
                          bool recurring) {
554
1
    client_->asyncTaskScheduled(task_name, task, recurring);
555
1
  }
556
557
1
  void AsyncTaskCanceled(void* task) {
558
1
    client_->asyncTaskCanceled(task);
559
1
  }
560
561
4
  void AsyncTaskStarted(void* task) {
562
4
    client_->asyncTaskStarted(task);
563
4
  }
564
565
1
  void AsyncTaskFinished(void* task) {
566
1
    client_->asyncTaskFinished(task);
567
1
  }
568
569
  void AllAsyncTasksCanceled() {
570
    client_->allAsyncTasksCanceled();
571
  }
572
573
13
  void schedulePauseOnNextStatement(const std::string& reason) {
574
40
    for (const auto& id_channel : channels_) {
575
27
      id_channel.second->schedulePauseOnNextStatement(reason);
576
    }
577
13
  }
578
579
152436
  bool hasConnectedSessions() {
580
152555
    for (const auto& id_channel : channels_) {
581
      // Other sessions are "invisible" more most purposes
582
280
      if (id_channel.second->preventShutdown())
583
161
        return true;
584
    }
585
152275
    return false;
586
  }
587
588
9256
  std::shared_ptr<MainThreadHandle> getThreadHandle() {
589
9256
    if (interface_ == nullptr) {
590
      interface_.reset(new MainThreadInterface(
591
13245
          env_->inspector_agent(), env_->event_loop(), env_->isolate(),
592
13245
          env_->isolate_data()->platform()));
593
    }
594
9256
    return interface_->GetHandle();
595
  }
596
597
5079
  std::shared_ptr<WorkerManager> getWorkerManager() {
598
5079
    if (worker_manager_ == nullptr) {
599
8826
      worker_manager_ =
600
4413
          std::make_shared<WorkerManager>(getThreadHandle());
601
    }
602
5079
    return worker_manager_;
603
  }
604
605
142157
  bool IsActive() {
606
142157
    return !channels_.empty();
607
  }
608
609
 private:
610
152187
  bool shouldRunMessageLoop() {
611
152187
    if (waiting_for_frontend_)
612
152009
      return true;
613

178
    if (waiting_for_io_shutdown_ || waiting_for_resume_)
614
142
      return hasConnectedSessions();
615
36
    return false;
616
  }
617
618
117
  void runMessageLoop() {
619
117
    if (running_nested_loop_)
620
117
      return;
621
622
117
    running_nested_loop_ = true;
623
624
117
    MultiIsolatePlatform* platform = env_->isolate_data()->platform();
625
152304
    while (shouldRunMessageLoop()) {
626

152070
      if (interface_ && hasConnectedSessions())
627
86
        interface_->WaitForFrontendEvent();
628
152070
      while (platform->FlushForegroundTasks(env_->isolate())) {}
629
    }
630
117
    running_nested_loop_ = false;
631
  }
632
633
137644
  double currentTimeMS() override {
634
137644
    return env_->isolate_data()->platform()->CurrentClockTimeMillis();
635
  }
636
637
416712
  std::unique_ptr<StringBuffer> resourceNameToUrl(
638
      const StringView& resource_name_view) override {
639
    std::string resource_name =
640
416712
        protocol::StringUtil::StringViewToUtf8(resource_name_view);
641
416712
    if (!IsFilePath(resource_name))
642
375368
      return nullptr;
643
82688
    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
458056
    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
4417
Agent::Agent(Environment* env)
666
    : parent_env_(env),
667
8834
      debug_options_(env->options()->debug_options()),
668
22085
      host_port_(env->inspector_host_port()) {}
669
670
16196
Agent::~Agent() {
671
4049
  if (start_io_thread_async.data == this) {
672
3898
    CHECK(start_io_thread_async_initialized.exchange(false));
673
3898
    start_io_thread_async.data = nullptr;
674
    // This is global, will never get freed
675
3898
    uv_close(reinterpret_cast<uv_handle_t*>(&start_io_thread_async), nullptr);
676
  }
677
4049
}
678
679
4417
bool Agent::Start(const std::string& path,
680
                  const DebugOptions& options,
681
                  std::shared_ptr<HostPort> host_port,
682
                  bool is_main) {
683
4417
  path_ = path;
684
4417
  debug_options_ = options;
685
4417
  CHECK_NOT_NULL(host_port);
686
4417
  host_port_ = host_port;
687
688
4417
  client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
689
4417
  if (parent_env_->owns_inspector()) {
690
4266
    CHECK_EQ(start_io_thread_async_initialized.exchange(true), false);
691
4266
    CHECK_EQ(0, uv_async_init(parent_env_->event_loop(),
692
                              &start_io_thread_async,
693
                              StartIoThreadAsyncCallback));
694
4266
    uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
695
4266
    start_io_thread_async.data = this;
696
    // Ignore failure, SIGUSR1 won't work, but that should not block node start.
697
4266
    StartDebugSignalHandler();
698
  }
699
700
4417
  bool wait_for_connect = options.wait_for_connect();
701
4417
  if (parent_handle_) {
702
151
    wait_for_connect = parent_handle_->WaitForConnect();
703
151
    parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
704

4266
  } else if (!options.inspector_enabled || !StartIoThread()) {
705
4196
    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
221
  if (wait_for_connect) {
713
13
    HandleScope scope(parent_env_->isolate());
714
13
    parent_env_->process_object()->DefineOwnProperty(
715
        parent_env_->context(),
716
        FIXED_ONE_BYTE_STRING(parent_env_->isolate(), "_breakFirstLine"),
717
        True(parent_env_->isolate()),
718
78
        static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum))
719
26
        .FromJust();
720
13
    client_->waitForFrontend();
721
  }
722
221
  return true;
723
}
724
725
77
bool Agent::StartIoThread() {
726
77
  if (io_ != nullptr)
727
3
    return true;
728
729
74
  CHECK_NOT_NULL(client_);
730
731
74
  io_ = InspectorIo::Start(client_->getThreadHandle(), path_, host_port_);
732
74
  if (io_ == nullptr) {
733
1
    return false;
734
  }
735
73
  NotifyClusterWorkersDebugEnabled(parent_env_);
736
73
  return true;
737
}
738
739
155
void Agent::Stop() {
740
155
  io_.reset();
741
155
}
742
743
4618
std::unique_ptr<InspectorSession> Agent::Connect(
744
    std::unique_ptr<InspectorSessionDelegate> delegate,
745
    bool prevent_shutdown) {
746
4618
  CHECK_NOT_NULL(client_);
747
4618
  int session_id = client_->connectFrontend(std::move(delegate),
748
9236
                                            prevent_shutdown);
749
  return std::unique_ptr<InspectorSession>(
750
4618
      new SameThreadInspectorSession(session_id, client_));
751
}
752
753
224
void Agent::WaitForDisconnect() {
754
224
  CHECK_NOT_NULL(client_);
755
224
  bool is_worker = parent_handle_ != nullptr;
756
224
  parent_handle_.reset();
757

224
  if (client_->hasConnectedSessions() && !is_worker) {
758
10
    fprintf(stderr, "Waiting for the debugger to disconnect...\n");
759
10
    fflush(stderr);
760
  }
761
  // TODO(addaleax): Maybe this should use an at-exit hook for the Environment
762
  // or something similar?
763
224
  client_->contextDestroyed(parent_env_->context());
764
224
  if (io_ != nullptr) {
765
69
    io_->StopAcceptingNewConnections();
766
69
    client_->waitForIoShutdown();
767
  }
768
224
}
769
770
154
void Agent::FatalException(Local<Value> error, Local<Message> message) {
771
154
  if (!IsListening())
772
305
    return;
773
3
  client_->FatalException(error, message);
774
3
  WaitForDisconnect();
775
}
776
777
13
void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
778
13
  client_->schedulePauseOnNextStatement(reason);
779
13
}
780
781
4410
void Agent::RegisterAsyncHook(Isolate* isolate,
782
                              Local<Function> enable_function,
783
                              Local<Function> disable_function) {
784
4410
  enable_async_hook_function_.Reset(isolate, enable_function);
785
4410
  disable_async_hook_function_.Reset(isolate, disable_function);
786
4410
  if (pending_enable_async_hook_) {
787
2
    CHECK(!pending_disable_async_hook_);
788
2
    pending_enable_async_hook_ = false;
789
2
    EnableAsyncHook();
790
4408
  } else if (pending_disable_async_hook_) {
791
    CHECK(!pending_enable_async_hook_);
792
    pending_disable_async_hook_ = false;
793
    DisableAsyncHook();
794
  }
795
4410
}
796
797
8
void Agent::EnableAsyncHook() {
798
16
  if (!enable_async_hook_function_.IsEmpty()) {
799
6
    ToggleAsyncHook(parent_env_->isolate(), enable_async_hook_function_);
800
2
  } else if (pending_disable_async_hook_) {
801
    CHECK(!pending_enable_async_hook_);
802
    pending_disable_async_hook_ = false;
803
  } else {
804
2
    pending_enable_async_hook_ = true;
805
  }
806
8
}
807
808
6
void Agent::DisableAsyncHook() {
809
12
  if (!disable_async_hook_function_.IsEmpty()) {
810
6
    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
6
}
818
819
12
void Agent::ToggleAsyncHook(Isolate* isolate,
820
                            const node::Persistent<Function>& fn) {
821
12
  HandleScope handle_scope(isolate);
822
24
  CHECK(!fn.IsEmpty());
823
12
  auto context = parent_env_->context();
824
36
  auto result = fn.Get(isolate)->Call(context, Undefined(isolate), 0, nullptr);
825
12
  if (result.IsEmpty()) {
826
    FatalError(
827
        "node::inspector::Agent::ToggleAsyncHook",
828
        "Cannot toggle Inspector's AsyncHook, please report this.");
829
12
  }
830
12
}
831
832
1
void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
833
                               bool recurring) {
834
1
  client_->AsyncTaskScheduled(task_name, task, recurring);
835
1
}
836
837
1
void Agent::AsyncTaskCanceled(void* task) {
838
1
  client_->AsyncTaskCanceled(task);
839
1
}
840
841
4
void Agent::AsyncTaskStarted(void* task) {
842
4
  client_->AsyncTaskStarted(task);
843
4
}
844
845
1
void Agent::AsyncTaskFinished(void* task) {
846
1
  client_->AsyncTaskFinished(task);
847
1
}
848
849
void Agent::AllAsyncTasksCanceled() {
850
  client_->AllAsyncTasksCanceled();
851
}
852
853
1
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
1
  CHECK(start_io_thread_async_initialized);
858
1
  uv_async_send(&start_io_thread_async);
859
1
  Isolate* isolate = parent_env_->isolate();
860
1
  v8::Platform* platform = parent_env_->isolate_data()->platform();
861
  std::shared_ptr<TaskRunner> taskrunner =
862
1
    platform->GetForegroundTaskRunner(isolate);
863
1
  taskrunner->PostTask(std::make_unique<StartIoTask>(this));
864
1
  isolate->RequestInterrupt(StartIoInterrupt, this);
865
1
  CHECK(start_io_thread_async_initialized);
866
1
  uv_async_send(&start_io_thread_async);
867
1
}
868
869
8940
void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
870
8940
  if (client_ == nullptr)  // This happens for a main context
871
13357
    return;
872
4523
  client_->contextCreated(context, info);
873
}
874
875
4416
bool Agent::WillWaitForConnect() {
876
4416
  if (debug_options_.wait_for_connect()) return true;
877
4405
  if (parent_handle_)
878
151
    return parent_handle_->WaitForConnect();
879
4254
  return false;
880
}
881
882
142256
bool Agent::IsActive() {
883
142256
  if (client_ == nullptr)
884
    return false;
885

142256
  return io_ != nullptr || client_->IsActive();
886
}
887
888
151
void Agent::SetParentHandle(
889
    std::unique_ptr<ParentInspectorHandle> parent_handle) {
890
151
  parent_handle_ = std::move(parent_handle);
891
151
}
892
893
155
std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
894
    int thread_id, const std::string& url) {
895
155
  return client_->getWorkerManager()->NewParentHandle(thread_id, url);
896
}
897
898
void Agent::WaitForConnect() {
899
  CHECK_NOT_NULL(client_);
900
  client_->waitForFrontend();
901
}
902
903
306
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
904
306
  CHECK_NOT_NULL(client_);
905
306
  return client_->getWorkerManager();
906
}
907
908
13830
SameThreadInspectorSession::~SameThreadInspectorSession() {
909
4610
  auto client = client_.lock();
910
4610
  if (client)
911
4606
    client->disconnectFrontend(session_id_);
912
9219
}
913
914
13916
void SameThreadInspectorSession::Dispatch(
915
    const v8_inspector::StringView& message) {
916
13916
  auto client = client_.lock();
917
13918
  if (client)
918
13918
    client->dispatchMessageFromFrontend(session_id_, message);
919
13691
}
920
921
922
923
}  // namespace inspector
924
}  // namespace node