GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: inspector_agent.cc Lines: 452 481 94.0 %
Date: 2021-08-24 04:12:49 Branches: 149 208 71.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-inl.h"
16
#include "node_url.h"
17
#include "util-inl.h"
18
#include "timer_wrap.h"
19
#include "v8-inspector.h"
20
#include "v8-platform.h"
21
22
#include "libplatform/libplatform.h"
23
24
#ifdef __POSIX__
25
#include <pthread.h>
26
#include <climits>  // PTHREAD_STACK_MIN
27
#endif  // __POSIX__
28
29
#include <algorithm>
30
#include <cstring>
31
#include <sstream>
32
#include <unordered_map>
33
#include <vector>
34
35
namespace node {
36
namespace inspector {
37
namespace {
38
39
using node::FatalError;
40
41
using v8::Context;
42
using v8::Function;
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
static Mutex start_io_thread_async_mutex;
62
63
4
std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
64
                                               Local<Value> value) {
65
4
  TwoByteValue buffer(isolate, value);
66
4
  return StringBuffer::create(StringView(*buffer, buffer.length()));
67
}
68
69
// Called on the main thread.
70
void StartIoThreadAsyncCallback(uv_async_t* handle) {
71
  static_cast<Agent*>(handle->data)->StartIoThread();
72
}
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
4874
inline void* StartIoThreadMain(void* unused) {
81
  for (;;) {
82
4874
    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
4873
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
4873
  CHECK_EQ(0, uv_sem_init(&start_io_thread_semaphore, 0));
98
  pthread_attr_t attr;
99
4873
  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
4873
  const size_t stack_size = std::max(static_cast<size_t>(4 * 8192),
109
                                     static_cast<size_t>(PTHREAD_STACK_MIN));
110
4873
  CHECK_EQ(0, pthread_attr_setstacksize(&attr, stack_size));
111
#endif  // defined(PTHREAD_STACK_MIN) && !defined(__FreeBSD__)
112
4873
  CHECK_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
113
  sigset_t sigmask;
114
  // Mask all signals.
115
4873
  sigfillset(&sigmask);
116
  sigset_t savemask;
117
4873
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &savemask));
118
4873
  sigmask = savemask;
119
  pthread_t thread;
120
4873
  const int err = pthread_create(&thread, &attr,
121
                                 StartIoThreadMain, nullptr);
122
  // Restore original mask
123
4873
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
124
4873
  CHECK_EQ(0, pthread_attr_destroy(&attr));
125
4873
  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
4873
  RegisterSignalHandler(SIGUSR1, StartIoThreadWakeup);
134
  // Unblock SIGUSR1.  A pending SIGUSR1 signal will now be delivered.
135
4873
  sigemptyset(&sigmask);
136
4873
  sigaddset(&sigmask, SIGUSR1);
137
4873
  CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr));
138
4873
  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
