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: 487 514 94.7 %
Date: 2019-09-26 22:31:05 Branches: 157 216 72.7 %

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

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

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


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

192
    if (waiting_for_sessions_disconnect_ || waiting_for_resume_) {
696
148
      return hasConnectedSessions();
697
    }
698
44
    return false;
699
  }
700
701
129
  void runMessageLoop() {
702
129
    if (running_nested_loop_)
703
129
      return;
704
705
129
    running_nested_loop_ = true;
706
707
129
    MultiIsolatePlatform* platform = env_->isolate_data()->platform();
708
390
    while (shouldRunMessageLoop()) {
709
132
      if (interface_) interface_->WaitForFrontendEvent();
710
132
      while (platform->FlushForegroundTasks(env_->isolate())) {}
711
    }
712
129
    running_nested_loop_ = false;
713
  }
714
715
226395
  double currentTimeMS() override {
716
226395
    return env_->isolate_data()->platform()->CurrentClockTimeMillis();
717
  }
718
719
458661
  std::unique_ptr<StringBuffer> resourceNameToUrl(
720
      const StringView& resource_name_view) override {
721
    std::string resource_name =
722
458661
        protocol::StringUtil::StringViewToUtf8(resource_name_view);
723
458662
    if (!IsFilePath(resource_name))
724
419253
      return nullptr;
725
78818
    node::url::URL url = node::url::URL::FromFilePath(resource_name);
726
    // TODO(ak239spb): replace this code with url.href().
727
    // Refs: https://github.com/nodejs/node/issues/22610
728
498071
    return Utf8ToStringView(url.protocol() + "//" + url.path());
729
  }
730
731
  node::Environment* env_;
732
  bool is_main_;
733
  bool running_nested_loop_ = false;
734
  std::unique_ptr<V8Inspector> client_;
735
  std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
736
  std::unordered_map<void*, InspectorTimerHandle> timers_;
737
  int next_session_id_ = 1;
738
  bool waiting_for_resume_ = false;
739
  bool waiting_for_frontend_ = false;
740
  bool waiting_for_sessions_disconnect_ = false;
741
  // Allows accessing Inspector from non-main threads
742
  std::unique_ptr<MainThreadInterface> interface_;
743
  std::shared_ptr<WorkerManager> worker_manager_;
744
};
745
746
5104
Agent::Agent(Environment* env)
747
    : parent_env_(env),
748
10208
      debug_options_(env->options()->debug_options()),
749
25520
      host_port_(env->inspector_host_port()) {}
