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: 505 534 94.6 %
Date: 2020-02-27 22:14:15 Branches: 153 210 72.9 %

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

459569
  return path.length() && path[0] == '/';
461
}
462
#endif  // __POSIX__
463
464
}  // namespace
465
466
3963
class NodeInspectorClient : public V8InspectorClient {
467
 public:
468
4368
  explicit NodeInspectorClient(node::Environment* env, bool is_main)
469
4368
      : env_(env), is_main_(is_main) {
470
4368
    client_ = V8Inspector::create(env->isolate(), this);
471
    // TODO(bnoordhuis) Make name configurable from src/node.cc.
472
    std::string name =
473
8736
        is_main_ ? GetHumanReadableProcessName() : GetWorkerLabel(env);
474
8735
    ContextInfo info(name);
475
4367
    info.is_default = true;
476
4367
    contextCreated(env->context(), info);
477
4368
  }
478
479
36
  void runMessageLoopOnPause(int context_group_id) override {
480
36
    waiting_for_resume_ = true;
481
36
    runMessageLoop();
482
36
  }
483
484
78
  void waitForSessionsDisconnect() {
485
78
    waiting_for_sessions_disconnect_ = true;
486
78
    runMessageLoop();
487
78
  }
488
489
18
  void waitForFrontend() {
490
18
    waiting_for_frontend_ = true;
491
18
    runMessageLoop();
492
18
  }
493
494
14
  void maxAsyncCallStackDepthChanged(int depth) override {
495
14
    if (waiting_for_sessions_disconnect_) {
496
      // V8 isolate is mostly done and is only letting Inspector protocol
497
      // clients gather data.
498
4
      return;
499
    }
500
10
    if (auto agent = env_->inspector_agent()) {
501
10
      if (depth == 0) {
502
4
        agent->DisableAsyncHook();
503
      } else {
504
6
        agent->EnableAsyncHook();
505
      }
506
    }
507
  }
508
509
4928
  void contextCreated(Local<Context> context, const ContextInfo& info) {
510
9857
    auto name_buffer = Utf8ToStringView(info.name);
511
9856
    auto origin_buffer = Utf8ToStringView(info.origin);
512
9857
    std::unique_ptr<StringBuffer> aux_data_buffer;
513
514
    v8_inspector::V8ContextInfo v8info(
515
4929
        context, CONTEXT_GROUP_ID, name_buffer->string());
516
4928
    v8info.origin = origin_buffer->string();
517
518
4928
    if (info.is_default) {
519
4367
      aux_data_buffer = Utf8ToStringView("{\"isDefault\":true}");
520
    } else {
521
561
      aux_data_buffer = Utf8ToStringView("{\"isDefault\":false}");
522
    }
523
4929
    v8info.auxData = aux_data_buffer->string();
524
525
4929
    client_->contextCreated(v8info);
526
4929
  }
527
528
4354
  void contextDestroyed(Local<Context> context) {
529
4354
    client_->contextDestroyed(context);
530
4354
  }
531
532
24
  void quitMessageLoopOnPause() override {
533
24
    waiting_for_resume_ = false;
534
24
  }
535
536
20
  void runIfWaitingForDebugger(int context_group_id) override {
537
20
    waiting_for_frontend_ = false;
538
20
  }
539
540
4673
  int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,
541
                      bool prevent_shutdown) {
542
4673
    int session_id = next_session_id_++;
543
9349
    channels_[session_id] = std::make_unique<ChannelImpl>(env_,
544
                                                          client_,
545
9350
                                                          getWorkerManager(),
546
4675
                                                          std::move(delegate),
547
9348
                                                          getThreadHandle(),
548
4675
                                                          prevent_shutdown);
549
4675
    return session_id;
550
  }
