GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/inspector_agent.cc Lines: 18 367 4.9 %
Date: 2016-09-10 Branches: 3 211 1.4 %

Line Branch Exec Source
1
#include "inspector_agent.h"
2
3
#include "inspector_socket.h"
4
#include "env.h"
5
#include "env-inl.h"
6
#include "node.h"
7
#include "node_mutex.h"
8
#include "node_version.h"
9
#include "v8-platform.h"
10
#include "util.h"
11
12
#include "platform/v8_inspector/public/InspectorVersion.h"
13
#include "platform/v8_inspector/public/V8Inspector.h"
14
#include "platform/v8_inspector/public/V8InspectorClient.h"
15
#include "platform/v8_inspector/public/V8InspectorSession.h"
16
#include "platform/v8_inspector/public/V8StackTrace.h"
17
#include "platform/inspector_protocol/InspectorProtocol.h"
18
19
#include "libplatform/libplatform.h"
20
21
#include <string.h>
22
#include <utility>
23
#include <vector>
24
25
// We need pid to use as ID with Chrome
26
#if defined(_MSC_VER)
27
#include <direct.h>
28
#include <io.h>
29
#define getpid GetCurrentProcessId
30
#else
31
#include <unistd.h>  // setuid, getuid
32
#endif
33
34
namespace node {
35
namespace {
36
37
const char TAG_CONNECT[] = "#connect";
38
const char TAG_DISCONNECT[] = "#disconnect";
39
40
const char DEVTOOLS_PATH[] = "/node";
41
const char DEVTOOLS_HASH[] = V8_INSPECTOR_REVISION;
42
43
void PrintDebuggerReadyMessage(int port) {
44
  fprintf(stderr, "Debugger listening on port %d.\n"
45
    "Warning: This is an experimental feature and could change at any time.\n"
46
    "To start debugging, open the following URL in Chrome:\n"
47
    "    chrome-devtools://devtools/remote/serve_file/"
48
    "@%s/inspector.html?"
49
    "experiments=true&v8only=true&ws=localhost:%d/node\n",
50
      port, DEVTOOLS_HASH, port);
51
}
52
53
bool AcceptsConnection(inspector_socket_t* socket, const std::string& path) {
54
  return StringEqualNoCaseN(path.c_str(), DEVTOOLS_PATH,
55
                            sizeof(DEVTOOLS_PATH) - 1);
56
}
57
58
void Escape(std::string* string) {
59
  for (char& c : *string) {
60
    c = (c == '\"' || c == '\\') ? '_' : c;
61
  }
62
}
63
64
void DisposeInspector(inspector_socket_t* socket, int status) {
65
  delete socket;
66
}
67
68
void DisconnectAndDisposeIO(inspector_socket_t* socket) {
69
  if (socket) {
70
    inspector_close(socket, DisposeInspector);
71
  }
72
}
73
74
void OnBufferAlloc(uv_handle_t* handle, size_t len, uv_buf_t* buf) {
75
  buf->base = new char[len];
76
  buf->len = len;
77
}
78
79
void SendHttpResponse(inspector_socket_t* socket, const char* response,
80
                      size_t len) {
81
  const char HEADERS[] = "HTTP/1.0 200 OK\r\n"
82
                         "Content-Type: application/json; charset=UTF-8\r\n"
83
                         "Cache-Control: no-cache\r\n"
84
                         "Content-Length: %ld\r\n"
85
                         "\r\n";
86
  char header[sizeof(HEADERS) + 20];
87
  int header_len = snprintf(header, sizeof(header), HEADERS, len);
88
  inspector_write(socket, header, header_len);
89
  inspector_write(socket, response, len);
90
}
91
92
void SendVersionResponse(inspector_socket_t* socket) {
93
  const char VERSION_RESPONSE_TEMPLATE[] =
94
      "[ {"
95
      "  \"Browser\": \"node.js/%s\","
96
      "  \"Protocol-Version\": \"1.1\","
97
      "  \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
98
            "(KHTML, like Gecko) Chrome/45.0.2446.0 Safari/537.36\","
99
      "  \"WebKit-Version\": \"537.36 (@198122)\""
100
      "} ]";
101
  char buffer[sizeof(VERSION_RESPONSE_TEMPLATE) + 128];
102
  size_t len = snprintf(buffer, sizeof(buffer), VERSION_RESPONSE_TEMPLATE,
103
                        NODE_VERSION);
104
  ASSERT_LT(len, sizeof(buffer));
105
  SendHttpResponse(socket, buffer, len);
106
}
107
108
std::string GetProcessTitle() {
109
  // uv_get_process_title will trim the title if it is too long.
110
  char title[2048];
111
  int err = uv_get_process_title(title, sizeof(title));
112
  if (err == 0) {
113
    return title;
114
  } else {
115
    return "Node.js";
116
  }
117
}
118
119
void SendTargentsListResponse(inspector_socket_t* socket,
120
                              const std::string& script_name_,
121
                              const std::string& script_path_,
122
                              int port) {
123
  const char LIST_RESPONSE_TEMPLATE[] =
124
      "[ {"
125
      "  \"description\": \"node.js instance\","
126
      "  \"devtoolsFrontendUrl\": "
127
            "\"https://chrome-devtools-frontend.appspot.com/serve_file/"
128
            "@%s/inspector.html?experiments=true&v8only=true"
129
            "&ws=localhost:%d%s\","
130
      "  \"faviconUrl\": \"https://nodejs.org/static/favicon.ico\","
131
      "  \"id\": \"%d\","
132
      "  \"title\": \"%s\","
133
      "  \"type\": \"node\","
134
      "  \"url\": \"%s\","
135
      "  \"webSocketDebuggerUrl\": \"ws://localhost:%d%s\""
136
      "} ]";
137
  std::string title = script_name_.empty() ? GetProcessTitle() : script_name_;
138
139
  // This attribute value is a "best effort" URL that is passed as a JSON
140
  // string. It is not guaranteed to resolve to a valid resource.
141
  std::string url = "file://" + script_path_;
142
143
  Escape(&title);
144
  Escape(&url);
145
146
  const int NUMERIC_FIELDS_LENGTH = 5 * 2 + 20;  // 2 x port + 1 x pid (64 bit)
147
148
  int buf_len = sizeof(LIST_RESPONSE_TEMPLATE) + sizeof(DEVTOOLS_HASH) +
149
                sizeof(DEVTOOLS_PATH) * 2 + title.length() +
150
                url.length() + NUMERIC_FIELDS_LENGTH;
151
  std::string buffer(buf_len, '\0');
152
153
  int len = snprintf(&buffer[0], buf_len, LIST_RESPONSE_TEMPLATE,
154
                     DEVTOOLS_HASH, port, DEVTOOLS_PATH, getpid(),
155
                     title.c_str(), url.c_str(),
156
                     port, DEVTOOLS_PATH);
157
  buffer.resize(len);
158
  ASSERT_LT(len, buf_len);  // Buffer should be big enough!
159
  SendHttpResponse(socket, buffer.data(), len);
160
}
161
162
const char* match_path_segment(const char* path, const char* expected) {
163
  size_t len = strlen(expected);
164
  if (StringEqualNoCaseN(path, expected, len)) {
165
    if (path[len] == '/') return path + len + 1;
166
    if (path[len] == '\0') return path + len;
167
  }
168
  return nullptr;
169
}
170
171
bool RespondToGet(inspector_socket_t* socket, const std::string& script_name_,
172
                  const std::string& script_path_, const std::string& path,
173
                  int port) {
174
  const char* command = match_path_segment(path.c_str(), "/json");
175
  if (command == nullptr)
176
    return false;
177
178
  if (match_path_segment(command, "list") || command[0] == '\0') {
179
    SendTargentsListResponse(socket, script_name_, script_path_, port);
180
  } else if (match_path_segment(command, "version")) {
181
    SendVersionResponse(socket);
182
  } else {
183
    const char* pid = match_path_segment(command, "activate");
184
    if (pid == nullptr || atoi(pid) != getpid())
185
      return false;
186
    const char TARGET_ACTIVATED[] = "Target activated";
187
    SendHttpResponse(socket, TARGET_ACTIVATED, sizeof(TARGET_ACTIVATED) - 1);
188
  }
189
  return true;
190
}
191
192
}  // namespace
193
194
namespace inspector {
195
196
197
class V8NodeInspector;
198
199
class AgentImpl {
200
 public:
201
  explicit AgentImpl(node::Environment* env);
202
  ~AgentImpl();
203
204
  // Start the inspector agent thread
205
  bool Start(v8::Platform* platform, const char* path, int port, bool wait);
206
  // Stop the inspector agent
207
  void Stop();
208
209
  bool IsStarted();
210
1570
  bool IsConnected() {  return state_ == State::kConnected; }
211
  void WaitForDisconnect();
212
213
  void FatalException(v8::Local<v8::Value> error,
214
                      v8::Local<v8::Message> message);
215
216
 private:
217
  using MessageQueue = std::vector<std::pair<int, String16>>;
218
  enum class State { kNew, kAccepting, kConnected, kDone, kError };
219
220
  static void ThreadCbIO(void* agent);
221
  static void OnSocketConnectionIO(uv_stream_t* server, int status);
222
  static bool OnInspectorHandshakeIO(inspector_socket_t* socket,
223
                                     enum inspector_handshake_event state,
224
                                     const std::string& path);
225
  static void WriteCbIO(uv_async_t* async);
226
227
  void InstallInspectorOnProcess();
228
229
  void WorkerRunIO();
230
  void OnInspectorConnectionIO(inspector_socket_t* socket);
231
  void OnRemoteDataIO(inspector_socket_t* stream, ssize_t read,
232
                      const uv_buf_t* b);
233
  void SetConnected(bool connected);
234
  void DispatchMessages();
235
  void Write(int session_id, const String16& message);
236
  bool AppendMessage(MessageQueue* vector, int session_id,
237
                     const String16& message);
238
  void SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2);
239
  void PostIncomingMessage(const String16& message);
240
  State ToState(State state);
241
242
  uv_sem_t start_sem_;
243
  ConditionVariable pause_cond_;
244
  Mutex pause_lock_;
245
  Mutex queue_lock_;
246
  uv_thread_t thread_;
247
  uv_loop_t child_loop_;
248
249
  int port_;
250
  bool wait_;
251
  bool shutting_down_;
252
  State state_;
253
  node::Environment* parent_env_;
254
255
  uv_async_t* data_written_;
256
  uv_async_t io_thread_req_;
257
  inspector_socket_t* client_socket_;
258
  V8NodeInspector* inspector_;
259
  v8::Platform* platform_;
260
  MessageQueue incoming_message_queue_;
261
  MessageQueue outgoing_message_queue_;
262
  bool dispatching_messages_;
263
  int frontend_session_id_;
264
  int backend_session_id_;
265
266
  std::string script_name_;
267
  std::string script_path_;
268
269
  friend class ChannelImpl;
270
  friend class DispatchOnInspectorBackendTask;
271
  friend class SetConnectedTask;
272
  friend class V8NodeInspector;
273
  friend void InterruptCallback(v8::Isolate*, void* agent);
274
  friend void DataCallback(uv_stream_t* stream, ssize_t read,
275
                           const uv_buf_t* buf);
276
};
277
278
void InterruptCallback(v8::Isolate*, void* agent) {
279
  static_cast<AgentImpl*>(agent)->DispatchMessages();
280
}
281
282
void DataCallback(uv_stream_t* stream, ssize_t read, const uv_buf_t* buf) {
283
  inspector_socket_t* socket = inspector_from_stream(stream);
284
  static_cast<AgentImpl*>(socket->data)->OnRemoteDataIO(socket, read, buf);
285
}
286
287
class DispatchOnInspectorBackendTask : public v8::Task {
288
 public:
289
  explicit DispatchOnInspectorBackendTask(AgentImpl* agent) : agent_(agent) {}
290
291
  void Run() override {
292
    agent_->DispatchMessages();
293
  }
294
295
 private:
296
  AgentImpl* agent_;
297
};
298
299
class ChannelImpl final : public blink::protocol::FrontendChannel {
300
 public:
301
  explicit ChannelImpl(AgentImpl* agent): agent_(agent) {}
302
  virtual ~ChannelImpl() {}
303
 private:
304
  void sendProtocolResponse(int callId, const String16& message) override {
305
    sendMessageToFrontend(message);
306
  }
307
308
  void sendProtocolNotification(const String16& message) override {
309
    sendMessageToFrontend(message);
310
  }
311
312
  void flushProtocolNotifications() override { }
313
314
  void sendMessageToFrontend(const String16& message) {
315
    agent_->Write(agent_->frontend_session_id_, message);
316
  }
317
318
  AgentImpl* const agent_;
319
};
320
321
// Used in V8NodeInspector::currentTimeMS() below.
322
#define NANOS_PER_MSEC 1000000
323
324
using V8Inspector = v8_inspector::V8Inspector;
325
326
class V8NodeInspector : public v8_inspector::V8InspectorClient {
327
 public:
328
  V8NodeInspector(AgentImpl* agent, node::Environment* env,
329
                  v8::Platform* platform)
330
                  : agent_(agent),
331
                    isolate_(env->isolate()),
332
                    platform_(platform),
333
                    terminated_(false),
334
                    running_nested_loop_(false),
335
                    inspector_(V8Inspector::create(env->isolate(), this)) {
336
    inspector_->contextCreated(
337
        v8_inspector::V8ContextInfo(env->context(), 1, "NodeJS Main Context"));
338
  }
339
340
  void runMessageLoopOnPause(int context_group_id) override {
341
    if (running_nested_loop_)
342
      return;
343
    terminated_ = false;
344
    running_nested_loop_ = true;
345
    agent_->DispatchMessages();
346
    do {
347
      {
348
        Mutex::ScopedLock scoped_lock(agent_->pause_lock_);
349
        agent_->pause_cond_.Wait(scoped_lock);
350
      }
351
      while (v8::platform::PumpMessageLoop(platform_, isolate_))
352
        {}
353
    } while (!terminated_);
354
    terminated_ = false;
355
    running_nested_loop_ = false;
356
  }
357
358
  double currentTimeMS() override {
359
    return uv_hrtime() * 1.0 / NANOS_PER_MSEC;
360
  }
361
362
  void quitMessageLoopOnPause() override {
363
    terminated_ = true;
364
  }
365
366
  void connectFrontend() {
367
    session_ = inspector_->connect(1, new ChannelImpl(agent_), nullptr);
368
  }
369
370
  void disconnectFrontend() {
371
    session_.reset();
372
  }
373
374
  void dispatchMessageFromFrontend(const String16& message) {
375
    CHECK(session_);
376
    session_->dispatchProtocolMessage(message);
377
  }
378
379
  V8Inspector* inspector() {
380
    return inspector_.get();
381
  }
382
383
 private:
384
  AgentImpl* agent_;
385
  v8::Isolate* isolate_;
386
  v8::Platform* platform_;
387
  bool terminated_;
388
  bool running_nested_loop_;
389
  std::unique_ptr<V8Inspector> inspector_;
390
  std::unique_ptr<v8_inspector::V8InspectorSession> session_;
391
};
392
393
1631
AgentImpl::AgentImpl(Environment* env) : port_(0),
394
                                         wait_(false),
395
                                         shutting_down_(false),
396
                                         state_(State::kNew),
397
                                         parent_env_(env),
398
                                         data_written_(new uv_async_t()),
399
                                         client_socket_(nullptr),
400
                                         inspector_(nullptr),
401
                                         platform_(nullptr),
402
                                         dispatching_messages_(false),
403
                                         frontend_session_id_(0),
404
8155
                                         backend_session_id_(0) {
405
1631
  CHECK_EQ(0, uv_sem_init(&start_sem_, 0));
406
3262
  memset(&io_thread_req_, 0, sizeof(io_thread_req_));
407
3262
  CHECK_EQ(0, uv_async_init(env->event_loop(), data_written_, nullptr));
408
1631
  uv_unref(reinterpret_cast<uv_handle_t*>(data_written_));
409
1631
}
410
411
10045
AgentImpl::~AgentImpl() {
412
  auto close_cb = [](uv_handle_t* handle) {
413
    delete reinterpret_cast<uv_async_t*>(handle);
414
  };
415
1435
  uv_close(reinterpret_cast<uv_handle_t*>(data_written_), close_cb);
416
1435
  data_written_ = nullptr;
417
1435
}
418
419
void InspectorConsoleCall(const v8::FunctionCallbackInfo<v8::Value>& info) {
420
  v8::Isolate* isolate = info.GetIsolate();
421
  v8::Local<v8::Context> context = isolate->GetCurrentContext();
422
423
  CHECK(info.Data()->IsArray());
424
  v8::Local<v8::Array> args = info.Data().As<v8::Array>();
425
  CHECK_EQ(args->Length(), 3);
426
427
  v8::Local<v8::Value> inspector_method =
428
      args->Get(context, 0).ToLocalChecked();
429
  CHECK(inspector_method->IsFunction());
430
  v8::Local<v8::Value> node_method =
431
      args->Get(context, 1).ToLocalChecked();
432
  CHECK(node_method->IsFunction());
433
  v8::Local<v8::Value> config_value =
434
      args->Get(context, 2).ToLocalChecked();
435
  CHECK(config_value->IsObject());
436
  v8::Local<v8::Object> config_object = config_value.As<v8::Object>();
437
438
  std::vector<v8::Local<v8::Value>> call_args(info.Length());
439
  for (int i = 0; i < info.Length(); ++i) {
440
    call_args[i] = info[i];
441
  }
442
443
  v8::Local<v8::String> in_call_key = OneByteString(isolate, "in_call");
444
  bool in_call = config_object->Has(context, in_call_key).FromMaybe(false);
445
  if (!in_call) {
446
    CHECK(config_object->Set(context,
447
                             in_call_key,
448
                             v8::True(isolate)).FromJust());
449
    CHECK(!inspector_method.As<v8::Function>()->Call(
450
        context,
451
        info.Holder(),
452
        call_args.size(),
453
        call_args.data()).IsEmpty());
454
  }
455
456
  v8::TryCatch try_catch(info.GetIsolate());
457
  static_cast<void>(node_method.As<v8::Function>()->Call(context,
458
                                                         info.Holder(),
459
                                                         call_args.size(),
460
                                                         call_args.data()));
461
  CHECK(config_object->Delete(context, in_call_key).FromJust());
462
  if (try_catch.HasCaught())
463
    try_catch.ReThrow();
464
}
465
466
void InspectorWrapConsoleCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
467
  Environment* env = Environment::GetCurrent(args);
468
469
  if (args.Length() != 3 || !args[0]->IsFunction() ||
470
      !args[1]->IsFunction() || !args[2]->IsObject()) {
471
    return env->ThrowError("inspector.wrapConsoleCall takes exactly 3 "
472
        "arguments: two functions and an object.");
473
  }
474
475
  v8::Local<v8::Array> array = v8::Array::New(env->isolate(), args.Length());
476
  CHECK(array->Set(env->context(), 0, args[0]).FromJust());
477
  CHECK(array->Set(env->context(), 1, args[1]).FromJust());
478
  CHECK(array->Set(env->context(), 2, args[2]).FromJust());
479
  args.GetReturnValue().Set(v8::Function::New(env->context(),
480
                                              InspectorConsoleCall,
481
                                              array).ToLocalChecked());
482
}
483
484
bool AgentImpl::Start(v8::Platform* platform, const char* path,
485
                      int port, bool wait) {
486
  auto env = parent_env_;
487
  inspector_ = new V8NodeInspector(this, env, platform);
488
  platform_ = platform;
489
  if (path != nullptr)
490
    script_name_ = path;
491
492
  InstallInspectorOnProcess();
493
494
  int err = uv_loop_init(&child_loop_);
495
  CHECK_EQ(err, 0);
496
497
  port_ = port;
498
  wait_ = wait;
499
500
  err = uv_thread_create(&thread_, AgentImpl::ThreadCbIO, this);
501
  CHECK_EQ(err, 0);
502
  uv_sem_wait(&start_sem_);
503
504
  if (state_ == State::kError) {
505
    Stop();
506
    return false;
507
  }
508
  state_ = State::kAccepting;
509
  if (wait) {
510
    DispatchMessages();
511
  }
512
  return true;
513
}
514
515
void AgentImpl::Stop() {
516
  int err = uv_thread_join(&thread_);
517
  CHECK_EQ(err, 0);
518
  delete inspector_;
519
}
520
521
bool AgentImpl::IsStarted() {
522
  return !!platform_;
523
}
524
525
void AgentImpl::WaitForDisconnect() {
526
  shutting_down_ = true;
527
  fprintf(stderr, "Waiting for the debugger to disconnect...\n");
528
  inspector_->runMessageLoopOnPause(0);
529
}
530
531
#define READONLY_PROPERTY(obj, str, var)                                      \
532
  do {                                                                        \
533
    obj->DefineOwnProperty(env->context(),                                    \
534
                           OneByteString(env->isolate(), str),                \
535
                           var,                                               \
536
                           v8::ReadOnly).FromJust();                          \
537
  } while (0)