561
std::string GetWorkerLabel(node::Environment* env) {
205
1122
  std::ostringstream result;
206
561
  result << "Worker[" << env->thread_id() << "]";
207
561
  return result.str();
208
}
209
210
class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
211
                          public protocol::FrontendChannel {
212
 public:
213
6414
  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
12828
      : delegate_(std::move(delegate)), prevent_shutdown_(prevent_shutdown),
220
6414
        retaining_context_(false) {
221
6414
    session_ = inspector->connect(CONTEXT_GROUP_ID, this, StringView());
222
6414
    node_dispatcher_ = std::make_unique<protocol::UberDispatcher>(this);
223
    tracing_agent_ =
224
6414
        std::make_unique<protocol::TracingAgent>(env, main_thread_);
225
6414
    tracing_agent_->Wire(node_dispatcher_.get());
226
6414
    if (worker_manager) {
227
5838
      worker_agent_ = std::make_unique<protocol::WorkerAgent>(worker_manager);
228
5838
      worker_agent_->Wire(node_dispatcher_.get());
229
    }
230
6414
    runtime_agent_ = std::make_unique<protocol::RuntimeAgent>();
231
6414
    runtime_agent_->Wire(node_dispatcher_.get());
232
6414
  }
233
234
23724
  ~ChannelImpl() override {
235
11862
    tracing_agent_->disable();
236
11862
    tracing_agent_.reset();  // Dispose before the dispatchers
237
11862
    if (worker_agent_) {
238
10710
      worker_agent_->disable();
239
10710
      worker_agent_.reset();  // Dispose before the dispatchers
240
    }
241
11862
    runtime_agent_->disable();
242
11862
    runtime_agent_.reset();  // Dispose before the dispatchers
243
23724
  }
244
245
17745
  void dispatchProtocolMessage(const StringView& message) {
246
35490
    std::string raw_message = protocol::StringUtil::StringViewToUtf8(message);
247
    std::unique_ptr<protocol::DictionaryValue> value =
248
17745
        protocol::DictionaryValue::cast(protocol::StringUtil::parseMessage(
249
35490
            raw_message, false));
250
    int call_id;
251
35490
    std::string method;
252
17745
    node_dispatcher_->parseCommand(value.get(), &call_id, &method);
253
17745
    if (v8_inspector::V8InspectorSession::canDispatchMethod(
254
35490
            Utf8ToStringView(method)->string())) {
255
17706
      session_->dispatchProtocolMessage(message);
256
    } else {
257
39
      node_dispatcher_->dispatch(call_id, method, std::move(value),
258
                                 raw_message);
259
    }
260
17745
  }
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
5706
  bool preventShutdown() {
268
5706
    return prevent_shutdown_;
269
  }
270
271
5482
  bool notifyWaitingForDisconnect() {
272
5482
    retaining_context_ = runtime_agent_->notifyWaitingForDisconnect();
273
5482
    return retaining_context_;
274
  }
275
276
942
  bool retainingContext() {
277
942
    return retaining_context_;
278
  }
279
280
 private:
281
17706
  void sendResponse(
282
      int callId,
283
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
284
17706
    sendMessageToFrontend(message->string());
285
17706
  }
286
287
6672
  void sendNotification(
288
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
289
6672
    sendMessageToFrontend(message->string());
290
6672
  }
291
292
1332
  void flushProtocolNotifications() override { }
293
294
24742
  void sendMessageToFrontend(const StringView& message) {
295
24742
    delegate_->SendMessageToFrontend(message);
296
24742
  }
297
298
364
  void sendMessageToFrontend(const std::string& message) {
299
364
    sendMessageToFrontend(Utf8ToStringView(message)->string());
300
364
  }
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
325
  void sendProtocolNotification(
309
      std::unique_ptr<Serializable> message) override {
310
325
    sendMessageToFrontend(message->serializeToJSON());
311
325
  }
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 SameThreadInspectorSession : public InspectorSession {
330
 public:
331
6414
  SameThreadInspectorSession(
332
      int session_id, std::shared_ptr<NodeInspectorClient> client)
333
6414
      : session_id_(session_id), client_(client) {}
334
  ~SameThreadInspectorSession() override;
335
  void Dispatch(const v8_inspector::StringView& message) override;
336
337
 private:
338
  int session_id_;
339
  std::weak_ptr<NodeInspectorClient> client_;
340
};
341
342
77
void NotifyClusterWorkersDebugEnabled(Environment* env) {
343
77
  Isolate* isolate = env->isolate();
344
77
  HandleScope handle_scope(isolate);
345
77
  Local<Context> context = env->context();
346
347
  // Send message to enable debug in cluster workers
348
77
  Local<Object> message = Object::New(isolate);
349
77
  message->Set(context, FIXED_ONE_BYTE_STRING(isolate, "cmd"),
350
308
               FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED")).Check();
351
77
  ProcessEmit(env, "internalMessage", message);
352
77
}
353
354
#ifdef _WIN32
355
bool IsFilePath(const std::string& path) {
356
  // '\\'
357
  if (path.length() > 2 && path[0] == '\\' && path[1] == '\\')
358
    return true;
359
  // '[A-Z]:[/\\]'
360
  if (path.length() < 3)
361
    return false;
362
  if ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
363
    return path[1] == ':' && (path[2] == '/' || path[2] == '\\');
364
  return false;
365
}
366
#else
367
602681
bool IsFilePath(const std::string& path) {
368

602681
  return !path.empty() && path[0] == '/';
369
}
370
#endif  // __POSIX__
371
372
}  // namespace
373
374
class NodeInspectorClient : public V8InspectorClient {
375
 public:
376
5436
  explicit NodeInspectorClient(node::Environment* env, bool is_main)
377
5436
      : env_(env), is_main_(is_main) {
378
5436
    client_ = V8Inspector::create(env->isolate(), this);
379
    // TODO(bnoordhuis) Make name configurable from src/node.cc.
380
    std::string name =
381
10872
        is_main_ ? GetHumanReadableProcessName() : GetWorkerLabel(env);
382
10872
    ContextInfo info(name);
383
5436
    info.is_default = true;
384
5436
    contextCreated(env->context(), info);
385
5436
  }
386
387
40
  void runMessageLoopOnPause(int context_group_id) override {
388
40
    waiting_for_resume_ = true;
389
40
    runMessageLoop();
390
40
  }
391
392
77
  void waitForSessionsDisconnect() {
393
77
    waiting_for_sessions_disconnect_ = true;
394
77
    runMessageLoop();
395
77
  }
396
397
19
  void waitForFrontend() {
398
19
    waiting_for_frontend_ = true;
399
19
    runMessageLoop();
400
19
  }
401
402
9
  void maxAsyncCallStackDepthChanged(int depth) override {
403
9
    if (waiting_for_sessions_disconnect_) {
404
      // V8 isolate is mostly done and is only letting Inspector protocol
405
      // clients gather data.
406
4
      return;
407
    }
408
5
    if (auto agent = env_->inspector_agent()) {
409
5
      if (depth == 0) {
410
1
        agent->DisableAsyncHook();
411
      } else {
412
4
        agent->EnableAsyncHook();
413
      }
414
    }
415
  }
416
417
6025
  void contextCreated(Local<Context> context, const ContextInfo& info) {
418
12050
    auto name_buffer = Utf8ToStringView(info.name);
419
12050
    auto origin_buffer = Utf8ToStringView(info.origin);
420
6025
    std::unique_ptr<StringBuffer> aux_data_buffer;
421
422
    v8_inspector::V8ContextInfo v8info(
423
6025
        context, CONTEXT_GROUP_ID, name_buffer->string());
424
6025
    v8info.origin = origin_buffer->string();
425
426
6025
    if (info.is_default) {
427
5436
      aux_data_buffer = Utf8ToStringView("{\"isDefault\":true}");
428
    } else {
429
589
      aux_data_buffer = Utf8ToStringView("{\"isDefault\":false}");
430
    }
431
6025
    v8info.auxData = aux_data_buffer->string();
432
433
6025
    client_->contextCreated(v8info);
434
6025
  }
435
436
5421
  void contextDestroyed(Local<Context> context) {
437
5421
    client_->contextDestroyed(context);
438
5421
  }
439
440
28
  void quitMessageLoopOnPause() override {
441
28
    waiting_for_resume_ = false;
442
28
  }
443
444
20
  void runIfWaitingForDebugger(int context_group_id) override {
445
20
    waiting_for_frontend_ = false;
446
20
  }
447
448
6414
  int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,
449
                      bool prevent_shutdown) {
450
6414
    int session_id = next_session_id_++;
451
12828
    channels_[session_id] = std::make_unique<ChannelImpl>(env_,
452
6414
                                                          client_,
453
12828
                                                          getWorkerManager(),
454
6414
                                                          std::move(delegate),
455
12828
                                                          getThreadHandle(),
456
6414
                                                          prevent_shutdown);
457
6414
    return session_id;
458
  }
459
460
939
  void disconnectFrontend(int session_id) {
461
939
    auto it = channels_.find(session_id);
462
939
    if (it == channels_.end())
463
      return;
464
939
    bool retaining_context = it->second->retainingContext();
465
939
    channels_.erase(it);
466
939
    if (retaining_context) {
467
6
      for (const auto& id_channel : channels_) {
468
3
        if (id_channel.second->retainingContext())
469
          return;
470
      }
471
3
      contextDestroyed(env_->context());
472
    }
473

939
    if (waiting_for_sessions_disconnect_ && !is_main_)
474
2
      waiting_for_sessions_disconnect_ = false;
475
  }
476
477
17745
  void dispatchMessageFromFrontend(int session_id, const StringView& message) {
478
17745
    channels_[session_id]->dispatchProtocolMessage(message);
479
17745
  }
480
481
324
  Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
482
324
    return env_->context();
483
  }
