GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/inspector_agent.cc Lines: 98 349 28.1 %
Date: 2016-12-16 Branches: 28 155 18.1 %

Line Branch Exec Source
1
#include "inspector_agent.h"
2
3
#include "inspector_socket_server.h"
4
#include "env.h"
5
#include "env-inl.h"
6
#include "node.h"
7
#include "node_crypto.h"
8
#include "node_mutex.h"
9
#include "node_version.h"
10
#include "v8-inspector.h"
11
#include "v8-platform.h"
12
#include "util.h"
13
#include "zlib.h"
14
15
#include "libplatform/libplatform.h"
16
17
#include <map>
18
#include <sstream>
19
#include <tuple>
20
#include <unicode/unistr.h>
21
22
#include <string.h>
23
#include <utility>
24
#include <vector>
25
26
27
namespace node {
28
namespace inspector {
29
namespace {
30
31
using v8_inspector::StringBuffer;
32
using v8_inspector::StringView;
33
34
static const uint8_t PROTOCOL_JSON[] = {
35
#include "v8_inspector_protocol_json.h"  // NOLINT(build/include_order)
36
};
37
38
std::string GetProcessTitle() {
39
  // uv_get_process_title will trim the title if it is too long.
40
  char title[2048];
41
  int err = uv_get_process_title(title, sizeof(title));
42
  if (err == 0) {
43
    return title;
44
  } else {
45
    return "Node.js";
46
  }
47
}
48
49
// UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
50
// Used ver 4 - with numbers
51
7
std::string GenerateID() {
52
  uint16_t buffer[8];
53
7
  CHECK(crypto::EntropySource(reinterpret_cast<unsigned char*>(buffer),
54
                              sizeof(buffer)));
55
56
  char uuid[256];
57
56
  snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
58
7
           buffer[0],  // time_low
59
7
           buffer[1],  // time_mid
60
7
           buffer[2],  // time_low
61
7
           (buffer[3] & 0x0fff) | 0x4000,  // time_hi_and_version
62
7
           (buffer[4] & 0x3fff) | 0x8000,  // clk_seq_hi clk_seq_low
63
7
           buffer[5],  // node
64
7
           buffer[6],
65
7
           buffer[7]);
66
21
  return uuid;
67
}
68
69
std::string StringViewToUtf8(const StringView& view) {
70
  if (view.is8Bit()) {
71
    return std::string(reinterpret_cast<const char*>(view.characters8()),
72
                       view.length());
73
  }
74
  const uint16_t* source = view.characters16();
75
  const UChar* unicodeSource = reinterpret_cast<const UChar*>(source);
76
  static_assert(sizeof(*source) == sizeof(*unicodeSource),
77
                "sizeof(*source) == sizeof(*unicodeSource)");
78
79
  size_t result_length = view.length() * sizeof(*source);
80
  std::string result(result_length, '\0');
81
  UnicodeString utf16(unicodeSource, view.length());
82
  // ICU components for std::string compatibility are not enabled in build...
83
  bool done = false;
84
  while (!done) {
85
    CheckedArrayByteSink sink(&result[0], result_length);
86
    utf16.toUTF8(sink);
87
    result_length = sink.NumberOfBytesAppended();
88
    result.resize(result_length);
89
    done = !sink.Overflowed();
90
  }
91
  return result;
92
}
93
94
std::unique_ptr<StringBuffer> Utf8ToStringView(const std::string& message) {
95
  UnicodeString utf16 =
96
      UnicodeString::fromUTF8(StringPiece(message.data(), message.length()));
97
  StringView view(reinterpret_cast<const uint16_t*>(utf16.getBuffer()),
98
                  utf16.length());
99
  return StringBuffer::create(view);
100
}
101
102
}  // namespace
103
104
class V8NodeInspector;
105
106
enum class InspectorAction {
107
  kStartSession, kEndSession, kSendMessage
108
};
109
110
enum class TransportAction {
111
  kSendMessage, kStop
112
};
113
114
class InspectorAgentDelegate: public node::inspector::SocketServerDelegate {
115
 public:
116
  InspectorAgentDelegate(AgentImpl* agent, const std::string& script_path,
117
                         const std::string& script_name, bool wait);
118
  bool StartSession(int session_id, const std::string& target_id) override;
119
  void MessageReceived(int session_id, const std::string& message) override;
120
  void EndSession(int session_id) override;
121
  std::vector<std::string> GetTargetIds() override;
122
  std::string GetTargetTitle(const std::string& id) override;
123
  std::string GetTargetUrl(const std::string& id) override;
124
  bool IsConnected() { return connected_; }
125
 private:
126
  AgentImpl* agent_;
127
  bool connected_;
128
  int session_id_;
129
  const std::string script_name_;
130
  const std::string script_path_;
131
  const std::string target_id_;
132
  bool waiting_;
133
};
134
135
class AgentImpl {
136
 public:
137
  explicit AgentImpl(node::Environment* env);
138
  ~AgentImpl();
139
140
  // Start the inspector agent thread
141
  bool Start(v8::Platform* platform, const char* path,
142
             const DebugOptions& options);
143
  // Stop the inspector agent
144
  void Stop();
145
146
  bool IsStarted();
147
  bool IsConnected();
148
  void WaitForDisconnect();
149
150
  void FatalException(v8::Local<v8::Value> error,
151
                      v8::Local<v8::Message> message);
152
153
  void PostIncomingMessage(InspectorAction action, int session_id,
154
                           const std::string& message);
155
  void ResumeStartup() {
156
    uv_sem_post(&start_sem_);
157
  }
158
159
 private:
160
  template <typename Action>
161
  using MessageQueue =
162
      std::vector<std::tuple<Action, int, std::unique_ptr<StringBuffer>>>;
163
  enum class State { kNew, kAccepting, kConnected, kDone, kError };
164
165
  static void ThreadCbIO(void* agent);
166
  static void WriteCbIO(uv_async_t* async);
167
168
  void InstallInspectorOnProcess();
169
170
  void WorkerRunIO();
171
  void SetConnected(bool connected);
172
  void DispatchMessages();
173
  void Write(TransportAction action, int session_id, const StringView& message);
174
  template <typename ActionType>
175
  bool AppendMessage(MessageQueue<ActionType>* vector, ActionType action,
176
                     int session_id, std::unique_ptr<StringBuffer> buffer);
177
  template <typename ActionType>
178
  void SwapBehindLock(MessageQueue<ActionType>* vector1,
179
                      MessageQueue<ActionType>* vector2);
180
  void WaitForFrontendMessage();
181
  void NotifyMessageReceived();
182
  State ToState(State state);
183
184
  DebugOptions options_;
185
  uv_sem_t start_sem_;
186
  ConditionVariable incoming_message_cond_;
187
  Mutex state_lock_;
188
  uv_thread_t thread_;
189
  uv_loop_t child_loop_;
190
191
  InspectorAgentDelegate* delegate_;
192
  bool wait_;
193
  bool shutting_down_;
194
  State state_;
195
  node::Environment* parent_env_;
196
197
  uv_async_t* data_written_;
198
  uv_async_t io_thread_req_;
199
  V8NodeInspector* inspector_;
200
  v8::Platform* platform_;
201
  MessageQueue<InspectorAction> incoming_message_queue_;
202
  MessageQueue<TransportAction> outgoing_message_queue_;
203
  bool dispatching_messages_;
204
  int session_id_;
205
  InspectorSocketServer* server_;
206
207
  std::string script_name_;
208
  std::string script_path_;
209
  const std::string id_;
210
211
  friend class ChannelImpl;
212
  friend class DispatchOnInspectorBackendTask;
213
  friend class SetConnectedTask;
214
  friend class V8NodeInspector;
215
  friend void InterruptCallback(v8::Isolate*, void* agent);
216
  friend void DataCallback(uv_stream_t* stream, ssize_t read,
217
                           const uv_buf_t* buf);
218
};
219
220
void InterruptCallback(v8::Isolate*, void* agent) {
221
  static_cast<AgentImpl*>(agent)->DispatchMessages();
222
}
223
224
class DispatchOnInspectorBackendTask : public v8::Task {
225
 public:
226
  explicit DispatchOnInspectorBackendTask(AgentImpl* agent) : agent_(agent) {}
227
228
  void Run() override {
229
    agent_->DispatchMessages();
230
  }
231
232
 private:
233
  AgentImpl* agent_;
234
};
235
236
class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
237
 public:
238
  explicit ChannelImpl(AgentImpl* agent): agent_(agent) {}
239
  virtual ~ChannelImpl() {}
240
 private:
241
  void sendProtocolResponse(int callId, const StringView& message) override {
242
    sendMessageToFrontend(message);
243
  }
244
245
  void sendProtocolNotification(const StringView& message) override {
246
    sendMessageToFrontend(message);
247
  }
248
249
  void flushProtocolNotifications() override { }
250
251
  void sendMessageToFrontend(const StringView& message) {
252
    agent_->Write(TransportAction::kSendMessage, agent_->session_id_, message);
253
  }
254
255
  AgentImpl* const agent_;
256
};
257
258
// Used in V8NodeInspector::currentTimeMS() below.
259
#define NANOS_PER_MSEC 1000000
260
261
using V8Inspector = v8_inspector::V8Inspector;
262
263
class V8NodeInspector : public v8_inspector::V8InspectorClient {
264
 public:
265
7
  V8NodeInspector(AgentImpl* agent, node::Environment* env,
266
                  v8::Platform* platform)
267
                  : agent_(agent),
268
                    env_(env),
269
                    platform_(platform),
270
                    terminated_(false),
271
                    running_nested_loop_(false),
272
21
                    inspector_(V8Inspector::create(env->isolate(), this)) {
273
7
    const uint8_t CONTEXT_NAME[] = "Node.js Main Context";
274
14
    StringView context_name(CONTEXT_NAME, sizeof(CONTEXT_NAME) - 1);
275
14
    v8_inspector::V8ContextInfo info(env->context(), 1, context_name);
276
14
    inspector_->contextCreated(info);
277
7
  }
278
279
  void runMessageLoopOnPause(int context_group_id) override {
280
    if (running_nested_loop_)
281
      return;
282
    terminated_ = false;
283
    running_nested_loop_ = true;
284
    while (!terminated_) {
285
      agent_->WaitForFrontendMessage();
286
      while (v8::platform::PumpMessageLoop(platform_, env_->isolate()))
287
        {}
288
    }
289
    terminated_ = false;
290
    running_nested_loop_ = false;
291
  }
292
293
  double currentTimeMS() override {
294
    return uv_hrtime() * 1.0 / NANOS_PER_MSEC;
295
  }
296
297
  void quitMessageLoopOnPause() override {
298
    terminated_ = true;
299
  }
300
301
  void connectFrontend() {
302
    session_ = inspector_->connect(1, new ChannelImpl(agent_), StringView());
303
  }
304
305
  void disconnectFrontend() {
306
    session_.reset();
307
  }
308
309
  void dispatchMessageFromFrontend(const StringView& message) {
310
    CHECK(session_);
311
    session_->dispatchProtocolMessage(message);
312
  }
313
314
  v8::Local<v8::Context> ensureDefaultContextInGroup(int contextGroupId)
315
      override {
316
    return env_->context();
317
  }
318
319
  V8Inspector* inspector() {
320
    return inspector_.get();
321
  }
322
323
 private:
324
  AgentImpl* agent_;
325
  node::Environment* env_;
326
  v8::Platform* platform_;
327
  bool terminated_;
328
  bool running_nested_loop_;
329
  std::unique_ptr<V8Inspector> inspector_;
330
  std::unique_ptr<v8_inspector::V8InspectorSession> session_;
331
};
332
333
1722
AgentImpl::AgentImpl(Environment* env) : delegate_(nullptr),
334
                                         wait_(false),
