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

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

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


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

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

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

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

44975
  return io_ != nullptr || client_->IsActive();
987
}
988
989
300
void Agent::SetParentHandle(
990
    std::unique_ptr<ParentInspectorHandle> parent_handle) {
991
300
  parent_handle_ = std::move(parent_handle);
992
301
}
993
994
547
std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
995
    int thread_id, const std::string& url) {
996
547
  if (!parent_handle_) {
997
541
    return client_->getWorkerManager()->NewParentHandle(thread_id, url);
998
  } else {
999
6
    return parent_handle_->NewParentInspectorHandle(thread_id, url);
1000
  }
1001
}
1002
1003
void Agent::WaitForConnect() {
1004
  CHECK_NOT_NULL(client_);
1005
  client_->waitForFrontend();
1006
}
1007
1008
827
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
1009
827
  CHECK_NOT_NULL(client_);
1010
827
  return client_->getWorkerManager();
1011
}
1012
1013
6
std::string Agent::GetWsUrl() const {
1014
6
  if (io_ == nullptr)
1015
1
    return "";
1016
5
  return io_->GetWsUrl();
1017
}
1018
1019
13450
SameThreadInspectorSession::~SameThreadInspectorSession() {
1020
8967
  auto client = client_.lock();
1021
4483
  if (client)
1022
288
    client->disconnectFrontend(session_id_);
1023
8967
}
1024
1025
14453
void SameThreadInspectorSession::Dispatch(
1026
    const v8_inspector::StringView& message) {
1027
28904
  auto client = client_.lock();
1028
14454
  if (client)
1029
14454
    client->dispatchMessageFromFrontend(session_id_, message);
1030
14455
}
1031
1032
}  // namespace inspector
1033

12978
}  // namespace node