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: 491 523 93.9 %
Date: 2020-06-24 22:13:30 Branches: 151 208 72.6 %

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

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

288
    if (waiting_for_sessions_disconnect_ && !is_main_)
554
2
      waiting_for_sessions_disconnect_ = false;
555
  }
556
557
14781
  void dispatchMessageFromFrontend(int session_id, const StringView& message) {
558
14781
    channels_[session_id]->dispatchProtocolMessage(message);
559
14782
  }
560
561
62
  Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
562
62
    return env_->context();
563
  }
564
565
3
  void installAdditionalCommandLineAPI(Local<Context> context,
566
                                       Local<Object> target) override {
567
3
    Local<Function> installer = env_->inspector_console_extension_installer();
568
3
    if (!installer.IsEmpty()) {
569
6
      Local<Value> argv[] = {target};
570
      // If there is an exception, proceed in JS land
571
6
      USE(installer->Call(context, target, arraysize(argv), argv));
572
    }
573
3
  }
574
575
2
  void ReportUncaughtException(Local<Value> error, Local<Message> message) {
576
2
    Isolate* isolate = env_->isolate();
577
2
    Local<Context> context = env_->context();
578
579
6
    int script_id = message->GetScriptOrigin().ScriptID()->Value();
580
581
2
    Local<v8::StackTrace> stack_trace = message->GetStackTrace();
582
583


6
    if (!stack_trace.IsEmpty() && stack_trace->GetFrameCount() > 0 &&
584
4
        script_id == stack_trace->GetFrame(isolate, 0)->GetScriptId()) {
585
1
      script_id = 0;
586
    }
587
588
2
    const uint8_t DETAILS[] = "Uncaught";
589
590
12
    client_->exceptionThrown(
591
        context,
592
        StringView(DETAILS, sizeof(DETAILS) - 1),
593
        error,
594
6
        ToProtocolString(isolate, message->Get())->string(),
595
4
        ToProtocolString(isolate, message->GetScriptResourceName())->string(),
596
8
        message->GetLineNumber(context).FromMaybe(0),
597
8
        message->GetStartColumn(context).FromMaybe(0),
598
4
        client_->createStackTrace(stack_trace),
599
4
        script_id);
600
2
  }
601
602
2
  void startRepeatingTimer(double interval_s,
603
                           TimerCallback callback,
604
                           void* data) override {
605
2
    timers_.emplace(std::piecewise_construct, std::make_tuple(data),
606
4
                    std::make_tuple(env_, interval_s, callback,
607
2
                                    data));
608
2
  }
609
610
2
  void cancelTimer(void* data) override {
611
2
    timers_.erase(data);
612
2
  }
613
614
  // Async stack traces instrumentation.
615
1
  void AsyncTaskScheduled(const StringView& task_name, void* task,
616
                          bool recurring) {
617
1
    client_->asyncTaskScheduled(task_name, task, recurring);
618
1
  }
619
620
1
  void AsyncTaskCanceled(void* task) {
621
1
    client_->asyncTaskCanceled(task);
622
1
  }
623
624
4
  void AsyncTaskStarted(void* task) {
625
4
    client_->asyncTaskStarted(task);
626
4
  }
627
628
1
  void AsyncTaskFinished(void* task) {
629
1
    client_->asyncTaskFinished(task);
630
1
  }
631
632
  void AllAsyncTasksCanceled() {
633
    client_->allAsyncTasksCanceled();
634
  }
635
636
19
  void schedulePauseOnNextStatement(const std::string& reason) {
637
57
    for (const auto& id_channel : channels_) {
638
38
      id_channel.second->schedulePauseOnNextStatement(reason);
639
    }
640
19
  }
641
642
4844
  bool hasConnectedSessions() {
643
9718
    for (const auto& id_channel : channels_) {
644
      // Other sessions are "invisible" more most purposes
645
4967
      if (id_channel.second->preventShutdown())
646
93
        return true;
647
    }
648
4751
    return false;
649
  }