335
                                         shutting_down_(false),
336
                                         state_(State::kNew),
337
                                         parent_env_(env),
338
                                         data_written_(new uv_async_t()),
339
                                         inspector_(nullptr),
340
                                         platform_(nullptr),
341
                                         dispatching_messages_(false),
342
                                         session_id_(0),
343
10332
                                         server_(nullptr) {
344
1722
  CHECK_EQ(0, uv_sem_init(&start_sem_, 0));
345
3444
  memset(&io_thread_req_, 0, sizeof(io_thread_req_));
346
3444
  CHECK_EQ(0, uv_async_init(env->event_loop(), data_written_, nullptr));
347
1722
  uv_unref(reinterpret_cast<uv_handle_t*>(data_written_));
348
1722
}
349
350
12128
AgentImpl::~AgentImpl() {
351
  auto close_cb = [](uv_handle_t* handle) {
352
    delete reinterpret_cast<uv_async_t*>(handle);
353
  };
354
1516
  uv_close(reinterpret_cast<uv_handle_t*>(data_written_), close_cb);
355
1516
  data_written_ = nullptr;
356
1516
}
357
358
void InspectorConsoleCall(const v8::FunctionCallbackInfo<v8::Value>& info) {
359
  v8::Isolate* isolate = info.GetIsolate();
360
  v8::Local<v8::Context> context = isolate->GetCurrentContext();
361
362
  CHECK(info.Data()->IsArray());
363
  v8::Local<v8::Array> args = info.Data().As<v8::Array>();
364
  CHECK_EQ(args->Length(), 3);
365
366
  v8::Local<v8::Value> inspector_method =
367
      args->Get(context, 0).ToLocalChecked();
368
  CHECK(inspector_method->IsFunction());
369
  v8::Local<v8::Value> node_method =
370
      args->Get(context, 1).ToLocalChecked();
371
  CHECK(node_method->IsFunction());
372
  v8::Local<v8::Value> config_value =
373
      args->Get(context, 2).ToLocalChecked();
374
  CHECK(config_value->IsObject());
375
  v8::Local<v8::Object> config_object = config_value.As<v8::Object>();
376
377
  std::vector<v8::Local<v8::Value>> call_args(info.Length());
378
  for (int i = 0; i < info.Length(); ++i) {
379
    call_args[i] = info[i];
380
  }
381
382
  v8::Local<v8::String> in_call_key = OneByteString(isolate, "in_call");
383
  bool in_call = config_object->Has(context, in_call_key).FromMaybe(false);
384
  if (!in_call) {
385
    CHECK(config_object->Set(context,
386
                             in_call_key,
387
                             v8::True(isolate)).FromJust());
388
    CHECK(!inspector_method.As<v8::Function>()->Call(
389
        context,
390
        info.Holder(),
391
        call_args.size(),
392
        call_args.data()).IsEmpty());
393
  }
394
395
  v8::TryCatch try_catch(info.GetIsolate());
396
  static_cast<void>(node_method.As<v8::Function>()->Call(context,
397
                                                         info.Holder(),
398
                                                         call_args.size(),
399
                                                         call_args.data()));
400
  CHECK(config_object->Delete(context, in_call_key).FromJust());
401
  if (try_catch.HasCaught())
402
    try_catch.ReThrow();
403
}
404
405
63
void InspectorWrapConsoleCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
406
63
  Environment* env = Environment::GetCurrent(args);
