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: 442 463 95.5 %
Date: 2019-02-23 22:23:05 Branches: 132 182 72.5 %

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
4241
inline void* StartIoThreadMain(void* unused) {
92
  for (;;) {
93
4241
    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
4240
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
4240
  CHECK_EQ(0, uv_sem_init(&start_io_thread_semaphore, 0));
107
  pthread_attr_t attr;
108
4240
  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
4240
  CHECK_EQ(0, pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN));
114
#endif  // __FreeBSD__
115
4240
  CHECK_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
116
  sigset_t sigmask;
117
  // Mask all signals.
118
4240
  sigfillset(&sigmask);
119
  sigset_t savemask;
120
4240
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &savemask));
121
4240
  sigmask = savemask;
122
  pthread_t thread;
123
  const int err = pthread_create(&thread, &attr,
124
4240
                                 StartIoThreadMain, nullptr);
125
  // Restore original mask
126
4240
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
127
4240
  CHECK_EQ(0, pthread_attr_destroy(&attr));
128
4240
  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
4240
  RegisterSignalHandler(SIGUSR1, StartIoThreadWakeup);
137
  // Unblock SIGUSR1.  A pending SIGUSR1 signal will now be delivered.
138
4240
  sigemptyset(&sigmask);
139
4240
  sigaddset(&sigmask, SIGUSR1);
140
4240
  CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr));
141
4240
  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