551
552
288
  void disconnectFrontend(int session_id) {
553
288
    auto it = channels_.find(session_id);
554
288
    if (it == channels_.end())
555
      return;
556
288
    bool retaining_context = it->second->retainingContext();
557
288
    channels_.erase(it);
558
288
    if (retaining_context) {
559
6
      for (const auto& id_channel : channels_) {
560
3
        if (id_channel.second->retainingContext())
561
          return;
562
      }
563
3
      contextDestroyed(env_->context());
564
    }
565

288
    if (waiting_for_sessions_disconnect_ && !is_main_)
566
2
      waiting_for_sessions_disconnect_ = false;
567
  }
568
569
13743
  void dispatchMessageFromFrontend(int session_id, const StringView& message) {
570
13743
    channels_[session_id]->dispatchProtocolMessage(message);
571
13745
  }
572
573
60
  Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
574
60
    return env_->context();
575
  }
576
577
4
  void installAdditionalCommandLineAPI(Local<Context> context,
578
                                       Local<Object> target) override {
579
4
    Local<Function> installer = env_->inspector_console_extension_installer();
580
4
    if (!installer.IsEmpty()) {
581
8
      Local<Value> argv[] = {target};
582
      // If there is an exception, proceed in JS land
583
8
      USE(installer->Call(context, target, arraysize(argv), argv));
584
    }
585
4
  }
586
587
3
  void ReportUncaughtException(Local<Value> error, Local<Message> message) {
588
3
    Isolate* isolate = env_->isolate();
589
3
    Local<Context> context = env_->context();
590
591
9
    int script_id = message->GetScriptOrigin().ScriptID()->Value();
592
593
3
    Local<v8::StackTrace> stack_trace = message->GetStackTrace();
594
595


8
    if (!stack_trace.IsEmpty() && stack_trace->GetFrameCount() > 0 &&
596
5
        script_id == stack_trace->GetFrame(isolate, 0)->GetScriptId()) {
597
1
      script_id = 0;
598
    }
599
600
3
    const uint8_t DETAILS[] = "Uncaught";
601
602
15
    client_->exceptionThrown(
603
        context,
604
6
        StringView(DETAILS, sizeof(DETAILS) - 1),
605
        error,
606
9
        ToProtocolString(isolate, message->Get())->string(),
607
6
        ToProtocolString(isolate, message->GetScriptResourceName())->string(),
608
12
        message->GetLineNumber(context).FromMaybe(0),
609
12
        message->GetStartColumn(context).FromMaybe(0),
610
6
        client_->createStackTrace(stack_trace),
611
6
        script_id);
612
3
  }
613
614
2
  void startRepeatingTimer(double interval_s,
615
                           TimerCallback callback,
616
                           void* data) override {
617
2
    timers_.emplace(std::piecewise_construct, std::make_tuple(data),
618
4
                    std::make_tuple(env_, interval_s, callback,
619
2
                                    data));
620
2
  }
621
622
2
  void cancelTimer(void* data) override {
623
2
    timers_.erase(data);
624
2
  }
625
626
  // Async stack traces instrumentation.
627
1
  void AsyncTaskScheduled(const StringView& task_name, void* task,
628
                          bool recurring) {
629
1
    client_->asyncTaskScheduled(task_name, task, recurring);
630
1
  }
631
632
1
  void AsyncTaskCanceled(void* task) {
633
1
    client_->asyncTaskCanceled(task);
634
1
  }
635
636
4
  void AsyncTaskStarted(void* task) {
637
4
    client_->asyncTaskStarted(task);
638
4
  }
639
640
1
  void AsyncTaskFinished(void* task) {
641
1
    client_->asyncTaskFinished(task);
642
1
  }
643
644
  void AllAsyncTasksCanceled() {
645
    client_->allAsyncTasksCanceled();
646
  }
647
648
15
  void schedulePauseOnNextStatement(const std::string& reason) {
649
45
    for (const auto& id_channel : channels_) {
650
30
      id_channel.second->schedulePauseOnNextStatement(reason);
651
    }
652
15
  }
653
654
4503
  bool hasConnectedSessions() {
655
9017
    for (const auto& id_channel : channels_) {
656
      // Other sessions are "invisible" more most purposes
657
4597
      if (id_channel.second->preventShutdown())
658
83
        return true;
659
    }
660
4420
    return false;
661
  }