407
408

315
  if (args.Length() != 3 || !args[0]->IsFunction() ||
409

315
      !args[1]->IsFunction() || !args[2]->IsObject()) {
410
    return env->ThrowError("inspector.wrapConsoleCall takes exactly 3 "
411
        "arguments: two functions and an object.");
412
  }
413
414
63
  v8::Local<v8::Array> array = v8::Array::New(env->isolate(), args.Length());
415
189
  CHECK(array->Set(env->context(), 0, args[0]).FromJust());
416
189
  CHECK(array->Set(env->context(), 1, args[1]).FromJust());
417
189
  CHECK(array->Set(env->context(), 2, args[2]).FromJust());
418
189
  args.GetReturnValue().Set(v8::Function::New(env->context(),
419
                                              InspectorConsoleCall,
420
189
                                              array).ToLocalChecked());
421
}
422
423
7
bool AgentImpl::Start(v8::Platform* platform, const char* path,
424
                      const DebugOptions& options) {
425
14
  options_ = options;
426
7
  wait_ = options.wait_for_connect();
427
428
7
  auto env = parent_env_;
429
7
  inspector_ = new V8NodeInspector(this, env, platform);
430
7
  platform_ = platform;
431
7
  if (path != nullptr)
432
7
    script_name_ = path;
433
434
7
  InstallInspectorOnProcess();
435
436
7
  int err = uv_loop_init(&child_loop_);
437
7
  CHECK_EQ(err, 0);
438
439
7
  err = uv_thread_create(&thread_, AgentImpl::ThreadCbIO, this);
440
7
  CHECK_EQ(err, 0);
441
7
  uv_sem_wait(&start_sem_);
442
443
7
  if (state_ == State::kError) {
444
    Stop();
445
    return false;
446
  }
447
7
  state_ = State::kAccepting;
448
7
  if (options_.wait_for_connect()) {
449
    DispatchMessages();
450
  }
451
  return true;
452
}
453
454
void AgentImpl::Stop() {
455
  int err = uv_thread_join(&thread_);
456
  CHECK_EQ(err, 0);
457
  delete inspector_;
458
}
459
460
bool AgentImpl::IsConnected() {
461


1660
  return delegate_ != nullptr && delegate_->IsConnected();
462
}
463
464
bool AgentImpl::IsStarted() {
465
  return !!platform_;
466
}
467
468
void AgentImpl::WaitForDisconnect() {
469
  if (state_ == State::kConnected) {
470
    shutting_down_ = true;
471
    Write(TransportAction::kStop, 0, StringView());
472
    fprintf(stderr, "Waiting for the debugger to disconnect...\n");
473
    fflush(stderr);
474
    inspector_->runMessageLoopOnPause(0);
475
  }
476
}
477
478
#define READONLY_PROPERTY(obj, str, var)                                      \
479
  do {                                                                        \
480
    obj->DefineOwnProperty(env->context(),                                    \
481
                           OneByteString(env->isolate(), str),                \
482
                           var,                                               \
483
                           v8::ReadOnly).FromJust();                          \
484
  } while (0)