165
std::string GetWorkerLabel(node::Environment* env) {
207
165
  std::ostringstream result;
208
165
  result << "Worker[" << env->thread_id() << "]";
209
165
  return result.str();
210
}
211
212
class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
213
                          public protocol::FrontendChannel {
214
 public:
215
4607
  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
4607
      : delegate_(std::move(delegate)), prevent_shutdown_(prevent_shutdown) {
222
4607
    session_ = inspector->connect(1, this, StringView());
223
4607
    node_dispatcher_ = std::make_unique<protocol::UberDispatcher>(this);
224
9214
    tracing_agent_ =
225
4607
        std::make_unique<protocol::TracingAgent>(env, main_thread_);
226
4607
    tracing_agent_->Wire(node_dispatcher_.get());
227
4607
    worker_agent_ = std::make_unique<protocol::WorkerAgent>(worker_manager);
228
4606
    worker_agent_->Wire(node_dispatcher_.get());
229
4607
  }
230
231
12714
  ~ChannelImpl() override {
232
4238
    tracing_agent_->disable();
233
4238
    tracing_agent_.reset();  // Dispose before the dispatchers
234
4238
    worker_agent_->disable();
235
4238
    worker_agent_.reset();  // Dispose before the dispatchers
236
8476
  }
237
238
13955
  std::string dispatchProtocolMessage(const StringView& message) {
239
13955
    std::unique_ptr<protocol::DictionaryValue> parsed;
240
13955
    std::string method;
241
    node_dispatcher_->getCommandName(
242
13955
        protocol::StringUtil::StringViewToUtf8(message), &method, &parsed);
243
27910
    if (v8_inspector::V8InspectorSession::canDispatchMethod(
244
27910
            Utf8ToStringView(method)->string())) {
245
13938
      session_->dispatchProtocolMessage(message);
246
    } else {
247
17
      node_dispatcher_->dispatch(std::move(parsed));
248
    }
249
13955
    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
4455
  bool preventShutdown() {
258
4455
    return prevent_shutdown_;
259
  }
260
261
 private:
262
13938
  void sendResponse(
263
      int callId,
264
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
265
13938
    sendMessageToFrontend(message->string());
266
13938
  }
267
268
3385
  void sendNotification(
269
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
270
3385
    sendMessageToFrontend(message->string());
271
3385
  }
272
273
372
  void flushProtocolNotifications() override { }
274
275
17446
  void sendMessageToFrontend(const StringView& message) {
276
17446
    delegate_->SendMessageToFrontend(message);
277
17446
  }
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
2
  InspectorTimer(Environment* env,
305
                 double interval_s,
306
                 V8InspectorClient::TimerCallback callback,
307
                 void* data) : env_(env),
308
                               callback_(callback),
309
2
                               data_(data) {
310
2
    uv_timer_init(env->event_loop(), &timer_);
311
2
    int64_t interval_ms = 1000 * interval_s;
312
2
    uv_timer_start(&timer_, OnTimer, interval_ms, interval_ms);
313
2
    timer_.data = this;
314
315
2
    env->AddCleanupHook(CleanupHook, this);
316
2
  }
317
318
  InspectorTimer(const InspectorTimer&) = delete;
319
320
3
  void Stop() {
321
3
    env_->RemoveCleanupHook(CleanupHook, this);
322
323
3
    if (timer_.data == this) {
324
2
      timer_.data = nullptr;
325
2
      uv_timer_stop(&timer_);
326
2
      env_->CloseHandle(reinterpret_cast<uv_handle_t*>(&timer_), TimerClosedCb);
327
    }
328
3
  }
329
330
 private:
331
5
  static void OnTimer(uv_timer_t* uvtimer) {
332
5
    InspectorTimer* timer = node::ContainerOf(&InspectorTimer::timer_, uvtimer);
333
5
    timer->callback_(timer->data_);
334
5
  }
335
336
1
  static void CleanupHook(void* data) {
337
1
    static_cast<InspectorTimer*>(data)->Stop();
338
1
  }
339
340
2
  static void TimerClosedCb(uv_handle_t* uvtimer) {
341
    std::unique_ptr<InspectorTimer> timer(
342
        node::ContainerOf(&InspectorTimer::timer_,
343
2
                          reinterpret_cast<uv_timer_t*>(uvtimer)));
344
    // Unique_ptr goes out of scope here and pointer is deleted.
345
2
  }
346
347
2
  ~InspectorTimer() {}
348
349
  Environment* env_;
350
  uv_timer_t timer_;
351
  V8InspectorClient::TimerCallback callback_;
352
  void* data_;
353
354
  friend std::unique_ptr<InspectorTimer>::deleter_type;
355
};
356
357
class InspectorTimerHandle {
358
 public:
359
2
  InspectorTimerHandle(Environment* env, double interval_s,
360
                       V8InspectorClient::TimerCallback callback, void* data) {
361
2
    timer_ = new InspectorTimer(env, interval_s, callback, data);
362
2
  }
363
364
  InspectorTimerHandle(const InspectorTimerHandle&) = delete;
365
366
2
  ~InspectorTimerHandle() {
367
2
    CHECK_NOT_NULL(timer_);
368
2
    timer_->Stop();
369
2
    timer_ = nullptr;
370
2
  }
371
 private:
372
  InspectorTimer* timer_;
373
};
374
375
class SameThreadInspectorSession : public InspectorSession {
376
 public:
377
4607
  SameThreadInspectorSession(
378
      int session_id, std::shared_ptr<NodeInspectorClient> client)
379
4607
      : session_id_(session_id), client_(client) {}
380
  ~SameThreadInspectorSession() override;
381
  void Dispatch(const v8_inspector::StringView& message) override;
382
383
 private:
384
  int session_id_;
385
  std::weak_ptr<NodeInspectorClient> client_;
386
};
387
388
73
void NotifyClusterWorkersDebugEnabled(Environment* env) {
389
73
  Isolate* isolate = env->isolate();
390
73
  HandleScope handle_scope(isolate);
391
73
  Local<Context> context = env->context();
392
393
  // Send message to enable debug in cluster workers
394
73
  Local<Object> message = Object::New(isolate);
395
  message->Set(context, FIXED_ONE_BYTE_STRING(isolate, "cmd"),
396
292
               FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED")).FromJust();
397
73
  ProcessEmit(env, "internalMessage", message);
398
73
}
399
400
#ifdef _WIN32
401
bool IsFilePath(const std::string& path) {
402
  // '\\'
403
  if (path.length() > 2 && path[0] == '\\' && path[1] == '\\')
404
    return true;
405
  // '[A-Z]:[/\\]'
406
  if (path.length() < 3)
407
    return false;
408
  if ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
409
    return path[1] == ':' && (path[2] == '/' || path[2] == '\\');
410
  return false;
411
}
412
#else
413
415886
bool IsFilePath(const std::string& path) {
414

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


14
    if (!stack_trace.IsEmpty() && stack_trace->GetFrameCount() > 0 &&
534
6
        script_id == stack_trace->GetFrame(isolate, 0)->GetScriptId()) {
535
1
      script_id = 0;
536
    }
537
538
3
    const uint8_t DETAILS[] = "Uncaught";
539
540
3
    client_->exceptionThrown(
541
        context,
542
        StringView(DETAILS, sizeof(DETAILS) - 1),
543
        error,
544
9
        ToProtocolString(isolate, message->Get())->string(),
545
6
        ToProtocolString(isolate, message->GetScriptResourceName())->string(),
546
12
        message->GetLineNumber(context).FromMaybe(0),
547
12
        message->GetStartColumn(context).FromMaybe(0),
548
3
        client_->createStackTrace(stack_trace),
549
18
        script_id);
550
3
  }
551
552
2
  void startRepeatingTimer(double interval_s,
553
                           TimerCallback callback,
554
                           void* data) override {
555
    timers_.emplace(std::piecewise_construct, std::make_tuple(data),
556
                    std::make_tuple(env_, interval_s, callback,
557
2
                                    data));
558
2
  }
559
560
2
  void cancelTimer(void* data) override {
561
2
    timers_.erase(data);
562
2
  }
563
564
  // Async stack traces instrumentation.
565
1
  void AsyncTaskScheduled(const StringView& task_name, void* task,
566
                          bool recurring) {
567
1
    client_->asyncTaskScheduled(task_name, task, recurring);
568
1
  }
569
570
1
  void AsyncTaskCanceled(void* task) {
571
1
    client_->asyncTaskCanceled(task);
572
1
  }
573
574
4
  void AsyncTaskStarted(void* task) {
575
4
    client_->asyncTaskStarted(task);
576
4
  }
577
578
1
  void AsyncTaskFinished(void* task) {
579
1
    client_->asyncTaskFinished(task);
580
1
  }
581
582
  void AllAsyncTasksCanceled() {
583
    client_->allAsyncTasksCanceled();
584
  }
585
586
13
  void schedulePauseOnNextStatement(const std::string& reason) {
587
40
    for (const auto& id_channel : channels_) {
588
27
      id_channel.second->schedulePauseOnNextStatement(reason);
589
    }
590
13
  }
591
592
158360
  bool hasConnectedSessions() {
593
162672
    for (const auto& id_channel : channels_) {
594
      // Other sessions are "invisible" more most purposes
595
4455
      if (id_channel.second->preventShutdown())
596
143
        return true;
597
    }
598
158217
    return false;
599
  }
600
601
9247
  std::shared_ptr<MainThreadHandle> getThreadHandle() {
602
9247
    if (interface_ == nullptr) {
603
      interface_.reset(new MainThreadInterface(
604
13209
          env_->inspector_agent(), env_->event_loop(), env_->isolate(),
605
13209
          env_->isolate_data()->platform()));
606
    }
607
9247
    return interface_->GetHandle();
608
  }
609
610
5112
  std::shared_ptr<WorkerManager> getWorkerManager() {
611
5112
    if (worker_manager_ == nullptr) {
612
8802
      worker_manager_ =
613
4401
          std::make_shared<WorkerManager>(getThreadHandle());
614
    }
615
5111
    return worker_manager_;
616
  }
617
618
132168
  bool IsActive() {
619
132168
    return !channels_.empty();
620
  }
621
622
 private:
623
154074
  bool shouldRunMessageLoop() {
624
154074
    if (waiting_for_frontend_)
625
153902
      return true;
626

172
    if (waiting_for_io_shutdown_ || waiting_for_resume_)
627
136
      return hasConnectedSessions();
628
36
    return false;
629
  }
630
631
117
  void runMessageLoop() {
632
117
    if (running_nested_loop_)
633
117
      return;
634
635
117
    running_nested_loop_ = true;
636
637
117
    MultiIsolatePlatform* platform = env_->isolate_data()->platform();
638
154191
    while (shouldRunMessageLoop()) {
639

153957
      if (interface_ && hasConnectedSessions())
640
74
        interface_->WaitForFrontendEvent();
641
153957
      while (platform->FlushForegroundTasks(env_->isolate())) {}
642
    }
643
117
    running_nested_loop_ = false;
644
  }
645
646
127895
  double currentTimeMS() override {
647
127895
    return env_->isolate_data()->platform()->CurrentClockTimeMillis();
648
  }
649
650
415886
  std::unique_ptr<StringBuffer> resourceNameToUrl(
651
      const StringView& resource_name_view) override {
652
    std::string resource_name =
653
415886
        protocol::StringUtil::StringViewToUtf8(resource_name_view);
654
415886
    if (!IsFilePath(resource_name))
655
374319
      return nullptr;
656
83134
    node::url::URL url = node::url::URL::FromFilePath(resource_name);
657
    // TODO(ak239spb): replace this code with url.href().
658
    // Refs: https://github.com/nodejs/node/issues/22610
659
457453
    return Utf8ToStringView(url.protocol() + "//" + url.path());
660
  }
661
662
  node::Environment* env_;
663
  bool is_main_;
664
  bool running_nested_loop_ = false;
665
  std::unique_ptr<V8Inspector> client_;
666
  std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
667
  std::unordered_map<void*, InspectorTimerHandle> timers_;
668
  int next_session_id_ = 1;
669
  bool events_dispatched_ = false;
670
  bool waiting_for_resume_ = false;
671
  bool waiting_for_frontend_ = false;
672
  bool waiting_for_io_shutdown_ = false;
673
  // Allows accessing Inspector from non-main threads
674
  std::unique_ptr<MainThreadInterface> interface_;
675
  std::shared_ptr<WorkerManager> worker_manager_;
676
};
677
678
4405
Agent::Agent(Environment* env)
679
    : parent_env_(env),
680
8810
      debug_options_(env->options()->debug_options()),
681
22025
      host_port_(env->inspector_host_port()) {}
682
683
16144
Agent::~Agent() {
684
4036
  if (start_io_thread_async.data == this) {
685
3871
    CHECK(start_io_thread_async_initialized.exchange(false));
686
3871
    start_io_thread_async.data = nullptr;
687
    // This is global, will never get freed
688
3871
    uv_close(reinterpret_cast<uv_handle_t*>(&start_io_thread_async), nullptr);
689
  }
690
4036
}
691
692
4405
bool Agent::Start(const std::string& path,
693
                  const DebugOptions& options,
694
                  std::shared_ptr<HostPort> host_port,
695
                  bool is_main) {
696
4405
  path_ = path;
697
4405
  debug_options_ = options;
698
4405
  CHECK_NOT_NULL(host_port);
699
4405
  host_port_ = host_port;
700
701
4405
  client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
702
4405
  if (parent_env_->owns_inspector()) {
703
4240
    CHECK_EQ(start_io_thread_async_initialized.exchange(true), false);
704
4240
    CHECK_EQ(0, uv_async_init(parent_env_->event_loop(),
705
                              &start_io_thread_async,
706
                              StartIoThreadAsyncCallback));
707
4240
    uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
708
4240
    start_io_thread_async.data = this;
709
    // Ignore failure, SIGUSR1 won't work, but that should not block node start.
710
4240
    StartDebugSignalHandler();
711
  }
712
713
4405
  bool wait_for_connect = options.wait_for_connect();
714
4405
  if (parent_handle_) {
715
165
    wait_for_connect = parent_handle_->WaitForConnect();
716
165
    parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
717

4240
  } else if (!options.inspector_enabled || !StartIoThread()) {
718
4170
    return false;
719
  }
720
721
  // TODO(joyeecheung): we should not be using process as a global object
722
  // to transport --inspect-brk. Instead, the JS land can get this through
723
  // require('internal/options') since it should be set once CLI parsing
724
  // is done.
725
235
  if (wait_for_connect) {
726
13
    HandleScope scope(parent_env_->isolate());
727
13
    parent_env_->process_object()->DefineOwnProperty(
728
        parent_env_->context(),
729
        FIXED_ONE_BYTE_STRING(parent_env_->isolate(), "_breakFirstLine"),
730
        True(parent_env_->isolate()),
731
78
        static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum))
732
26
        .FromJust();
733
13
    client_->waitForFrontend();
734
  }
735
235
  return true;
736
}
737
738
77
bool Agent::StartIoThread() {
739
77
  if (io_ != nullptr)
740
3
    return true;
741
742
74
  CHECK_NOT_NULL(client_);
743
744
74
  io_ = InspectorIo::Start(client_->getThreadHandle(), path_, host_port_);
745
74
  if (io_ == nullptr) {
746
1
    return false;
747
  }
748
73
  NotifyClusterWorkersDebugEnabled(parent_env_);
749
73
  return true;
750
}
751
752
169
void Agent::Stop() {
753
169
  io_.reset();
754
169
}
755
756
4607
std::unique_ptr<InspectorSession> Agent::Connect(
757
    std::unique_ptr<InspectorSessionDelegate> delegate,
758
    bool prevent_shutdown) {
759
4607
  CHECK_NOT_NULL(client_);
760
4607
  int session_id = client_->connectFrontend(std::move(delegate),
761
9214
                                            prevent_shutdown);
762
  return std::unique_ptr<InspectorSession>(
763
4607
      new SameThreadInspectorSession(session_id, client_));
764
}
765
766
4267
void Agent::WaitForDisconnect() {
767
4267
  CHECK_NOT_NULL(client_);
768
4267
  bool is_worker = parent_handle_ != nullptr;
769
4267
  parent_handle_.reset();
770

4267
  if (client_->hasConnectedSessions() && !is_worker) {
771
10
    fprintf(stderr, "Waiting for the debugger to disconnect...\n");
772
10
    fflush(stderr);
773
  }
774
  // TODO(addaleax): Maybe this should use an at-exit hook for the Environment
775
  // or something similar?
776
4267
  client_->contextDestroyed(parent_env_->context());
777
4267
  if (io_ != nullptr) {
778
69
    io_->StopAcceptingNewConnections();
779
69
    client_->waitForIoShutdown();
780
  }
781
4267
}
782
783
153
void Agent::FatalException(Local<Value> error, Local<Message> message) {
784
153
  if (!IsListening())
785
303
    return;
786
3
  client_->FatalException(error, message);
787
3
  WaitForDisconnect();
788
}
789
790
13
void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
791
13
  client_->schedulePauseOnNextStatement(reason);