650
651
4689
  bool notifyWaitingForDisconnect() {
652
4689
    bool retaining_context = false;
653
9434
    for (const auto& id_channel : channels_) {
654
4745
      if (id_channel.second->notifyWaitingForDisconnect())
655
3
        retaining_context = true;
656
    }
657
4689
    return retaining_context;
658
  }
659
660
9801
  std::shared_ptr<MainThreadHandle> getThreadHandle() {
661
9801
    if (!interface_) {
662
9398
      interface_ = std::make_shared<MainThreadInterface>(
663
14097
          env_->inspector_agent());
664
    }
665
9801
    return interface_->GetHandle();
666
  }
667
668
6443
  std::shared_ptr<WorkerManager> getWorkerManager() {
669
6443
    if (!is_main_) {
670
351
      return nullptr;
671
    }
672
6092
    if (worker_manager_ == nullptr) {
673
      worker_manager_ =
674
4361
          std::make_shared<WorkerManager>(getThreadHandle());
675
    }
676
6092
    return worker_manager_;
677
  }
678
679
45059
  bool IsActive() {
680
45059
    return !channels_.empty();
681
  }
682
683
 private:
684
267
  bool shouldRunMessageLoop() {
685
267
    if (waiting_for_frontend_)
686
63
      return true;
687

204
    if (waiting_for_sessions_disconnect_ || waiting_for_resume_) {
688
155
      return hasConnectedSessions();
689
    }
690
49
    return false;
691
  }
692
693
136
  void runMessageLoop() {
694
136
    if (running_nested_loop_)
695
      return;
696
697
136
    running_nested_loop_ = true;
698
699
398
    while (shouldRunMessageLoop()) {
700
131
      if (interface_) interface_->WaitForFrontendEvent();
701
131
      env_->RunAndClearInterrupts();
702
    }
703
136
    running_nested_loop_ = false;
704
  }
705
706
40465
  double currentTimeMS() override {
707
40465
    return env_->isolate_data()->platform()->CurrentClockTimeMillis();
708
  }
709
710
519355
  std::unique_ptr<StringBuffer> resourceNameToUrl(
711
      const StringView& resource_name_view) override {
712
    std::string resource_name =
713
1038710
        protocol::StringUtil::StringViewToUtf8(resource_name_view);
714
519353
    if (!IsFilePath(resource_name))
715
493893
      return nullptr;
716
50920
    node::url::URL url = node::url::URL::FromFilePath(resource_name);
717
    // TODO(ak239spb): replace this code with url.href().
718
    // Refs: https://github.com/nodejs/node/issues/22610
719
25460
    return Utf8ToStringView(url.protocol() + "//" + url.path());
720
  }
721
722
  node::Environment* env_;
723
  bool is_main_;
724
  bool running_nested_loop_ = false;
725
  std::unique_ptr<V8Inspector> client_;
726
  // Note: ~ChannelImpl may access timers_ so timers_ has to come first.
727
  std::unordered_map<void*, InspectorTimerHandle> timers_;
728
  std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
729
  int next_session_id_ = 1;
730
  bool waiting_for_resume_ = false;
731
  bool waiting_for_frontend_ = false;
732
  bool waiting_for_sessions_disconnect_ = false;
733
  // Allows accessing Inspector from non-main threads
734
  std::shared_ptr<MainThreadInterface> interface_;
735
  std::shared_ptr<WorkerManager> worker_manager_;
736
};
737
738
4703
Agent::Agent(Environment* env)
739
    : parent_env_(env),
740
9406
      debug_options_(env->options()->debug_options()),
741
23515
      host_port_(env->inspector_host_port()) {}