485
486
7
void AgentImpl::InstallInspectorOnProcess() {
487
7
  auto env = parent_env_;
488
7
  v8::Local<v8::Object> process = env->process_object();
489
7
  v8::Local<v8::Object> inspector = v8::Object::New(env->isolate());
490
28
  READONLY_PROPERTY(process, "inspector", inspector);
491
7
  env->SetMethod(inspector, "wrapConsoleCall", InspectorWrapConsoleCall);
492
7
}
493
494
std::unique_ptr<StringBuffer> ToProtocolString(v8::Local<v8::Value> value) {
495
  if (value.IsEmpty() || value->IsNull() || value->IsUndefined() ||
496
      !value->IsString()) {
497
    return StringBuffer::create(StringView());
498
  }
499
  v8::Local<v8::String> string_value = v8::Local<v8::String>::Cast(value);
500
  size_t len = string_value->Length();
501
  std::basic_string<uint16_t> buffer(len, '\0');
502
  string_value->Write(&buffer[0], 0, len);
503
  return StringBuffer::create(StringView(buffer.data(), len));
504
}
505
506
void AgentImpl::FatalException(v8::Local<v8::Value> error,
507
                               v8::Local<v8::Message> message) {
508
  if (!IsStarted())
509
    return;
510
  auto env = parent_env_;
511
  v8::Local<v8::Context> context = env->context();
512
513
  int script_id = message->GetScriptOrigin().ScriptID()->Value();
514
515
  v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace();
516
517
  if (!stack_trace.IsEmpty() &&
518
      stack_trace->GetFrameCount() > 0 &&
519
      script_id == stack_trace->GetFrame(0)->GetScriptId()) {
520
    script_id = 0;
521
  }
522
523
  const uint8_t DETAILS[] = "Uncaught";
524
525
  inspector_->inspector()->exceptionThrown(
526
      context,
527
      StringView(DETAILS, sizeof(DETAILS) - 1),
528
      error,
529
      ToProtocolString(message->Get())->string(),
530
      ToProtocolString(message->GetScriptResourceName())->string(),
531
      message->GetLineNumber(context).FromMaybe(0),
532
      message->GetStartColumn(context).FromMaybe(0),
533
      inspector_->inspector()->createStackTrace(stack_trace),
534
      script_id);
535
  WaitForDisconnect();
536
}
537
538
// static
539
7
void AgentImpl::ThreadCbIO(void* agent) {
540
7
  static_cast<AgentImpl*>(agent)->WorkerRunIO();
541
}
542
543
// static
544
void AgentImpl::WriteCbIO(uv_async_t* async) {
545
  AgentImpl* agent = static_cast<AgentImpl*>(async->data);
546
  MessageQueue<TransportAction> outgoing_messages;
547
  agent->SwapBehindLock(&agent->outgoing_message_queue_, &outgoing_messages);
548
  for (const auto& outgoing : outgoing_messages) {
549
    switch (std::get<0>(outgoing)) {
550
    case TransportAction::kStop:
551
      agent->server_->Stop(nullptr);
552
      break;
553
    case TransportAction::kSendMessage:
554
      std::string message = StringViewToUtf8(std::get<2>(outgoing)->string());
555
      agent->server_->Send(std::get<1>(outgoing), message);
556
      break;
557
    }
558
  }
559
}
560
561
7
void AgentImpl::WorkerRunIO() {
562
7
  int err = uv_loop_init(&child_loop_);
563
7
  CHECK_EQ(err, 0);
564
7
  err = uv_async_init(&child_loop_, &io_thread_req_, AgentImpl::WriteCbIO);
565
7
  CHECK_EQ(err, 0);
566
7
  io_thread_req_.data = this;
567
7
  std::string script_path;
568
14
  if (!script_name_.empty()) {
569
    uv_fs_t req;
570
14
    if (0 == uv_fs_realpath(&child_loop_, &req, script_name_.c_str(), nullptr))
571
21
      script_path = std::string(reinterpret_cast<char*>(req.ptr));
572
7
    uv_fs_req_cleanup(&req);
573
  }
574
7
  InspectorAgentDelegate delegate(this, script_path, script_name_, wait_);
575
7
  delegate_ = &delegate;
576
7
  InspectorSocketServer server(&delegate, options_.port());
577
7
  if (!server.Start(&child_loop_)) {
578
    fprintf(stderr, "Unable to open devtools socket: %s\n", uv_strerror(err));
579
    state_ = State::kError;  // Safe, main thread is waiting on semaphore
580
    uv_close(reinterpret_cast<uv_handle_t*>(&io_thread_req_), nullptr);
581
    uv_loop_close(&child_loop_);
582
    uv_sem_post(&start_sem_);
583
    return;
584
  }
585
7
  server_ = &server;
586
7
  if (!wait_) {
587
7
    uv_sem_post(&start_sem_);
588
  }
589
7
  uv_run(&child_loop_, UV_RUN_DEFAULT);
590
  uv_close(reinterpret_cast<uv_handle_t*>(&io_thread_req_), nullptr);
591
  server.Stop(nullptr);
592
  server.TerminateConnections(nullptr);
593
  uv_run(&child_loop_, UV_RUN_NOWAIT);
594
  err = uv_loop_close(&child_loop_);
595
  CHECK_EQ(err, 0);
596
  delegate_ = nullptr;
597
  server_ = nullptr;
598
}
599
600
template <typename ActionType>
601
bool AgentImpl::AppendMessage(MessageQueue<ActionType>* queue,
602
                              ActionType action, int session_id,
603
                              std::unique_ptr<StringBuffer> buffer) {
604
  Mutex::ScopedLock scoped_lock(state_lock_);
605
  bool trigger_pumping = queue->empty();
606
  queue->push_back(std::make_tuple(action, session_id, std::move(buffer)));
607
  return trigger_pumping;
608
}
609
610
template <typename ActionType>
611
void AgentImpl::SwapBehindLock(MessageQueue<ActionType>* vector1,
612
                               MessageQueue<ActionType>* vector2) {
613
  Mutex::ScopedLock scoped_lock(state_lock_);
614
  vector1->swap(*vector2);
615
}
616
617
void AgentImpl::PostIncomingMessage(InspectorAction action, int session_id,
618
                                    const std::string& message) {
619
  if (AppendMessage(&incoming_message_queue_, action, session_id,
620
                    Utf8ToStringView(message))) {
621
    v8::Isolate* isolate = parent_env_->isolate();
622
    platform_->CallOnForegroundThread(isolate,
623
                                      new DispatchOnInspectorBackendTask(this));
624
    isolate->RequestInterrupt(InterruptCallback, this);
625
    uv_async_send(data_written_);
626
  }
627
  NotifyMessageReceived();
628
}
629
630
void AgentImpl::WaitForFrontendMessage() {
631
  Mutex::ScopedLock scoped_lock(state_lock_);
632
  if (incoming_message_queue_.empty())
633
    incoming_message_cond_.Wait(scoped_lock);
634
}
635
636
void AgentImpl::NotifyMessageReceived() {
637
  Mutex::ScopedLock scoped_lock(state_lock_);
638
  incoming_message_cond_.Broadcast(scoped_lock);
639
}
640
641
void AgentImpl::DispatchMessages() {
642
  // This function can be reentered if there was an incoming message while
643
  // V8 was processing another inspector request (e.g. if the user is
644
  // evaluating a long-running JS code snippet). This can happen only at
645
  // specific points (e.g. the lines that call inspector_ methods)
646
  if (dispatching_messages_)
647
    return;
648
  dispatching_messages_ = true;
649
  MessageQueue<InspectorAction> tasks;
650
  do {
651
    tasks.clear();
652
    SwapBehindLock(&incoming_message_queue_, &tasks);
653
    for (const auto& task : tasks) {
654
      StringView message = std::get<2>(task)->string();
655
      switch (std::get<0>(task)) {
656
      case InspectorAction::kStartSession:
657
        CHECK_EQ(State::kAccepting, state_);
658
        session_id_ = std::get<1>(task);
659
        state_ = State::kConnected;
660
        fprintf(stderr, "Debugger attached.\n");
661
        inspector_->connectFrontend();
662
        break;
663
      case InspectorAction::kEndSession:
664
        CHECK_EQ(State::kConnected, state_);
665
        if (shutting_down_) {
666
          state_ = State::kDone;
667
        } else {
668
          state_ = State::kAccepting;
669
        }
670
        inspector_->quitMessageLoopOnPause();
671
        inspector_->disconnectFrontend();
672
        break;
673
      case InspectorAction::kSendMessage:
674
        inspector_->dispatchMessageFromFrontend(message);
675
        break;
676
      }
677
    }
678
  } while (!tasks.empty());
679
  uv_async_send(data_written_);
680
  dispatching_messages_ = false;
681
}
682
683
void AgentImpl::Write(TransportAction action, int session_id,
684
                      const StringView& inspector_message) {
685
  AppendMessage(&outgoing_message_queue_, action, session_id,
686
                StringBuffer::create(inspector_message));
687
  int err = uv_async_send(&io_thread_req_);
688
  CHECK_EQ(0, err);
689
}
690
691
// Exported class Agent
692
1722
Agent::Agent(node::Environment* env) : impl(new AgentImpl(env)) {}
693
694
3032
Agent::~Agent() {
695
1516
  delete impl;
696
1516
}
697
698
7
bool Agent::Start(v8::Platform* platform, const char* path,
699
                  const DebugOptions& options) {
700
7
  return impl->Start(platform, path, options);
701
}
702
703
void Agent::Stop() {
704
  impl->Stop();
705
}
706
707
bool Agent::IsStarted() {
708
  return impl->IsStarted();
709
}
710
711
1660
bool Agent::IsConnected() {
712
3320
  return impl->IsConnected();
713
}
714
715
void Agent::WaitForDisconnect() {
716
  impl->WaitForDisconnect();
717
}
718
719
void Agent::FatalException(v8::Local<v8::Value> error,
720
                           v8::Local<v8::Message> message) {
721
  impl->FatalException(error, message);
722
}
723
724
7
InspectorAgentDelegate::InspectorAgentDelegate(AgentImpl* agent,
725
                                               const std::string& script_path,
726
                                               const std::string& script_name,
727
                                               bool wait)