662
663
4354
  bool notifyWaitingForDisconnect() {
664
4354
    bool retaining_context = false;
665
8750
    for (const auto& id_channel : channels_) {
666
4396
      if (id_channel.second->notifyWaitingForDisconnect())
667
3
        retaining_context = true;
668
    }
669
4354
    return retaining_context;
670
  }
671
672
9115
  std::shared_ptr<MainThreadHandle> getThreadHandle() {
673
9115
    if (!interface_) {
674
8728
      interface_ = std::make_shared<MainThreadInterface>(
675
8728
          env_->inspector_agent(), env_->event_loop(), env_->isolate(),
676
13090
          env_->isolate_data()->platform());
677
    }
678
9116
    return interface_->GetHandle();
679
  }
680
681
5359
  std::shared_ptr<WorkerManager> getWorkerManager() {
682
5359
    if (!is_main_) {
683
241
      return nullptr;
684
    }
685
5118
    if (worker_manager_ == nullptr) {
686
      worker_manager_ =
687
4134
          std::make_shared<WorkerManager>(getThreadHandle());
688
    }
689
5118
    return worker_manager_;
690
  }
691
692
31408
  bool IsActive() {
693
31408
    return !channels_.empty();
694
  }
695
696
 private:
697
258
  bool shouldRunMessageLoop() {
698
258
    if (waiting_for_frontend_)
699
65
      return true;
700

193
    if (waiting_for_sessions_disconnect_ || waiting_for_resume_) {
701
149
      return hasConnectedSessions();
702
    }
703
44
    return false;
704
  }
705
706
132
  void runMessageLoop() {
707
132
    if (running_nested_loop_)
708
      return;
709
710
132
    running_nested_loop_ = true;
711
712
132
    MultiIsolatePlatform* platform = env_->isolate_data()->platform();
713
384
    while (shouldRunMessageLoop()) {
714
126
      if (interface_) interface_->WaitForFrontendEvent();
715
304
      while (platform->FlushForegroundTasks(env_->isolate())) {}
716
    }
717
132
    running_nested_loop_ = false;
718
  }
719
720
27159
  double currentTimeMS() override {
721
27159
    return env_->isolate_data()->platform()->CurrentClockTimeMillis();
722
  }
723
724
459577
  std::unique_ptr<StringBuffer> resourceNameToUrl(
725
      const StringView& resource_name_view) override {
726
    std::string resource_name =
727
919154
        protocol::StringUtil::StringViewToUtf8(resource_name_view);
728
459573
    if (!IsFilePath(resource_name))
729
434931
      return nullptr;
730
49288
    node::url::URL url = node::url::URL::FromFilePath(resource_name);
731
    // TODO(ak239spb): replace this code with url.href().
732
    // Refs: https://github.com/nodejs/node/issues/22610
733
24645
    return Utf8ToStringView(url.protocol() + "//" + url.path());
734
  }
735
736
  node::Environment* env_;
737
  bool is_main_;
738
  bool running_nested_loop_ = false;
739
  std::unique_ptr<V8Inspector> client_;
740
  std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
741
  std::unordered_map<void*, InspectorTimerHandle> timers_;
742
  int next_session_id_ = 1;
743
  bool waiting_for_resume_ = false;
744
  bool waiting_for_frontend_ = false;
745
  bool waiting_for_sessions_disconnect_ = false;
746
  // Allows accessing Inspector from non-main threads
747
  std::shared_ptr<MainThreadInterface> interface_;
748
  std::shared_ptr<WorkerManager> worker_manager_;
749
};
750
751
4393
Agent::Agent(Environment* env)
752
    : parent_env_(env),
753
8786
      debug_options_(env->options()->debug_options()),
754
21965
      host_port_(env->inspector_host_port()) {}