484
485
3
  void installAdditionalCommandLineAPI(Local<Context> context,
486
                                       Local<Object> target) override {
487
3
    Local<Function> installer = env_->inspector_console_extension_installer();
488
3
    if (!installer.IsEmpty()) {
489
3
      Local<Value> argv[] = {target};
490
      // If there is an exception, proceed in JS land
491
3
      USE(installer->Call(context, target, arraysize(argv), argv));
492
    }
493
3
  }
494
495
2
  void ReportUncaughtException(Local<Value> error, Local<Message> message) {
496
2
    Isolate* isolate = env_->isolate();
497
2
    Local<Context> context = env_->context();
498
499
4
    int script_id = message->GetScriptOrigin().ScriptId();
500
501
2
    Local<v8::StackTrace> stack_trace = message->GetStackTrace();
502
503

4
    if (!stack_trace.IsEmpty() && stack_trace->GetFrameCount() > 0 &&
504
4
        script_id == stack_trace->GetFrame(isolate, 0)->GetScriptId()) {
505
1
      script_id = 0;
506
    }
507
508
2
    const uint8_t DETAILS[] = "Uncaught";
509
510
4
    client_->exceptionThrown(
511
        context,
512
        StringView(DETAILS, sizeof(DETAILS) - 1),
513
        error,
514
6
        ToProtocolString(isolate, message->Get())->string(),
515
4
        ToProtocolString(isolate, message->GetScriptResourceName())->string(),
516
4
        message->GetLineNumber(context).FromMaybe(0),
517
4
        message->GetStartColumn(context).FromMaybe(0),
518
4
        client_->createStackTrace(stack_trace),
519
2
        script_id);
520
2
  }