742
743
12699
Agent::~Agent() {}
744
745
4703
bool Agent::Start(const std::string& path,
746
                  const DebugOptions& options,
747
                  std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
748
                  bool is_main) {
749
4703
  path_ = path;
750
4703
  debug_options_ = options;
751
4703
  CHECK_NOT_NULL(host_port);
752
4703
  host_port_ = host_port;
753
754
4703
  client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
755
4703
  if (parent_env_->owns_inspector()) {
756
8732
    Mutex::ScopedLock lock(start_io_thread_async_mutex);
757
4366
    CHECK_EQ(start_io_thread_async_initialized.exchange(true), false);
758
4366
    CHECK_EQ(0, uv_async_init(parent_env_->event_loop(),
759
                              &start_io_thread_async,
760
                              StartIoThreadAsyncCallback));
761
4366
    uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
762
4366
    start_io_thread_async.data = this;
763
    // Ignore failure, SIGUSR1 won't work, but that should not block node start.
764
4366
    StartDebugSignalHandler();
765
766
20890
    parent_env_->AddCleanupHook([](void* data) {
767
3896
      Environment* env = static_cast<Environment*>(data);
768
769
      {
770
7792
        Mutex::ScopedLock lock(start_io_thread_async_mutex);
771
3896
        start_io_thread_async.data = nullptr;
772
      }
773
774
      // This is global, will never get freed
775
7792
      env->CloseHandle(&start_io_thread_async, [](uv_async_t*) {
776
3896
        CHECK(start_io_thread_async_initialized.exchange(false));
777
7792
      });
778
20890
    }, parent_env_);
779
  }
780
781
23494
  AtExit(parent_env_, [](void* env) {
782
4692
    Agent* agent = static_cast<Environment*>(env)->inspector_agent();
783
4693
    if (agent->IsActive()) {
784
4687
      agent->WaitForDisconnect();
785
    }
786
23494
  }, parent_env_);
787
788
4703
  bool wait_for_connect = options.wait_for_connect();
789
4703
  if (parent_handle_) {
790
336
    wait_for_connect = parent_handle_->WaitForConnect();
791
336
    parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
792

4367
  } else if (!options.inspector_enabled || !StartIoThread()) {
793
4291
    return false;
794
  }
795
796
  // Patch the debug options to implement waitForDebuggerOnStart for
797
  // the NodeWorker.enable method.
798
412
  if (wait_for_connect) {
799
19
    CHECK(!parent_env_->has_serialized_options());
800
19
    debug_options_.EnableBreakFirstLine();
801
19
    parent_env_->options()->get_debug_options()->EnableBreakFirstLine();
802
19
    client_->waitForFrontend();
803
  }
804
412
  return true;
805
}
806
807
79
bool Agent::StartIoThread() {
808
79
  if (io_ != nullptr)
809
1
    return true;
810
811
78
  CHECK_NOT_NULL(client_);
812
813
156
  io_ = InspectorIo::Start(client_->getThreadHandle(),
814
                           path_,
815
                           host_port_,
816
78
                           debug_options_.inspect_publish_uid);
817
78
  if (io_ == nullptr) {
818
1
    return false;
819
  }
820
77
  NotifyClusterWorkersDebugEnabled(parent_env_);
821
77
  return true;
822
}
823
824
4
void Agent::Stop() {
825
4
  io_.reset();
826
4
}
827
828
5024
std::unique_ptr<InspectorSession> Agent::Connect(
829
    std::unique_ptr<InspectorSessionDelegate> delegate,
830
    bool prevent_shutdown) {
831
5024
  CHECK_NOT_NULL(client_);
832
10048
  int session_id = client_->connectFrontend(std::move(delegate),
833
5024
                                            prevent_shutdown);
834
  return std::unique_ptr<InspectorSession>(
835
5024
      new SameThreadInspectorSession(session_id, client_));
836
}
837
838
2
std::unique_ptr<InspectorSession> Agent::ConnectToMainThread(
839
    std::unique_ptr<InspectorSessionDelegate> delegate,
840
    bool prevent_shutdown) {
841
2
  CHECK_NOT_NULL(parent_handle_);
842
2
  CHECK_NOT_NULL(client_);
843
  auto thread_safe_delegate =
844
4
      client_->getThreadHandle()->MakeDelegateThreadSafe(std::move(delegate));
845
2
  return parent_handle_->Connect(std::move(thread_safe_delegate),
846
6
                                 prevent_shutdown);
847
}
848
849
4689
void Agent::WaitForDisconnect() {
850
4689
  CHECK_NOT_NULL(client_);
851
4689
  bool is_worker = parent_handle_ != nullptr;
852
4689
  parent_handle_.reset();
853

4689
  if (client_->hasConnectedSessions() && !is_worker) {
854
13
    fprintf(stderr, "Waiting for the debugger to disconnect...\n");
855
13
    fflush(stderr);
856
  }
857
4689
  if (!client_->notifyWaitingForDisconnect()) {
858
4686
    client_->contextDestroyed(parent_env_->context());
859
3
  } else if (is_worker) {
860
2
    client_->waitForSessionsDisconnect();
861
  }
862
4689
  if (io_ != nullptr) {
863
75
    io_->StopAcceptingNewConnections();
864
75
    client_->waitForSessionsDisconnect();
865
  }
866
4688
}
867
868
208
void Agent::ReportUncaughtException(Local<Value> error,
869
                                    Local<Message> message) {
870
208
  if (!IsListening())
871
206
    return;
872
2
  client_->ReportUncaughtException(error, message);
873
2
  WaitForDisconnect();
874
}
875
876
19
void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
877
19
  client_->schedulePauseOnNextStatement(reason);