538
539
void AgentImpl::InstallInspectorOnProcess() {
540
  auto env = parent_env_;
541
  v8::Local<v8::Object> process = env->process_object();
542
  v8::Local<v8::Object> inspector = v8::Object::New(env->isolate());
543
  READONLY_PROPERTY(process, "inspector", inspector);
544
  env->SetMethod(inspector, "wrapConsoleCall", InspectorWrapConsoleCall);
545
}
546
547
String16 ToProtocolString(v8::Local<v8::Value> value) {
548
  if (value.IsEmpty() || value->IsNull() || value->IsUndefined() ||
549
      !value->IsString()) {
550
    return String16();
551
  }
552
  v8::Local<v8::String> string_value = v8::Local<v8::String>::Cast(value);
553
  std::basic_string<uint16_t> buffer(string_value->Length(), '\0');
554
  string_value->Write(&buffer[0], 0, string_value->Length());
555
  return String16(buffer);
556
}
557
558
void AgentImpl::FatalException(v8::Local<v8::Value> error,
559
                               v8::Local<v8::Message> message) {
560
  if (!IsStarted())
561
    return;
562
  auto env = parent_env_;
563
  v8::Local<v8::Context> context = env->context();
564
565
  int script_id = message->GetScriptOrigin().ScriptID()->Value();
566
  std::unique_ptr<v8_inspector::V8StackTrace> stack_trace =
567
      inspector_->inspector()->createStackTrace(message->GetStackTrace());
568
569
  if (stack_trace && !stack_trace->isEmpty() &&
570
      String16::fromInteger(script_id) == stack_trace->topScriptId()) {
571
    script_id = 0;
572
  }
573
574
  inspector_->inspector()->exceptionThrown(
575
      context,
576
      "Uncaught",
577
      error,
578
      ToProtocolString(message->Get()),
579
      ToProtocolString(message->GetScriptResourceName()),
580
      message->GetLineNumber(context).FromMaybe(0),
581
      message->GetStartColumn(context).FromMaybe(0),
582
      std::move(stack_trace),
583
      script_id);
584
  WaitForDisconnect();
585
}
586
587
// static
588
void AgentImpl::ThreadCbIO(void* agent) {
589
  static_cast<AgentImpl*>(agent)->WorkerRunIO();
590
}
591
592
// static
593
void AgentImpl::OnSocketConnectionIO(uv_stream_t* server, int status) {
594
  if (status == 0) {
595
    inspector_socket_t* socket = new inspector_socket_t();
596
    socket->data = server->data;
597
    if (inspector_accept(server, socket,
598
                         AgentImpl::OnInspectorHandshakeIO) != 0) {
599
      delete socket;
600
    }
601
  }
602
}
603
604
// static
605
bool AgentImpl::OnInspectorHandshakeIO(inspector_socket_t* socket,
606
                                       enum inspector_handshake_event state,
607
                                       const std::string& path) {
608
  AgentImpl* agent = static_cast<AgentImpl*>(socket->data);
609
  switch (state) {
610
  case kInspectorHandshakeHttpGet:
611
    return RespondToGet(socket, agent->script_name_, agent->script_path_, path,
612
                        agent->port_);
613
  case kInspectorHandshakeUpgrading:
614
    return AcceptsConnection(socket, path);
615
  case kInspectorHandshakeUpgraded:
616
    agent->OnInspectorConnectionIO(socket);
617
    return true;
618
  case kInspectorHandshakeFailed:
619
    delete socket;
620
    return false;
621
  default:
622
    UNREACHABLE();
623
  }
624
}
625
626
void AgentImpl::OnRemoteDataIO(inspector_socket_t* socket,
627
                               ssize_t read,
628
                               const uv_buf_t* buf) {
629
  Mutex::ScopedLock scoped_lock(pause_lock_);
630
  if (read > 0) {
631
    String16 str = String16::fromUTF8(buf->base, read);
632
    // TODO(pfeldman): Instead of blocking execution while debugger
633
    // engages, node should wait for the run callback from the remote client
634
    // and initiate its startup. This is a change to node.cc that should be
635
    // upstreamed separately.
636
    if (wait_&& str.find("\"Runtime.runIfWaitingForDebugger\"")
637
        != std::string::npos) {
638
      wait_ = false;
639
      uv_sem_post(&start_sem_);
640
    }
641
    PostIncomingMessage(str);
642
  } else if (read <= 0) {
643
    // EOF
644
    if (client_socket_ == socket) {
645
      String16 message(TAG_DISCONNECT, sizeof(TAG_DISCONNECT) - 1);
646
      client_socket_ = nullptr;
647
      PostIncomingMessage(message);
648
    }
649
    DisconnectAndDisposeIO(socket);
650
  }
651
  if (buf) {
652
    delete[] buf->base;
653
  }
654
  pause_cond_.Broadcast(scoped_lock);
655
}
656
657
// static
658
void AgentImpl::WriteCbIO(uv_async_t* async) {
659
  AgentImpl* agent = static_cast<AgentImpl*>(async->data);
660
  inspector_socket_t* socket = agent->client_socket_;
661
  if (socket) {
662
    MessageQueue outgoing_messages;
663
    agent->SwapBehindLock(&agent->outgoing_message_queue_, &outgoing_messages);
664
    for (const MessageQueue::value_type& outgoing : outgoing_messages) {
665
      if (outgoing.first == agent->frontend_session_id_) {
666
        std::string message = outgoing.second.utf8();
667
        inspector_write(socket, message.c_str(), message.length());
668
      }
669
    }
670
  }
671
}
672
673
void AgentImpl::WorkerRunIO() {
674
  sockaddr_in addr;
675
  uv_tcp_t server;
676
  int err = uv_loop_init(&child_loop_);
677
  CHECK_EQ(err, 0);
678
  err = uv_async_init(&child_loop_, &io_thread_req_, AgentImpl::WriteCbIO);
679
  CHECK_EQ(err, 0);
680
  io_thread_req_.data = this;
681
  if (!script_name_.empty()) {
682
    uv_fs_t req;
683
    if (0 == uv_fs_realpath(&child_loop_, &req, script_name_.c_str(), nullptr))
684
      script_path_ = std::string(reinterpret_cast<char*>(req.ptr));
685
    uv_fs_req_cleanup(&req);
686
  }
687
  uv_tcp_init(&child_loop_, &server);
688
  uv_ip4_addr("0.0.0.0", port_, &addr);
689
  server.data = this;
690
  err = uv_tcp_bind(&server,
691
                    reinterpret_cast<const struct sockaddr*>(&addr), 0);
692
  if (err == 0) {
693
    err = uv_listen(reinterpret_cast<uv_stream_t*>(&server), 1,
694
                    OnSocketConnectionIO);
695
  }
696
  if (err != 0) {
697
    fprintf(stderr, "Unable to open devtools socket: %s\n", uv_strerror(err));
698
    state_ = State::kError;  // Safe, main thread is waiting on semaphore
699
    uv_close(reinterpret_cast<uv_handle_t*>(&io_thread_req_), nullptr);
700
    uv_close(reinterpret_cast<uv_handle_t*>(&server), nullptr);
701
    uv_loop_close(&child_loop_);
702
    uv_sem_post(&start_sem_);
703
    return;
704
  }
705
  PrintDebuggerReadyMessage(port_);
706
  if (!wait_) {
707
    uv_sem_post(&start_sem_);
708
  }
709
  uv_run(&child_loop_, UV_RUN_DEFAULT);
710
  uv_close(reinterpret_cast<uv_handle_t*>(&io_thread_req_), nullptr);
711
  uv_close(reinterpret_cast<uv_handle_t*>(&server), nullptr);
712
  DisconnectAndDisposeIO(client_socket_);
713
  uv_run(&child_loop_, UV_RUN_NOWAIT);
714
  err = uv_loop_close(&child_loop_);
715
  CHECK_EQ(err, 0);
716
}
717
718
bool AgentImpl::AppendMessage(MessageQueue* queue, int session_id,
719
                              const String16& message) {
720
  Mutex::ScopedLock scoped_lock(queue_lock_);
721
  bool trigger_pumping = queue->empty();
722
  queue->push_back(std::make_pair(session_id, message));
723
  return trigger_pumping;
724
}
725
726
void AgentImpl::SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2) {
727
  Mutex::ScopedLock scoped_lock(queue_lock_);
728
  vector1->swap(*vector2);
729
}
730
731
void AgentImpl::PostIncomingMessage(const String16& message) {
732
  if (AppendMessage(&incoming_message_queue_, frontend_session_id_, message)) {
733
    v8::Isolate* isolate = parent_env_->isolate();
734
    platform_->CallOnForegroundThread(isolate,
735
                                      new DispatchOnInspectorBackendTask(this));
736
    isolate->RequestInterrupt(InterruptCallback, this);
737
    uv_async_send(data_written_);
738
  }
739
}
740
741
void AgentImpl::OnInspectorConnectionIO(inspector_socket_t* socket) {
742
  if (client_socket_) {
743
    DisconnectAndDisposeIO(socket);
744
    return;
745
  }
746
  client_socket_ = socket;
747
  inspector_read_start(socket, OnBufferAlloc, DataCallback);
748
  frontend_session_id_++;
749
  PostIncomingMessage(String16(TAG_CONNECT, sizeof(TAG_CONNECT) - 1));
750
}
751
752
void AgentImpl::DispatchMessages() {
753
  // This function can be reentered if there was an incoming message while
754
  // V8 was processing another inspector request (e.g. if the user is
755
  // evaluating a long-running JS code snippet). This can happen only at
756
  // specific points (e.g. the lines that call inspector_ methods)
757
  if (dispatching_messages_)
758
    return;
759
  dispatching_messages_ = true;
760
  MessageQueue tasks;
761
  do {
762
    tasks.clear();
763
    SwapBehindLock(&incoming_message_queue_, &tasks);
764
    for (const MessageQueue::value_type& pair : tasks) {
765
      const String16& message = pair.second;
766
      if (message == TAG_CONNECT) {
767
        CHECK_EQ(State::kAccepting, state_);
768
        backend_session_id_++;
769
        state_ = State::kConnected;
770
        fprintf(stderr, "Debugger attached.\n");
771
        inspector_->connectFrontend();
772
      } else if (message == TAG_DISCONNECT) {
773
        CHECK_EQ(State::kConnected, state_);
774
        if (shutting_down_) {
775
          state_ = State::kDone;
776
        } else {
777
          PrintDebuggerReadyMessage(port_);
778
          state_ = State::kAccepting;
779
        }
780
        inspector_->quitMessageLoopOnPause();
781
        inspector_->disconnectFrontend();
782
      } else {
783
        inspector_->dispatchMessageFromFrontend(message);
784
      }
785
    }
786
  } while (!tasks.empty());
787
  uv_async_send(data_written_);
788
  dispatching_messages_ = false;
789
}
790
791
void AgentImpl::Write(int session_id, const String16& message) {
792
  AppendMessage(&outgoing_message_queue_, session_id, message);
793
  int err = uv_async_send(&io_thread_req_);
794
  CHECK_EQ(0, err);
795
}
796
797
// Exported class Agent
798
1631
Agent::Agent(node::Environment* env) : impl(new AgentImpl(env)) {}
799
800
2870
Agent::~Agent() {
801
1435
  delete impl;
802
1435
}
803
804
bool Agent::Start(v8::Platform* platform, const char* path,
805
                  int port, bool wait) {
806
  return impl->Start(platform, path, port, wait);
807
}
808
809
void Agent::Stop() {
810
  impl->Stop();
811
}
812
813
bool Agent::IsStarted() {
814
  return impl->IsStarted();
815
}
816
817
1570
bool Agent::IsConnected() {
818
3140
  return impl->IsConnected();
819
}
820
821
void Agent::WaitForDisconnect() {
822
  impl->WaitForDisconnect();
823
}
824
825
void Agent::FatalException(v8::Local<v8::Value> error,
826
                           v8::Local<v8::Message> message) {
827
  impl->FatalException(error, message);
828
}
829
830
831
}  // namespace inspector
832
}  // namespace node