521
522
2
  void startRepeatingTimer(double interval_s,
523
                           TimerCallback callback,
524
                           void* data) override {
525
    auto result =
526
2
        timers_.emplace(std::piecewise_construct, std::make_tuple(data),
527
8
                        std::make_tuple(env_, [=]() { callback(data); }));
528
2
    CHECK(result.second);
529
2
    uint64_t interval = static_cast<uint64_t>(1000 * interval_s);
530
2
    result.first->second.Update(interval, interval);
531
2
  }
532
533
2
  void cancelTimer(void* data) override {
534
2
    timers_.erase(data);
535
2
  }
536
537
  // Async stack traces instrumentation.
538
3
  void AsyncTaskScheduled(const StringView& task_name, void* task,
539
                          bool recurring) {
540
3
    client_->asyncTaskScheduled(task_name, task, recurring);
541
3
  }
542
543
4
  void AsyncTaskCanceled(void* task) {
544
4
    client_->asyncTaskCanceled(task);
545
4
  }
546
547
7
  void AsyncTaskStarted(void* task) {
548
7
    client_->asyncTaskStarted(task);
549
7
  }
550
551
7
  void AsyncTaskFinished(void* task) {
552
7
    client_->asyncTaskFinished(task);
553
7
  }
554
555
  void AllAsyncTasksCanceled() {
556
    client_->allAsyncTasksCanceled();
557
  }