750
751
18775
Agent::~Agent() {
752
4694
  if (start_io_thread_async.data == this) {
753
4485
    CHECK(start_io_thread_async_initialized.exchange(false));
754
4485
    start_io_thread_async.data = nullptr;
755
    // This is global, will never get freed
756
4485
    uv_close(reinterpret_cast<uv_handle_t*>(&start_io_thread_async), nullptr);
757
  }
758
4694
}
759
760
5104
bool Agent::Start(const std::string& path,
761
                  const DebugOptions& options,
762
                  std::shared_ptr<HostPort> host_port,
763
                  bool is_main) {
764
5104
  path_ = path;
765
5104
  debug_options_ = options;
766
5104
  CHECK_NOT_NULL(host_port);
767
5104
  host_port_ = host_port;
768
769
5104
  client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
770
5104
  if (parent_env_->owns_inspector()) {
771
4894
    CHECK_EQ(start_io_thread_async_initialized.exchange(true), false);
772
4894
    CHECK_EQ(0, uv_async_init(parent_env_->event_loop(),
773
                              &start_io_thread_async,
774
                              StartIoThreadAsyncCallback));
775
4894
    uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
776
4894
    start_io_thread_async.data = this;
777
    // Ignore failure, SIGUSR1 won't work, but that should not block node start.
778
4894
    StartDebugSignalHandler();
779
  }
780
781
5104
  bool wait_for_connect = options.wait_for_connect();
782
5104
  if (parent_handle_) {
783
210
    wait_for_connect = parent_handle_->WaitForConnect();
784
210
    parent_handle_->WorkerStarted(client_->getThreadHandle(), wait_for_connect);
785

4894
  } else if (!options.inspector_enabled || !StartIoThread()) {
786
4820
    return false;
787
  }
788
789
  // Patch the debug options to implement waitForDebuggerOnStart for
790
  // the NodeWorker.enable method.
791
284
  if (wait_for_connect) {
792
16
    CHECK(!parent_env_->has_serialized_options());
793
16
    debug_options_.EnableBreakFirstLine();
794
16
    parent_env_->options()->get_debug_options()->EnableBreakFirstLine();
795
16
    client_->waitForFrontend();
796
  }
797
284
  return true;
798
}
799
800
82
bool Agent::StartIoThread() {
801
82
  if (io_ != nullptr)
802
3
    return true;
803
804
79
  CHECK_NOT_NULL(client_);
805
806
158
  io_ = InspectorIo::Start(client_->getThreadHandle(),
807
                           path_,
808
                           host_port_,
809
79
                           debug_options_.inspect_publish_uid);
810
79
  if (io_ == nullptr) {
811
1
    return false;
812
  }
813
78
  NotifyClusterWorkersDebugEnabled(parent_env_);
814
78
  return true;
815
}
816
817
214
void Agent::Stop() {
818
214
  io_.reset();
819
214
}
820
821
5353
std::unique_ptr<InspectorSession> Agent::Connect(
822
    std::unique_ptr<InspectorSessionDelegate> delegate,
823
    bool prevent_shutdown) {
824
5353
  CHECK_NOT_NULL(client_);
825
5353
  int session_id = client_->connectFrontend(std::move(delegate),
826
10705
                                            prevent_shutdown);
827
  return std::unique_ptr<InspectorSession>(
828
5354
      new SameThreadInspectorSession(session_id, client_));
829
}
830
831
2
std::unique_ptr<InspectorSession> Agent::ConnectToMainThread(
832
    std::unique_ptr<InspectorSessionDelegate> delegate,
833
    bool prevent_shutdown) {
834
2
  CHECK_NOT_NULL(parent_handle_);
835
2
  CHECK_NOT_NULL(client_);
836
  auto thread_safe_delegate =
837
2
      client_->getThreadHandle()->MakeDelegateThreadSafe(std::move(delegate));
838
2
  return parent_handle_->Connect(std::move(thread_safe_delegate),
839
4
                                 prevent_shutdown);
840
}
841
842
4940
void Agent::WaitForDisconnect() {
843
4940
  CHECK_NOT_NULL(client_);
844
4940
  bool is_worker = parent_handle_ != nullptr;
845
4940
  parent_handle_.reset();
846

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

232191
  return io_ != nullptr || client_->IsActive();
980
}
981
982
210
void Agent::SetParentHandle(
983
    std::unique_ptr<ParentInspectorHandle> parent_handle) {
984
210
  parent_handle_ = std::move(parent_handle);
985
210
}
986
987
217
std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
988
    int thread_id, const std::string& url) {
989
217
  if (!parent_handle_) {
990
213
    return client_->getWorkerManager()->NewParentHandle(thread_id, url);
991
  } else {
992
4
    return parent_handle_->NewParentInspectorHandle(thread_id, url);
993
  }
994
}
995
996
2
void Agent::WaitForConnect() {
997
2
  CHECK_NOT_NULL(client_);
998
2
  client_->waitForFrontend();
999
2
}
1000
1001
427
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
1002
427
  CHECK_NOT_NULL(client_);
1003
427
  return client_->getWorkerManager();
1004
}
1005
1006
9
std::string Agent::GetWsUrl() const {
1007
9
  if (io_ == nullptr)
1008
2
    return "";
1009
7
  return io_->GetWsUrl();
1010
}
1011
1012
14827
SameThreadInspectorSession::~SameThreadInspectorSession() {
1013
4943
  auto client = client_.lock();
1014
4943
  if (client)
1015
231
    client->disconnectFrontend(session_id_);
1016
9887
}
1017
1018
15912
void SameThreadInspectorSession::Dispatch(
1019
    const v8_inspector::StringView& message) {
1020
15912
  auto client = client_.lock();
1021
15910
  if (client)
1022
15910
    client->dispatchMessageFromFrontend(session_id_, message);
1023
15912
}
1024
1025
}  // namespace inspector
1026
}  // namespace node