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: 439 467 94.0 %
Date: 2019-05-05 22:32:45 Branches: 130 186 69.9 %

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

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


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

169
    if (waiting_for_io_shutdown_ || waiting_for_resume_)
639
133
      return hasConnectedSessions();
640
36
    return false;
641
  }
642
643
116
  void runMessageLoop() {
644
116
    if (running_nested_loop_)
645
116
      return;
646
647
116
    running_nested_loop_ = true;
648
649
116
    MultiIsolatePlatform* platform = env_->isolate_data()->platform();
650
146064
    while (shouldRunMessageLoop()) {
651

145832
      if (interface_ && hasConnectedSessions())
652
81
        interface_->WaitForFrontendEvent();
653
145832
      while (platform->FlushForegroundTasks(env_->isolate())) {}
654
    }
655
116
    running_nested_loop_ = false;
656
  }
657
658
146078
  double currentTimeMS() override {
659
146078
    return env_->isolate_data()->platform()->CurrentClockTimeMillis();
660
  }
661
662
409878
  std::unique_ptr<StringBuffer> resourceNameToUrl(
663
      const StringView& resource_name_view) override {
664
    std::string resource_name =
665
409878
        protocol::StringUtil::StringViewToUtf8(resource_name_view);
666
409879
    if (!IsFilePath(resource_name))
667
367398
      return nullptr;
668
84962
    node::url::URL url = node::url::URL::FromFilePath(resource_name);
669
    // TODO(ak239spb): replace this code with url.href().
670
    // Refs: https://github.com/nodejs/node/issues/22610
671
452360
    return Utf8ToStringView(url.protocol() + "//" + url.path());
672
  }
673
674
  node::Environment* env_;
675
  bool is_main_;
676
  bool running_nested_loop_ = false;
677
  std::unique_ptr<V8Inspector> client_;
678
  std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
679
  std::unordered_map<void*, InspectorTimerHandle> timers_;
680
  int next_session_id_ = 1;
681
  bool events_dispatched_ = false;
682
  bool waiting_for_resume_ = false;
683
  bool waiting_for_frontend_ = false;
684
  bool waiting_for_io_shutdown_ = false;
685
  // Allows accessing Inspector from non-main threads
686
  std::unique_ptr<MainThreadInterface> interface_;
687
  std::shared_ptr<WorkerManager> worker_manager_;
688
};
689
690
4652
Agent::Agent(Environment* env)
691
    : parent_env_(env),
692
9304
      debug_options_(env->options()->debug_options()),
693
23260
      host_port_(env->inspector_host_port()) {}
694
695
17084
Agent::~Agent() {
696
4271
  if (start_io_thread_async.data == this) {
697
4089
    CHECK(start_io_thread_async_initialized.exchange(false));
698
4089
    start_io_thread_async.data = nullptr;
699
    // This is global, will never get freed
700
4089
    uv_close(reinterpret_cast<uv_handle_t*>(&start_io_thread_async), nullptr);
701
  }
702
4271
}
703
704
4652
bool Agent::Start(const std::string& path,
705
                  const DebugOptions& options,
706
                  std::shared_ptr<HostPort> host_port,
707
                  bool is_main) {
708
4652
  path_ = path;
709
4652
  debug_options_ = options;
710
4652
  CHECK_NOT_NULL(host_port);
711
4652
  host_port_ = host_port;
712
713
4652
  client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
714
4652
  if (parent_env_->owns_inspector()) {
715
4470
    CHECK_EQ(start_io_thread_async_initialized.exchange(true), false);
716
4470
    CHECK_EQ(0, uv_async_init(parent_env_->event_loop(),
717
                              &start_io_thread_async,
718
                              StartIoThreadAsyncCallback));
719
4470
    uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
720
4470
    start_io_thread_async.data = this;
721
    // Ignore failure, SIGUSR1 won't work, but that should not block node start.
722
4470
    StartDebugSignalHandler();
723
  }
724
725
4652
  bool wait_for_connect = options.wait_for_connect();
726
4652
  if (parent_handle_) {
727
182
    wait_for_connect = parent_handle_->WaitForConnect();
728
182
    parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
729

4470
  } else if (!options.inspector_enabled || !StartIoThread()) {
730
4400
    return false;
731
  }
732
733
  // Patch the debug options to implement waitForDebuggerOnStart for
734
  // the NodeWorker.enable method.
735
252
  if (wait_for_connect) {
736
13
    CHECK(!parent_env_->has_serialized_options());
737
13
    debug_options_.EnableBreakFirstLine();
738
13
    parent_env_->options()->get_debug_options()->EnableBreakFirstLine();
739
13
    client_->waitForFrontend();
740
  }
741
252
  return true;
742
}
743
744
77
bool Agent::StartIoThread() {
745
77
  if (io_ != nullptr)
746
3
    return true;
747
748
74
  CHECK_NOT_NULL(client_);
749
750
74
  io_ = InspectorIo::Start(client_->getThreadHandle(), path_, host_port_);
751
74
  if (io_ == nullptr) {
752
1
    return false;
753
  }
754
73
  NotifyClusterWorkersDebugEnabled(parent_env_);
755
73
  return true;
756
}
757
758
186
void Agent::Stop() {
759
186
  io_.reset();
760
186
}
761
762
4867
std::unique_ptr<InspectorSession> Agent::Connect(
763
    std::unique_ptr<InspectorSessionDelegate> delegate,
764
    bool prevent_shutdown) {
765
4867
  CHECK_NOT_NULL(client_);
766
4867
  int session_id = client_->connectFrontend(std::move(delegate),
767
9734
                                            prevent_shutdown);
768
  return std::unique_ptr<InspectorSession>(
769
4867
      new SameThreadInspectorSession(session_id, client_));
770
}
771
772
4510
void Agent::WaitForDisconnect() {
773
4510
  CHECK_NOT_NULL(client_);
774
4510
  bool is_worker = parent_handle_ != nullptr;
775
4510
  parent_handle_.reset();
776

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

150897
  return io_ != nullptr || client_->IsActive();
907
}
908
909
182
void Agent::SetParentHandle(
910
    std::unique_ptr<ParentInspectorHandle> parent_handle) {
911
182
  parent_handle_ = std::move(parent_handle);
912
182
}
913
914
186
std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
915
    int thread_id, const std::string& url) {
916
186
  return client_->getWorkerManager()->NewParentHandle(thread_id, url);
917
}
918
919
void Agent::WaitForConnect() {
920
  CHECK_NOT_NULL(client_);
921
  client_->waitForFrontend();
922
}
923
924
368
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
925
368
  CHECK_NOT_NULL(client_);
926
368
  return client_->getWorkerManager();
927
}
928
929
13458
SameThreadInspectorSession::~SameThreadInspectorSession() {
930
4486
  auto client = client_.lock();
931
4486
  if (client)
932
207
    client->disconnectFrontend(session_id_);
933
8972
}
934
935
14320
void SameThreadInspectorSession::Dispatch(
936
    const v8_inspector::StringView& message) {
937
14320
  auto client = client_.lock();
938
14320
  if (client)
939
14320
    client->dispatchMessageFromFrontend(session_id_, message);
940
14320
}
941
942
943
944
}  // namespace inspector
945
}  // namespace node