878
19
}
879
880
4680
void Agent::RegisterAsyncHook(Isolate* isolate,
881
                              Local<Function> enable_function,
882
                              Local<Function> disable_function) {
883
4680
  enable_async_hook_function_.Reset(isolate, enable_function);
884
4680
  disable_async_hook_function_.Reset(isolate, disable_function);
885
4680
  if (pending_enable_async_hook_) {
886
2
    CHECK(!pending_disable_async_hook_);
887
2
    pending_enable_async_hook_ = false;
888
2
    EnableAsyncHook();
889
4678
  } else if (pending_disable_async_hook_) {
890
    CHECK(!pending_enable_async_hook_);
891
    pending_disable_async_hook_ = false;
892
    DisableAsyncHook();
893
  }
894
4680
}
895
896
8
void Agent::EnableAsyncHook() {
897
16
  if (!enable_async_hook_function_.IsEmpty()) {
898
6
    ToggleAsyncHook(parent_env_->isolate(), enable_async_hook_function_);
899
2
  } else if (pending_disable_async_hook_) {
900
    CHECK(!pending_enable_async_hook_);
901
    pending_disable_async_hook_ = false;
902
  } else {
903
2
    pending_enable_async_hook_ = true;
904
  }
905
8
}
906
907
3
void Agent::DisableAsyncHook() {
908
6
  if (!disable_async_hook_function_.IsEmpty()) {
909
3
    ToggleAsyncHook(parent_env_->isolate(), disable_async_hook_function_);
910
  } else if (pending_enable_async_hook_) {
911
    CHECK(!pending_disable_async_hook_);
912
    pending_enable_async_hook_ = false;
913
  } else {
914
    pending_disable_async_hook_ = true;
915
  }
916
3
}
917
918
9
void Agent::ToggleAsyncHook(Isolate* isolate,
919
                            const Global<Function>& fn) {
920
9
  CHECK(parent_env_->has_run_bootstrapping_code());
921
18
  HandleScope handle_scope(isolate);
922
18
  CHECK(!fn.IsEmpty());
923
9
  auto context = parent_env_->context();
924
18
  v8::TryCatch try_catch(isolate);
925
27
  USE(fn.Get(isolate)->Call(context, Undefined(isolate), 0, nullptr));
926
9
  if (try_catch.HasCaught()) {
927
    PrintCaughtException(isolate, context, try_catch);
928
    FatalError("\nnode::inspector::Agent::ToggleAsyncHook",
929
               "Cannot toggle Inspector's AsyncHook, please report this.");
930
  }
931
9
}
932
933
1
void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
934
                               bool recurring) {
935
1
  client_->AsyncTaskScheduled(task_name, task, recurring);
936
1
}
937
938
1
void Agent::AsyncTaskCanceled(void* task) {
939
1
  client_->AsyncTaskCanceled(task);
940
1
}
941
942
4
void Agent::AsyncTaskStarted(void* task) {
943
4
  client_->AsyncTaskStarted(task);
944
4
}
945
946
1
void Agent::AsyncTaskFinished(void* task) {
947
1
  client_->AsyncTaskFinished(task);
948
1
}
949
950
void Agent::AllAsyncTasksCanceled() {
951
  client_->AllAsyncTasksCanceled();
952
}
953
954
1
void Agent::RequestIoThreadStart() {
955
  // We need to attempt to interrupt V8 flow (in case Node is running
956
  // continuous JS code) and to wake up libuv thread (in case Node is waiting
957
  // for IO events)
958
1
  CHECK(start_io_thread_async_initialized);
959
1
  uv_async_send(&start_io_thread_async);
960
3
  parent_env_->RequestInterrupt([this](Environment*) {
961
1
    StartIoThread();
962
2
  });
963
964
1
  CHECK(start_io_thread_async_initialized);
965
1
  uv_async_send(&start_io_thread_async);
966
1
}
967
968
5266
void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
969
5266
  if (client_ == nullptr)  // This happens for a main context