755
756
11964
Agent::~Agent() {}
757
758
4368
bool Agent::Start(const std::string& path,
759
                  const DebugOptions& options,
760
                  std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
761
                  bool is_main) {
762
4368
  path_ = path;
763
4368
  debug_options_ = options;
764
4368
  CHECK_NOT_NULL(host_port);
765
4368
  host_port_ = host_port;
766
767
4368
  client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
768
4368
  if (parent_env_->owns_inspector()) {
769
8280
    Mutex::ScopedLock lock(start_io_thread_async_mutex);
770
4140
    CHECK_EQ(start_io_thread_async_initialized.exchange(true), false);
771
4140
    CHECK_EQ(0, uv_async_init(parent_env_->event_loop(),
772
                              &start_io_thread_async,
773
                              StartIoThreadAsyncCallback));
774
4140
    uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
775
4140
    start_io_thread_async.data = this;
776
    // Ignore failure, SIGUSR1 won't work, but that should not block node start.
777
4140
    StartDebugSignalHandler();
778
779
19890
    parent_env_->AddCleanupHook([](void* data) {
780
3735
      Environment* env = static_cast<Environment*>(data);
781
782
      {
783
7470
        Mutex::ScopedLock lock(start_io_thread_async_mutex);
784
3735
        start_io_thread_async.data = nullptr;
785
      }
786
787
      // This is global, will never get freed
788
7470
      env->CloseHandle(&start_io_thread_async, [](uv_async_t*) {
789
3735
        CHECK(start_io_thread_async_initialized.exchange(false));
790
7470
      });
791
19890
    }, parent_env_);
792
  }
793
794
21820
  AtExit(parent_env_, [](void* env) {
795
4358
    Agent* agent = static_cast<Environment*>(env)->inspector_agent();
796
4358
    if (agent->IsActive()) {
797
4351
      agent->WaitForDisconnect();
798
    }
799
21820
  }, parent_env_);
800
801
4368
  bool wait_for_connect = options.wait_for_connect();
802
4367
  if (parent_handle_) {
803
228
    wait_for_connect = parent_handle_->WaitForConnect();
804
228
    parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
805

4140
  } else if (!options.inspector_enabled || !StartIoThread()) {
806
4067
    return false;
807
  }
808
809
  // Patch the debug options to implement waitForDebuggerOnStart for
810
  // the NodeWorker.enable method.
811
301
  if (wait_for_connect) {
812
16
    CHECK(!parent_env_->has_serialized_options());
813
16
    debug_options_.EnableBreakFirstLine();
814
16
    parent_env_->options()->get_debug_options()->EnableBreakFirstLine();
815
16
    client_->waitForFrontend();
816
  }
817
301
  return true;
818
}
819
820
81
bool Agent::StartIoThread() {
821
81
  if (io_ != nullptr)
822
3
    return true;
823
824
78
  CHECK_NOT_NULL(client_);
825
826
156
  io_ = InspectorIo::Start(client_->getThreadHandle(),
827
                           path_,
828
                           host_port_,
829
78
                           debug_options_.inspect_publish_uid);
830
78
  if (io_ == nullptr) {
831
1
    return false;
832
  }
833
77
  NotifyClusterWorkersDebugEnabled(parent_env_);
834
77
  return true;
835
}
836
837
4
void Agent::Stop() {
838
4
  io_.reset();
839
4
}
840
841
4674
std::unique_ptr<InspectorSession> Agent::Connect(
842
    std::unique_ptr<InspectorSessionDelegate> delegate,
843
    bool prevent_shutdown) {
844
4674
  CHECK_NOT_NULL(client_);
845
9349
  int session_id = client_->connectFrontend(std::move(delegate),
846
4672
                                            prevent_shutdown);
847
  return std::unique_ptr<InspectorSession>(
848
4675
      new SameThreadInspectorSession(session_id, client_));
849
}
850
851
2
std::unique_ptr<InspectorSession> Agent::ConnectToMainThread(
852
    std::unique_ptr<InspectorSessionDelegate> delegate,
853
    bool prevent_shutdown) {
854
2
  CHECK_NOT_NULL(parent_handle_);
855
2
  CHECK_NOT_NULL(client_);
856
  auto thread_safe_delegate =
857
4
      client_->getThreadHandle()->MakeDelegateThreadSafe(std::move(delegate));
858
2
  return parent_handle_->Connect(std::move(thread_safe_delegate),
859
6
                                 prevent_shutdown);
860
}
861
862
4354
void Agent::WaitForDisconnect() {
863
4354
  CHECK_NOT_NULL(client_);
864
4354
  bool is_worker = parent_handle_ != nullptr;
865
4354
  parent_handle_.reset();
866

4354
  if (client_->hasConnectedSessions() && !is_worker) {
867
11
    fprintf(stderr, "Waiting for the debugger to disconnect...\n");
868
11
    fflush(stderr);
869
  }
870
4354
  if (!client_->notifyWaitingForDisconnect()) {
871
4351
    client_->contextDestroyed(parent_env_->context());
872
3
  } else if (is_worker) {
873
2
    client_->waitForSessionsDisconnect();
874
  }
875
4354
  if (io_ != nullptr) {
876
76
    io_->StopAcceptingNewConnections();
877
76
    client_->waitForSessionsDisconnect();
878
  }
879
4354
}
880
881
199
void Agent::ReportUncaughtException(Local<Value> error,
882
                                    Local<Message> message) {
883
199
  if (!IsListening())
884
196
    return;
885
3
  client_->ReportUncaughtException(error, message);
886
3
  WaitForDisconnect();
887
}
888
889
15
void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
890
15
  client_->schedulePauseOnNextStatement(reason);