558
559
19
  void schedulePauseOnNextStatement(const std::string& reason) {
560
57
    for (const auto& id_channel : channels_) {
561
38
      id_channel.second->schedulePauseOnNextStatement(reason);
562
    }
563
19
  }
564
565
5578
  bool hasConnectedSessions() {
566
11189
    for (const auto& id_channel : channels_) {
567
      // Other sessions are "invisible" more most purposes
568
5706
      if (id_channel.second->preventShutdown())
569
95
        return true;
570
    }
571
5483
    return false;
572
  }
573
574
5421
  bool notifyWaitingForDisconnect() {
575
5421
    bool retaining_context = false;
576
10903
    for (const auto& id_channel : channels_) {
577
5482
      if (id_channel.second->notifyWaitingForDisconnect())
578
3
        retaining_context = true;
579
    }
580
5421
    return retaining_context;
581
  }
582
583
11925
  std::shared_ptr<MainThreadHandle> getThreadHandle() {
584
11925
    if (!interface_) {
585
5433
      interface_ = std::make_shared<MainThreadInterface>(
586
10866
          env_->inspector_agent());
587
    }
588
11925
    return interface_->GetHandle();
589
  }
590
591
8531
  std::shared_ptr<WorkerManager> getWorkerManager() {
592
8531
    if (!is_main_) {
593
576
      return nullptr;
594
    }
595
7955
    if (worker_manager_ == nullptr) {
596
      worker_manager_ =
597
4870
          std::make_shared<WorkerManager>(getThreadHandle());
598
    }
599
7955
    return worker_manager_;
600
  }
601
602
49410
  bool IsActive() {
603
49410
    return !channels_.empty();
604
  }
605
606
 private:
607
265
  bool shouldRunMessageLoop() {
608
265
    if (waiting_for_frontend_)
609
59
      return true;
610

206
    if (waiting_for_sessions_disconnect_ || waiting_for_resume_) {
611
157
      return hasConnectedSessions();
612
    }
613
49
    return false;
614
  }
615
616
136
  void runMessageLoop() {
617
136
    if (running_nested_loop_)
618
      return;
619
620
136
    running_nested_loop_ = true;
621
622
265
    while (shouldRunMessageLoop()) {
623
129
      if (interface_) interface_->WaitForFrontendEvent();
624
129
      env_->RunAndClearInterrupts();
625
    }
626
136
    running_nested_loop_ = false;
627
  }
628
629
44116
  double currentTimeMS() override {
630
44116
    return env_->isolate_data()->platform()->CurrentClockTimeMillis();
631
  }
632
633
602681
  std::unique_ptr<StringBuffer> resourceNameToUrl(
634
      const StringView& resource_name_view) override {
635
    std::string resource_name =
636
1205362
        protocol::StringUtil::StringViewToUtf8(resource_name_view);
637
602681
    if (!IsFilePath(resource_name))
638
569010
      return nullptr;
639
33671
    node::url::URL url = node::url::URL::FromFilePath(resource_name);
640
33671
    return Utf8ToStringView(url.href());
641
  }
642
643
  node::Environment* env_;
644
  bool is_main_;
645
  bool running_nested_loop_ = false;
646
  std::unique_ptr<V8Inspector> client_;
647
  // Note: ~ChannelImpl may access timers_ so timers_ has to come first.
648
  std::unordered_map<void*, TimerWrapHandle> timers_;
649
  std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
650
  int next_session_id_ = 1;
651
  bool waiting_for_resume_ = false;
652
  bool waiting_for_frontend_ = false;
653
  bool waiting_for_sessions_disconnect_ = false;
654
  // Allows accessing Inspector from non-main threads
655
  std::shared_ptr<MainThreadInterface> interface_;
656
  std::shared_ptr<WorkerManager> worker_manager_;
657
};
658
659
5442
Agent::Agent(Environment* env)
660
    : parent_env_(env),
661
5442
      debug_options_(env->options()->debug_options()),
662
10884
      host_port_(env->inspector_host_port()) {}