970
4703
    return;
971
563
  client_->contextCreated(context, info);
972
}
973
974
bool Agent::WillWaitForConnect() {
975
  if (debug_options_.wait_for_connect()) return true;
976
  if (parent_handle_)
977
    return parent_handle_->WaitForConnect();
978
  return false;
979
}
980
981
45169
bool Agent::IsActive() {
982
45169
  if (client_ == nullptr)
983
    return false;
984

45169
  return io_ != nullptr || client_->IsActive();
985
}
986
987
336
void Agent::SetParentHandle(
988
    std::unique_ptr<ParentInspectorHandle> parent_handle) {
989
336
  parent_handle_ = std::move(parent_handle);
990
336
}
991
992
555
std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
993
    int thread_id, const std::string& url) {
994
555
  if (!parent_handle_) {
995
549
    return client_->getWorkerManager()->NewParentHandle(thread_id, url);
996
  } else {
997
6
    return parent_handle_->NewParentInspectorHandle(thread_id, url);
998
  }
999
}
1000
1001
void Agent::WaitForConnect() {
1002
  CHECK_NOT_NULL(client_);
1003
  client_->waitForFrontend();
1004
}
1005
1006
870
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
1007
870
  CHECK_NOT_NULL(client_);
1008
870
  return client_->getWorkerManager();
1009
}
1010
1011
6
std::string Agent::GetWsUrl() const {
1012
6
  if (io_ == nullptr)
1013
1
    return "";
1014
5
  return io_->GetWsUrl();
1015
}
1016
1017
13655
SameThreadInspectorSession::~SameThreadInspectorSession() {
1018
9104
  auto client = client_.lock();
1019
4552
  if (client)
1020
288
    client->disconnectFrontend(session_id_);
1021
9103
}
1022
1023
14781
void SameThreadInspectorSession::Dispatch(
1024
    const v8_inspector::StringView& message) {
1025
29563
  auto client = client_.lock();
1026
14781
  if (client)
1027
14781
    client->dispatchMessageFromFrontend(session_id_, message);
1028
14782
}
1029
1030
}  // namespace inspector
1031

13197
}  // namespace node