792
13
}
793
794
4397
void Agent::RegisterAsyncHook(Isolate* isolate,
795
                              Local<Function> enable_function,
796
                              Local<Function> disable_function) {
797
4397
  enable_async_hook_function_.Reset(isolate, enable_function);
798
4397
  disable_async_hook_function_.Reset(isolate, disable_function);
799
4397
  if (pending_enable_async_hook_) {
800
2
    CHECK(!pending_disable_async_hook_);
801
2
    pending_enable_async_hook_ = false;
802
2
    EnableAsyncHook();
803
4395
  } else if (pending_disable_async_hook_) {
804
    CHECK(!pending_enable_async_hook_);
805
    pending_disable_async_hook_ = false;
806
    DisableAsyncHook();
807
  }
808
4397
}
809
810
8
void Agent::EnableAsyncHook() {
811
16
  if (!enable_async_hook_function_.IsEmpty()) {
812
6
    ToggleAsyncHook(parent_env_->isolate(), enable_async_hook_function_);
813
2
  } else if (pending_disable_async_hook_) {
814
    CHECK(!pending_enable_async_hook_);
815
    pending_disable_async_hook_ = false;
816
  } else {
817
2
    pending_enable_async_hook_ = true;
818
  }
819
8
}
820
821
6
void Agent::DisableAsyncHook() {
822
12
  if (!disable_async_hook_function_.IsEmpty()) {
823
6
    ToggleAsyncHook(parent_env_->isolate(), disable_async_hook_function_);
824
  } else if (pending_enable_async_hook_) {
825
    CHECK(!pending_disable_async_hook_);
826
    pending_enable_async_hook_ = false;
827
  } else {
828
    pending_disable_async_hook_ = true;
829
  }
830
6
}
831
832
12
void Agent::ToggleAsyncHook(Isolate* isolate,
833
                            const node::Persistent<Function>& fn) {
834
12
  HandleScope handle_scope(isolate);
835
24
  CHECK(!fn.IsEmpty());
836
12
  auto context = parent_env_->context();
837
36
  auto result = fn.Get(isolate)->Call(context, Undefined(isolate), 0, nullptr);
838
12
  if (result.IsEmpty()) {
839
    FatalError(
840
        "node::inspector::Agent::ToggleAsyncHook",
841
        "Cannot toggle Inspector's AsyncHook, please report this.");
842
12
  }
843
12
}
844
845
1
void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
846
                               bool recurring) {
847
1
  client_->AsyncTaskScheduled(task_name, task, recurring);
848
1
}
849
850
1
void Agent::AsyncTaskCanceled(void* task) {
851
1
  client_->AsyncTaskCanceled(task);
852
1
}
853
854
4
void Agent::AsyncTaskStarted(void* task) {
855
4
  client_->AsyncTaskStarted(task);
856
4
}
857
858
1
void Agent::AsyncTaskFinished(void* task) {
859
1
  client_->AsyncTaskFinished(task);
860
1
}
861
862
void Agent::AllAsyncTasksCanceled() {
863
  client_->AllAsyncTasksCanceled();
864
}
865
866
1
void Agent::RequestIoThreadStart() {
867
  // We need to attempt to interrupt V8 flow (in case Node is running
868
  // continuous JS code) and to wake up libuv thread (in case Node is waiting
869
  // for IO events)
870
1
  CHECK(start_io_thread_async_initialized);
871
1
  uv_async_send(&start_io_thread_async);
872
1
  Isolate* isolate = parent_env_->isolate();
873
1
  v8::Platform* platform = parent_env_->isolate_data()->platform();
874
  std::shared_ptr<TaskRunner> taskrunner =
875
1
    platform->GetForegroundTaskRunner(isolate);
876
1
  taskrunner->PostTask(std::make_unique<StartIoTask>(this));
877
1
  isolate->RequestInterrupt(StartIoInterrupt, this);
878
1
  CHECK(start_io_thread_async_initialized);
879
1
  uv_async_send(&start_io_thread_async);
880
1
}
881
882
8952
void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
883
8952
  if (client_ == nullptr)  // This happens for a main context