663
664
4961
Agent::~Agent() {}
665
666
5436
bool Agent::Start(const std::string& path,
667
                  const DebugOptions& options,
668
                  std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
669
                  bool is_main) {
670
5436
  path_ = path;
671
5436
  debug_options_ = options;
672
5436
  CHECK_NOT_NULL(host_port);
673
5436
  host_port_ = host_port;
674
675
5436
  client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
676
5436
  if (parent_env_->owns_inspector()) {
677
4873
    Mutex::ScopedLock lock(start_io_thread_async_mutex);
678
4873
    CHECK_EQ(start_io_thread_async_initialized.exchange(true), false);
679
4873
    CHECK_EQ(0, uv_async_init(parent_env_->event_loop(),
680
                              &start_io_thread_async,
681
                              StartIoThreadAsyncCallback));
682
4873
    uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
683
4873
    start_io_thread_async.data = this;
684
    // Ignore failure, SIGUSR1 won't work, but that should not block node start.
685
4873
    StartDebugSignalHandler();
686
687
4873
    parent_env_->AddCleanupHook([](void* data) {
688
4392
      Environment* env = static_cast<Environment*>(data);
689
690
      {
691
4392
        Mutex::ScopedLock lock(start_io_thread_async_mutex);
692
4392
        start_io_thread_async.data = nullptr;
693
      }
694
695
      // This is global, will never get freed
696
4392
      env->CloseHandle(&start_io_thread_async, [](uv_async_t*) {
697
4392
        CHECK(start_io_thread_async_initialized.exchange(false));
698
4392
      });
699
4873
    }, parent_env_);
700
  }
701
702
5436
  AtExit(parent_env_, [](void* env) {
703
5424
    Agent* agent = static_cast<Environment*>(env)->inspector_agent();
704
5424
    if (agent->IsActive()) {
705
5419
      agent->WaitForDisconnect();
706
    }
707
5436
  }, parent_env_);
708
709
5436
  bool wait_for_connect = options.wait_for_connect();
710
5436
  if (parent_handle_) {
711
561
    wait_for_connect = parent_handle_->WaitForConnect();
712
561
    parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
713

4875
  } else if (!options.inspector_enabled || !StartIoThread()) {
714
4799
    return false;
715
  }
716
717
  // Patch the debug options to implement waitForDebuggerOnStart for
718
  // the NodeWorker.enable method.
719
637
  if (wait_for_connect) {
720
19
    CHECK(!parent_env_->has_serialized_options());
721
19
    debug_options_.EnableBreakFirstLine();
722
19
    parent_env_->options()->get_debug_options()->EnableBreakFirstLine();
723
19
    client_->waitForFrontend();
724
  }
725
637
  return true;
726
}
727
728
78
bool Agent::StartIoThread() {
729
78
  if (io_ != nullptr)
730
    return true;
731
732
78
  CHECK_NOT_NULL(client_);
733
734
156
  io_ = InspectorIo::Start(client_->getThreadHandle(),
735
78
                           path_,
736
78
                           host_port_,
737
156
                           debug_options_.inspect_publish_uid);
738
78
  if (io_ == nullptr) {
739
1
    return false;
740
  }
741
77
  NotifyClusterWorkersDebugEnabled(parent_env_);
742
77
  return true;
743
}
744
745
4
void Agent::Stop() {
746
4
  io_.reset();
747
4
}
748
749
6414
std::unique_ptr<InspectorSession> Agent::Connect(
750
    std::unique_ptr<InspectorSessionDelegate> delegate,
751
    bool prevent_shutdown) {
752
6414
  CHECK_NOT_NULL(client_);
753
6414
  int session_id = client_->connectFrontend(std::move(delegate),
754
                                            prevent_shutdown);
755
  return std::unique_ptr<InspectorSession>(
756
6414
      new SameThreadInspectorSession(session_id, client_));
757
}
758
759
2
std::unique_ptr<InspectorSession> Agent::ConnectToMainThread(
760
    std::unique_ptr<InspectorSessionDelegate> delegate,
761
    bool prevent_shutdown) {
762
2
  CHECK_NOT_NULL(parent_handle_);
763
2
  CHECK_NOT_NULL(client_);
764
  auto thread_safe_delegate =
765
4
      client_->getThreadHandle()->MakeDelegateThreadSafe(std::move(delegate));
766
2
  return parent_handle_->Connect(std::move(thread_safe_delegate),
767
2
                                 prevent_shutdown);
768
}
769
770
5421
void Agent::WaitForDisconnect() {
771
5421
  CHECK_NOT_NULL(client_);
772
5421
  bool is_worker = parent_handle_ != nullptr;
773
5421
  parent_handle_.reset();
774

5421
  if (client_->hasConnectedSessions() && !is_worker) {
775
13
    fprintf(stderr, "Waiting for the debugger to disconnect...\n");
776
13
    fflush(stderr);
777
  }
778
5421
  if (!client_->notifyWaitingForDisconnect()) {
779
5418
    client_->contextDestroyed(parent_env_->context());
780
3
  } else if (is_worker) {
781
2
    client_->waitForSessionsDisconnect();
782
  }
783
5421
  if (io_ != nullptr) {
784
75
    io_->StopAcceptingNewConnections();
785
75
    client_->waitForSessionsDisconnect();
786
  }
787
5421
}
788
789
220
void Agent::ReportUncaughtException(Local<Value> error,
790
                                    Local<Message> message) {
791
220
  if (!IsListening())
792
218
    return;
793
2
  client_->ReportUncaughtException(error, message);
794
2
  WaitForDisconnect();
795
}
796
797
19
void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
798
19
  client_->schedulePauseOnNextStatement(reason);