728
                                               : agent_(agent),
729
                                                 connected_(false),
730
                                                 session_id_(0),
731
                                                 script_name_(script_name),
732
                                                 script_path_(script_path),
733
                                                 target_id_(GenerateID()),
734
28
                                                 waiting_(wait) { }
735
736
737
bool InspectorAgentDelegate::StartSession(int session_id,
738
                                          const std::string& target_id) {
739
  if (connected_)
740
    return false;
741
  connected_ = true;
742
  session_id_++;
743
  agent_->PostIncomingMessage(InspectorAction::kStartSession, session_id, "");
744
  return true;
745
}
746
747
void InspectorAgentDelegate::MessageReceived(int session_id,
748
                                             const std::string& message) {
749
  // TODO(pfeldman): Instead of blocking execution while debugger
750
  // engages, node should wait for the run callback from the remote client
751
  // and initiate its startup. This is a change to node.cc that should be
752
  // upstreamed separately.
753
  if (waiting_) {
754
    if (message.find("\"Runtime.runIfWaitingForDebugger\"") !=
755
        std::string::npos) {
756
      waiting_ = false;
757
      agent_->ResumeStartup();
758
    }
759
  }
760
  agent_->PostIncomingMessage(InspectorAction::kSendMessage, session_id,
761
                              message);
762
}
763
764
void InspectorAgentDelegate::EndSession(int session_id) {
765
  connected_ = false;
766
  agent_->PostIncomingMessage(InspectorAction::kEndSession, session_id, "");
767
}
768
769
7
std::vector<std::string> InspectorAgentDelegate::GetTargetIds() {
770
42
  return { target_id_ };
771
}
772
773
std::string InspectorAgentDelegate::GetTargetTitle(const std::string& id) {
774
  return script_name_.empty() ? GetProcessTitle() : script_name_;
775
}
776
777
std::string InspectorAgentDelegate::GetTargetUrl(const std::string& id) {
778
  return "file://" + script_path_;
779
}
780
781
}  // namespace inspector
782
}  // namespace node