884
13357
    return;
885
4547
  client_->contextCreated(context, info);
886
}
887
888
4396
bool Agent::WillWaitForConnect() {
889
4396
  if (debug_options_.wait_for_connect()) return true;
890
4385
  if (parent_handle_)
891
157
    return parent_handle_->WaitForConnect();
892
4228
  return false;
893
}
894
895
132267
bool Agent::IsActive() {
896
132267
  if (client_ == nullptr)
897
    return false;
898

132267
  return io_ != nullptr || client_->IsActive();
899
}
900
901
165
void Agent::SetParentHandle(
902
    std::unique_ptr<ParentInspectorHandle> parent_handle) {
903
165
  parent_handle_ = std::move(parent_handle);
904
165
}
905
906
170
std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
907
    int thread_id, const std::string& url) {
908
170
  return client_->getWorkerManager()->NewParentHandle(thread_id, url);
909
}
910
911
void Agent::WaitForConnect() {
912
  CHECK_NOT_NULL(client_);
913
  client_->waitForFrontend();
914
}
915
916
335
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
917
335
  CHECK_NOT_NULL(client_);
918
335
  return client_->getWorkerManager();
919
}
920
921
12714
SameThreadInspectorSession::~SameThreadInspectorSession() {
922
4238
  auto client = client_.lock();
923
4238
  if (client)
924
4234
    client->disconnectFrontend(session_id_);
925
8476
}
926
927
13955
void SameThreadInspectorSession::Dispatch(
928
    const v8_inspector::StringView& message) {
929
13955
  auto client = client_.lock();
930
13955
  if (client)
931
13955
    client->dispatchMessageFromFrontend(session_id_, message);
932
13955
}
933
934
935
936
}  // namespace inspector
937
}  // namespace node