799
19
}
800
801
5404
void Agent::RegisterAsyncHook(Isolate* isolate,
802
                              Local<Function> enable_function,
803
                              Local<Function> disable_function) {
804
5404
  parent_env_->set_inspector_enable_async_hooks(enable_function);
805
5404
  parent_env_->set_inspector_disable_async_hooks(disable_function);
806
5404
  if (pending_enable_async_hook_) {
807
2
    CHECK(!pending_disable_async_hook_);
808
2
    pending_enable_async_hook_ = false;
809
2
    EnableAsyncHook();
810
5402
  } else if (pending_disable_async_hook_) {
811
    CHECK(!pending_enable_async_hook_);
812
    pending_disable_async_hook_ = false;
813
    DisableAsyncHook();
814
  }
815
5404
}
816
817
6
void Agent::EnableAsyncHook() {
818
12
  HandleScope scope(parent_env_->isolate());
819
6
  Local<Function> enable = parent_env_->inspector_enable_async_hooks();
820
6
  if (!enable.IsEmpty()) {
821
4
    ToggleAsyncHook(parent_env_->isolate(), enable);
822
2
  } else if (pending_disable_async_hook_) {
823
    CHECK(!pending_enable_async_hook_);
824
    pending_disable_async_hook_ = false;
825
  } else {
826
2
    pending_enable_async_hook_ = true;
827
  }
828
6
}
829
830
1
void Agent::DisableAsyncHook() {
831
2
  HandleScope scope(parent_env_->isolate());
832
1
  Local<Function> disable = parent_env_->inspector_enable_async_hooks();
833
1
  if (!disable.IsEmpty()) {
834
1
    ToggleAsyncHook(parent_env_->isolate(), disable);
835
  } else if (pending_enable_async_hook_) {
836
    CHECK(!pending_disable_async_hook_);
837
    pending_enable_async_hook_ = false;
838
  } else {
839
    pending_disable_async_hook_ = true;
840
  }
841
1
}
842
843
5
void Agent::ToggleAsyncHook(Isolate* isolate, Local<Function> fn) {
844
  // Guard against running this during cleanup -- no async events will be
845
  // emitted anyway at that point anymore, and calling into JS is not possible.
846
  // This should probably not be something we're attempting in the first place,
847
  // Refs: https://github.com/nodejs/node/pull/34362#discussion_r456006039
848
5
  if (!parent_env_->can_call_into_js()) return;
849
5
  CHECK(parent_env_->has_run_bootstrapping_code());
850
10
  HandleScope handle_scope(isolate);
851
5
  CHECK(!fn.IsEmpty());
852
5
  auto context = parent_env_->context();
853
10
  v8::TryCatch try_catch(isolate);
854
10
  USE(fn->Call(context, Undefined(isolate), 0, nullptr));
855

5
  if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
856
    PrintCaughtException(isolate, context, try_catch);
857
    FatalError("\nnode::inspector::Agent::ToggleAsyncHook",
858
               "Cannot toggle Inspector's AsyncHook, please report this.");
859
  }
