GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../src/inspector_agent.cc Lines: 396 447 88.6 %
Date: 2019-01-07 12:15:22 Branches: 130 178 73.0 %

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

4807
  return path.length() && path[0] == '/';
409
}
410
#endif  // __POSIX__
411
412
}  // namespace
413
414
3325
class NodeInspectorClient : public V8InspectorClient {
415
 public:
416
3672
  explicit NodeInspectorClient(node::Environment* env, bool is_main)
417
3672
      : env_(env), is_main_(is_main) {
418
3672
    client_ = V8Inspector::create(env->isolate(), this);
419
    // TODO(bnoordhuis) Make name configurable from src/node.cc.
420
    std::string name =
421
3672
        is_main_ ? GetHumanReadableProcessName() : GetWorkerLabel(env);
422
7344
    ContextInfo info(name);
423
3672
    info.is_default = true;
424
7344
    contextCreated(env->context(), info);
425
3672
  }
426
427
36
  void runMessageLoopOnPause(int context_group_id) override {
428
36
    waiting_for_resume_ = true;
429
36
    runMessageLoop();
430
36
  }
431
432
70
  void waitForIoShutdown() {
433
70
    waiting_for_io_shutdown_ = true;
434
70
    runMessageLoop();
435
70
  }
436
437
14
  void waitForFrontend() {
438
14
    waiting_for_frontend_ = true;
439
14
    runMessageLoop();
440
14
  }
441
442
12
  void maxAsyncCallStackDepthChanged(int depth) override {
443
12
    if (auto agent = env_->inspector_agent()) {
444
12
      if (depth == 0) {
445
6
        agent->DisableAsyncHook();
446
      } else {
447
6
        agent->EnableAsyncHook();
448
      }
449
    }
450
12
  }
451
452
4069
  void contextCreated(Local<Context> context, const ContextInfo& info) {
453
4069
    auto name_buffer = Utf8ToStringView(info.name);
454
8138
    auto origin_buffer = Utf8ToStringView(info.origin);
455
8138
    std::unique_ptr<StringBuffer> aux_data_buffer;
456
457
    v8_inspector::V8ContextInfo v8info(
458
4069
        context, CONTEXT_GROUP_ID, name_buffer->string());
459
4069
    v8info.origin = origin_buffer->string();
460
461
4069
    if (info.is_default) {
462
3672
      aux_data_buffer = Utf8ToStringView("{\"isDefault\":true}");
463
    } else {
464
397
      aux_data_buffer = Utf8ToStringView("{\"isDefault\":false}");
465
    }
466
4069
    v8info.auxData = aux_data_buffer->string();
467
468
8138
    client_->contextCreated(v8info);
469
4069
  }
470
471
192
  void contextDestroyed(Local<Context> context) {
472
192
    client_->contextDestroyed(context);
473
192
  }
474
475
24
  void quitMessageLoopOnPause() override {
476
24
    waiting_for_resume_ = false;
477
24
  }
478
479
206
  int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,
480
                      bool prevent_shutdown) {
481
206
    events_dispatched_ = true;
482
206
    int session_id = next_session_id_++;
483
412
    channels_[session_id] =
484
        std::make_unique<ChannelImpl>(env_, client_, getWorkerManager(),
485
412
                                      std::move(delegate), prevent_shutdown);
486
206
    return session_id;
487
  }
488
489
202
  void disconnectFrontend(int session_id) {
490
202
    events_dispatched_ = true;
491
202
    channels_.erase(session_id);
492
202
  }
493
494
516
  void dispatchMessageFromFrontend(int session_id, const StringView& message) {
495
516
    events_dispatched_ = true;
496
    std::string method =
497
516
        channels_[session_id]->dispatchProtocolMessage(message);
498
516
    if (waiting_for_frontend_)
499
65
      waiting_for_frontend_ = method != "Runtime.runIfWaitingForDebugger";
500
516
  }
501
502
13
  Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
503
13
    return env_->context();
504
  }
505
506
3
  void installAdditionalCommandLineAPI(Local<Context> context,
507
                                       Local<Object> target) override {
508
3
    Local<Object> console_api = env_->inspector_console_api_object();
509
3
    CHECK(!console_api.IsEmpty());
510
511
    Local<Array> properties =
512
6
        console_api->GetOwnPropertyNames(context).ToLocalChecked();
513
12
    for (uint32_t i = 0; i < properties->Length(); ++i) {
514
6
      Local<Value> key = properties->Get(context, i).ToLocalChecked();
515
      target->Set(context,
516
                  key,
517
9
                  console_api->Get(context, key).ToLocalChecked()).FromJust();
518
    }
519
3
  }
520
521
3
  void FatalException(Local<Value> error, Local<Message> message) {
522
3
    Isolate* isolate = env_->isolate();
523
3
    Local<Context> context = env_->context();
524
525
9
    int script_id = message->GetScriptOrigin().ScriptID()->Value();
526
527
3
    Local<v8::StackTrace> stack_trace = message->GetStackTrace();
528
529


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

175
    if (waiting_for_io_shutdown_ || waiting_for_resume_)
623
137
      return hasConnectedSessions();
624
38
    return false;
625
  }
626
627
120
  void runMessageLoop() {
628
120
    if (running_nested_loop_)
629
120
      return;
630
631
120
    running_nested_loop_ = true;
632
633
120
    MultiIsolatePlatform* platform = env_->isolate_data()->platform();
634
327847
    while (shouldRunMessageLoop()) {
635

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

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

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

17122
  return io_ != nullptr || client_->IsActive();
891
}
892
893
119
void Agent::AddWorkerInspector(int thread_id,
894
                               const std::string& url,
895
                               Agent* agent) {
896
119
  CHECK_NOT_NULL(client_);
897
238
  agent->parent_handle_ =
898
119
      client_->getWorkerManager()->NewParentHandle(thread_id, url);
899
119
}
900
901
void Agent::WaitForConnect() {
902
  CHECK_NOT_NULL(client_);
903
  client_->waitForFrontend();
904
}
905
906
237
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
907
237
  CHECK_NOT_NULL(client_);
908
237
  return client_->getWorkerManager();
909
}
910
911
618
SameThreadInspectorSession::~SameThreadInspectorSession() {
912
206
  auto client = client_.lock();
913
206
  if (client)
914
202
    client->disconnectFrontend(session_id_);
915
412
}
916
917
516
void SameThreadInspectorSession::Dispatch(
918
    const v8_inspector::StringView& message) {
919
516
  auto client = client_.lock();
920
516
  if (client)
921
516
    client->dispatchMessageFromFrontend(session_id_, message);
922
516
}
923
924
925
926
}  // namespace inspector
927
}  // namespace node