891
15
}
892
893
4368
void Agent::RegisterAsyncHook(Isolate* isolate,
894
                              Local<Function> enable_function,
895
                              Local<Function> disable_function) {
896
4368
  enable_async_hook_function_.Reset(isolate, enable_function);
897
4368
  disable_async_hook_function_.Reset(isolate, disable_function);
898
4368
  if (pending_enable_async_hook_) {
899
2
    CHECK(!pending_disable_async_hook_);
900
2
    pending_enable_async_hook_ = false;
901
2
    EnableAsyncHook();
902
4366
  } else if (pending_disable_async_hook_) {
903
    CHECK(!pending_enable_async_hook_);
904
    pending_disable_async_hook_ = false;
905
    DisableAsyncHook();
906
  }
907
4368
}
908
909
8
void Agent::EnableAsyncHook() {
910
16
  if (!enable_async_hook_function_.IsEmpty()) {
911
6
    ToggleAsyncHook(parent_env_->isolate(), enable_async_hook_function_);
912
2
  } else if (pending_disable_async_hook_) {
913
    CHECK(!pending_enable_async_hook_);
914
    pending_disable_async_hook_ = false;
915
  } else {
916
2
    pending_enable_async_hook_ = true;
917
  }
918
8
}
919
920
4
void Agent::DisableAsyncHook() {
921
8
  if (!disable_async_hook_function_.IsEmpty()) {
922
4
    ToggleAsyncHook(parent_env_->isolate(), disable_async_hook_function_);
923
  } else if (pending_enable_async_hook_) {
924
    CHECK(!pending_disable_async_hook_);
925
    pending_enable_async_hook_ = false;
926
  } else {
927
    pending_disable_async_hook_ = true;
928
  }
929
4
}
930
931
10
void Agent::ToggleAsyncHook(Isolate* isolate,
932
                            const Global<Function>& fn) {
933
10
  CHECK(parent_env_->has_run_bootstrapping_code());
934
20
  HandleScope handle_scope(isolate);
935
20
  CHECK(!fn.IsEmpty());
936
10
  auto context = parent_env_->context();
937
20
  v8::TryCatch try_catch(isolate);
938
30
  USE(fn.Get(isolate)->Call(context, Undefined(isolate), 0, nullptr));
939
10
  if (try_catch.HasCaught()) {
940
    PrintCaughtException(isolate, context, try_catch);
941
    FatalError("\nnode::inspector::Agent::ToggleAsyncHook",
942
               "Cannot toggle Inspector's AsyncHook, please report this.");
943
  }
944
10
}
945
946
1
void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
947
                               bool recurring) {
948
1
  client_->AsyncTaskScheduled(task_name, task, recurring);
949
1
}
950
951
1
void Agent::AsyncTaskCanceled(void* task) {
952
1
  client_->AsyncTaskCanceled(task);
953
1
}
954
955
4
void Agent::AsyncTaskStarted(void* task) {
956
4
  client_->AsyncTaskStarted(task);
957
4
}
958
959
1
void Agent::AsyncTaskFinished(void* task) {
960
1
  client_->AsyncTaskFinished(task);
961
1
}
962
963
void Agent::AllAsyncTasksCanceled() {
964
  client_->AllAsyncTasksCanceled();
965
}
966
967
1
void Agent::RequestIoThreadStart() {
968
  // We need to attempt to interrupt V8 flow (in case Node is running
969
  // continuous JS code) and to wake up libuv thread (in case Node is waiting
970
  // for IO events)
971
1
  CHECK(start_io_thread_async_initialized);
972
1
  uv_async_send(&start_io_thread_async);
973
1
  Isolate* isolate = parent_env_->isolate();
974
1
  v8::Platform* platform = parent_env_->isolate_data()->platform();
975
  std::shared_ptr<TaskRunner> taskrunner =
976
2
    platform->GetForegroundTaskRunner(isolate);
977
1
  taskrunner->PostTask(std::make_unique<StartIoTask>(this));
978
1
  isolate->RequestInterrupt(StartIoInterrupt, this);
979
1
  CHECK(start_io_thread_async_initialized);
980
1
  uv_async_send(&start_io_thread_async);
981
1
}
982
983
4954
void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
984
4954
  if (client_ == nullptr)  // This happens for a main context