860
}
861
862
3
void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
863
                               bool recurring) {
864
3
  client_->AsyncTaskScheduled(task_name, task, recurring);
865
3
}
866
867
4
void Agent::AsyncTaskCanceled(void* task) {
868
4
  client_->AsyncTaskCanceled(task);
869
4
}
870
871
7
void Agent::AsyncTaskStarted(void* task) {
872
7
  client_->AsyncTaskStarted(task);
873
7
}
874
875
7
void Agent::AsyncTaskFinished(void* task) {
876
7
  client_->AsyncTaskFinished(task);
877
7
}
878
879
void Agent::AllAsyncTasksCanceled() {
880
  client_->AllAsyncTasksCanceled();
881
}
882
883
1
void Agent::RequestIoThreadStart() {
884
  // We need to attempt to interrupt V8 flow (in case Node is running
885
  // continuous JS code) and to wake up libuv thread (in case Node is waiting
886
  // for IO events)
887
1
  CHECK(start_io_thread_async_initialized);
888
1
  uv_async_send(&start_io_thread_async);
889
1
  parent_env_->RequestInterrupt([this](Environment*) {
890
1
    StartIoThread();
891
1
  });
892
893
1
  CHECK(start_io_thread_async_initialized);
894
1
  uv_async_send(&start_io_thread_async);
895
1
}
896
897
6031
void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
898
6031
  if (client_ == nullptr)  // This happens for a main context
899
5442
    return;
900
589
  client_->contextCreated(context, info);
901
}
902
903
49520
bool Agent::IsActive() {
904
49520
  if (client_ == nullptr)
905
    return false;
906

49520
  return io_ != nullptr || client_->IsActive();
907
}
908
909
561
void Agent::SetParentHandle(
910
    std::unique_ptr<ParentInspectorHandle> parent_handle) {
911
561
  parent_handle_ = std::move(parent_handle);
912
561
}
913
914
794
std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
915
    uint64_t thread_id, const std::string& url) {
916
794
  if (!parent_handle_) {
917
784
    return client_->getWorkerManager()->NewParentHandle(thread_id, url);
918
  } else {
919
10
    return parent_handle_->NewParentInspectorHandle(thread_id, url);
920
  }
921
}
922
923
void Agent::WaitForConnect() {
924
  CHECK_NOT_NULL(client_);
925
  client_->waitForFrontend();
926
}
927
928
1333
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
929
1333
  CHECK_NOT_NULL(client_);
930
1333
  return client_->getWorkerManager();
931
}
932
933
6
std::string Agent::GetWsUrl() const {
934
6
  if (io_ == nullptr)
935
1
    return "";
936
5
  return io_->GetWsUrl();
937
}
938
939
23724
SameThreadInspectorSession::~SameThreadInspectorSession() {
940
23724
  auto client = client_.lock();
941
11862
  if (client)
942
1878
    client->disconnectFrontend(session_id_);
943
23724
}
944
945
17745
void SameThreadInspectorSession::Dispatch(
946
    const v8_inspector::StringView& message) {
947
35490
  auto client = client_.lock();
948
17745
  if (client)
949
17745
    client->dispatchMessageFromFrontend(session_id_, message);
950
17745
}
951
952
}  // namespace inspector
953
}  // namespace node