985
4393
    return;
986
561
  client_->contextCreated(context, info);
987
}
988
989
bool Agent::WillWaitForConnect() {
990
  if (debug_options_.wait_for_connect()) return true;
991
  if (parent_handle_)
992
    return parent_handle_->WaitForConnect();
993
  return false;
994
}
995
996
31522
bool Agent::IsActive() {
997
31522
  if (client_ == nullptr)
998
    return false;
999

31522
  return io_ != nullptr || client_->IsActive();
1000
}
1001
1002
228
void Agent::SetParentHandle(
1003
    std::unique_ptr<ParentInspectorHandle> parent_handle) {
1004
228
  parent_handle_ = std::move(parent_handle);
1005
228
}
1006
1007
230
std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
1008
    int thread_id, const std::string& url) {
1009
230
  if (!parent_handle_) {
1010
226
    return client_->getWorkerManager()->NewParentHandle(thread_id, url);
1011
  } else {
1012
4
    return parent_handle_->NewParentInspectorHandle(thread_id, url);
1013
  }
1014
}
1015
1016
2
void Agent::WaitForConnect() {
1017
2
  CHECK_NOT_NULL(client_);
1018
2
  client_->waitForFrontend();
1019
2
}
1020
1021
458
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
1022
458
  CHECK_NOT_NULL(client_);
1023
458
  return client_->getWorkerManager();
1024
}
1025
1026
9
std::string Agent::GetWsUrl() const {
1027
9
  if (io_ == nullptr)
1028
2
    return "";
1029
7
  return io_->GetWsUrl();
1030
}
1031
1032
12807
SameThreadInspectorSession::~SameThreadInspectorSession() {
1033
8538
  auto client = client_.lock();
1034
4269
  if (client)
1035
288
    client->disconnectFrontend(session_id_);
1036
8538
}
1037
1038
13742
void SameThreadInspectorSession::Dispatch(
1039
    const v8_inspector::StringView& message) {
1040
27487
  auto client = client_.lock();
1041
13745
  if (client)
1042
13745
    client->dispatchMessageFromFrontend(session_id_, message);
1043
13745
}
1044
1045
}  // namespace inspector
1046

12606
}  // namespace node