GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../src/inspector_io.cc Lines: 270 277 97.5 %
Date: 2017-11-19 Branches: 86 118 72.9 %

Line Branch Exec Source
1
#include "inspector_io.h"
2
3
#include "inspector_socket_server.h"
4
#include "env-inl.h"
5
#include "node.h"
6
#include "node_crypto.h"
7
#include "node_mutex.h"
8
#include "v8-inspector.h"
9
#include "util.h"
10
#include "zlib.h"
11
12
#include <sstream>
13
#include <unicode/unistr.h>
14
15
#include <string.h>
16
#include <vector>
17
18
19
namespace node {
20
namespace inspector {
21
namespace {
22
using AsyncAndAgent = std::pair<uv_async_t, Agent*>;
23
using v8_inspector::StringBuffer;
24
using v8_inspector::StringView;
25
26
template<typename Transport>
27
using TransportAndIo = std::pair<Transport*, InspectorIo*>;
28
29
6
std::string GetProcessTitle() {
30
  char title[2048];
31
6
  int err = uv_get_process_title(title, sizeof(title));
32
6
  if (err == 0) {
33
6
    return title;
34
  } else {
35
    // Title is too long, or could not be retrieved.
36
    return "Node.js";
37
  }
38
}
39
40
67
std::string ScriptPath(uv_loop_t* loop, const std::string& script_name) {
41
67
  std::string script_path;
42
43
67
  if (!script_name.empty()) {
44
    uv_fs_t req;
45
55
    req.ptr = nullptr;
46
55
    if (0 == uv_fs_realpath(loop, &req, script_name.c_str(), nullptr)) {
47
54
      CHECK_NE(req.ptr, nullptr);
48
54
      script_path = std::string(static_cast<char*>(req.ptr));
49
    }
50
55
    uv_fs_req_cleanup(&req);
51
  }
52
53
67
  return script_path;
54
}
55
56
// UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
57
// Used ver 4 - with numbers
58
67
std::string GenerateID() {
59
  uint16_t buffer[8];
60
67
  CHECK(crypto::EntropySource(reinterpret_cast<unsigned char*>(buffer),
61
                              sizeof(buffer)));
62
63
  char uuid[256];
64
  snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
65
67
           buffer[0],  // time_low
66
67
           buffer[1],  // time_mid
67
67
           buffer[2],  // time_low
68
67
           (buffer[3] & 0x0fff) | 0x4000,  // time_hi_and_version
69
67
           (buffer[4] & 0x3fff) | 0x8000,  // clk_seq_hi clk_seq_low
70
67
           buffer[5],  // node
71
67
           buffer[6],
72
536
           buffer[7]);
73
67
  return uuid;
74
}
75
76
1044
std::string StringViewToUtf8(const StringView& view) {
77
1044
  if (view.is8Bit()) {
78
    return std::string(reinterpret_cast<const char*>(view.characters8()),
79
                       view.length());
80
  }
81
1044
  const uint16_t* source = view.characters16();
82
1044
  const UChar* unicodeSource = reinterpret_cast<const UChar*>(source);
83
  static_assert(sizeof(*source) == sizeof(*unicodeSource),
84
                "sizeof(*source) == sizeof(*unicodeSource)");
85
86
1044
  size_t result_length = view.length() * sizeof(*source);
87
1044
  std::string result(result_length, '\0');
88
2088
  UnicodeString utf16(unicodeSource, view.length());
89
  // ICU components for std::string compatibility are not enabled in build...
90
1044
  bool done = false;
91
3132
  while (!done) {
92
1044
    CheckedArrayByteSink sink(&result[0], result_length);
93
1044
    utf16.toUTF8(sink);
94
1044
    result_length = sink.NumberOfBytesAppended();
95
1044
    result.resize(result_length);
96
1044
    done = !sink.Overflowed();
97
1044
  }
98
2088
  return result;
99
}
100
101
2
void HandleSyncCloseCb(uv_handle_t* handle) {
102
2
  *static_cast<bool*>(handle->data) = true;
103
2
}
104
105
2
int CloseAsyncAndLoop(uv_async_t* async) {
106
2
  bool is_closed = false;
107
2
  async->data = &is_closed;
108
2
  uv_close(reinterpret_cast<uv_handle_t*>(async), HandleSyncCloseCb);
109
6
  while (!is_closed)
110
2
    uv_run(async->loop, UV_RUN_ONCE);
111
2
  async->data = nullptr;
112
2
  return uv_loop_close(async->loop);
113
}
114
115
// Delete main_thread_req_ on async handle close
116
2
void ReleasePairOnAsyncClose(uv_handle_t* async) {
117
  std::unique_ptr<AsyncAndAgent> pair(node::ContainerOf(&AsyncAndAgent::first,
118
2
      reinterpret_cast<uv_async_t*>(async)));
119
  // Unique_ptr goes out of scope here and pointer is deleted.
120
2
}
121
122
}  // namespace
123
124
3686
std::unique_ptr<StringBuffer> Utf8ToStringView(const std::string& message) {
125
  UnicodeString utf16 =
126
3686
      UnicodeString::fromUTF8(StringPiece(message.data(), message.length()));
127
3686
  StringView view(reinterpret_cast<const uint16_t*>(utf16.getBuffer()),
128
7372
                  utf16.length());
129
3686
  return StringBuffer::create(view);
130
}
131
132
133
20
class IoSessionDelegate : public InspectorSessionDelegate {
134
 public:
135
10
  explicit IoSessionDelegate(InspectorIo* io) : io_(io) { }
136
  bool WaitForFrontendMessageWhilePaused() override;
137
  void SendMessageToFrontend(const v8_inspector::StringView& message) override;
138
 private:
139
  InspectorIo* io_;
140
};
141
142
// Passed to InspectorSocketServer to handle WS inspector protocol events,
143
// mostly session start, message received, and session end.
144
15
class InspectorIoDelegate: public node::inspector::SocketServerDelegate {
145
 public:
146
  InspectorIoDelegate(InspectorIo* io, const std::string& script_path,
147
                      const std::string& script_name, bool wait);
148
  // Calls PostIncomingMessage() with appropriate InspectorAction:
149
  //   kStartSession
150
  bool StartSession(int session_id, const std::string& target_id) override;
151
  //   kSendMessage
152
  void MessageReceived(int session_id, const std::string& message) override;
153
  //   kEndSession
154
  void EndSession(int session_id) override;
155
156
  std::vector<std::string> GetTargetIds() override;
157
  std::string GetTargetTitle(const std::string& id) override;
158
  std::string GetTargetUrl(const std::string& id) override;
159
57
  bool IsConnected() { return connected_; }
160
13
  void ServerDone() override {
161
13
    io_->ServerDone();
162
13
  }
163
164
 private:
165
  InspectorIo* io_;
166
  bool connected_;
167
  int session_id_;
168
  const std::string script_name_;
169
  const std::string script_path_;
170
  const std::string target_id_;
171
  bool waiting_;
172
};
173
174
52
void InterruptCallback(v8::Isolate*, void* agent) {
175
52
  InspectorIo* io = static_cast<Agent*>(agent)->io();
176
52
  if (io != nullptr)
177
51
    io->DispatchMessages();
178
52
}
179
180
116
class DispatchMessagesTask : public v8::Task {
181
 public:
182
60
  explicit DispatchMessagesTask(Agent* agent) : agent_(agent) {}
183
184
58
  void Run() override {
185
58
    InspectorIo* io = agent_->io();
186
58
    if (io != nullptr)
187
58
      io->DispatchMessages();
188
58
  }
189
190
 private:
191
  Agent* agent_;
192
};
193
194
67
InspectorIo::InspectorIo(Environment* env, v8::Platform* platform,
195
                         const std::string& path, const DebugOptions& options,
196
                         bool wait_for_connect)
197
                         : options_(options), thread_(), delegate_(nullptr),
198
                           state_(State::kNew), parent_env_(env),
199
                           thread_req_(), platform_(platform),
200
                           dispatching_messages_(false), session_id_(0),
201
                           script_name_(path),
202
67
                           wait_for_connect_(wait_for_connect), port_(-1) {
203
67
  main_thread_req_ = new AsyncAndAgent({uv_async_t(), env->inspector_agent()});
204
67
  CHECK_EQ(0, uv_async_init(env->event_loop(), &main_thread_req_->first,
205
                            InspectorIo::MainThreadReqAsyncCb));
206
67
  uv_unref(reinterpret_cast<uv_handle_t*>(&main_thread_req_->first));
207
67
  CHECK_EQ(0, uv_sem_init(&thread_start_sem_, 0));
208
67
}
209
210
70
InspectorIo::~InspectorIo() {
211
35
  uv_sem_destroy(&thread_start_sem_);
212
  uv_close(reinterpret_cast<uv_handle_t*>(&main_thread_req_->first),
213
35
           ReleasePairOnAsyncClose);
214
35
}
215
216
67
bool InspectorIo::Start() {
217
67
  CHECK_EQ(state_, State::kNew);
218
67
  CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain, this), 0);
219
67
  uv_sem_wait(&thread_start_sem_);
220
221
67
  if (state_ == State::kError) {
222
2
    return false;
223
  }
224
65
  state_ = State::kAccepting;
225
65
  if (wait_for_connect_) {
226
9
    DispatchMessages();
227
  }
228
65
  return true;
229
}
230
231
4
void InspectorIo::Stop() {
232

4
  CHECK(state_ == State::kAccepting || state_ == State::kConnected);
233
4
  Write(TransportAction::kKill, 0, StringView());
234
4
  int err = uv_thread_join(&thread_);
235
4
  CHECK_EQ(err, 0);
236
4
  state_ = State::kShutDown;
237
4
  DispatchMessages();
238
4
}
239
240
57
bool InspectorIo::IsConnected() {
241

57
  return delegate_ != nullptr && delegate_->IsConnected();
242
}
243
244
bool InspectorIo::IsStarted() {
245
  return platform_ != nullptr;
246
}
247
248
12
void InspectorIo::WaitForDisconnect() {
249
12
  if (state_ == State::kAccepting)
250
3
    state_ = State::kDone;
251
12
  if (state_ == State::kConnected) {
252
9
    state_ = State::kShutDown;
253
9
    Write(TransportAction::kStop, 0, StringView());
254
9
    fprintf(stderr, "Waiting for the debugger to disconnect...\n");
255
9
    fflush(stderr);
256
9
    parent_env_->inspector_agent()->RunMessageLoop();
257
  }
258
12
}
259
260
// static
261
67
void InspectorIo::ThreadMain(void* io) {
262
67
  static_cast<InspectorIo*>(io)->ThreadMain<InspectorSocketServer>();
263
15
}
264
265
// static
266
template <typename Transport>
267
1028
void InspectorIo::IoThreadAsyncCb(uv_async_t* async) {
268
  TransportAndIo<Transport>* transport_and_io =
269
1028
      static_cast<TransportAndIo<Transport>*>(async->data);
270
1028
  if (transport_and_io == nullptr) {
271
1028
    return;
272
  }
273
1028
  Transport* transport = transport_and_io->first;
274
1028
  InspectorIo* io = transport_and_io->second;
275
1028
  MessageQueue<TransportAction> outgoing_message_queue;
276
1028
  io->SwapBehindLock(&io->outgoing_message_queue_, &outgoing_message_queue);
277
2085
  for (const auto& outgoing : outgoing_message_queue) {
278

1057
    switch (std::get<0>(outgoing)) {
279
    case TransportAction::kKill:
280
4
      transport->TerminateConnections();
281
      // Fallthrough
282
    case TransportAction::kStop:
283
13
      transport->Stop(nullptr);
284
13
      break;
285
    case TransportAction::kSendMessage:
286
1044
      std::string message = StringViewToUtf8(std::get<2>(outgoing)->string());
287
1044
      transport->Send(std::get<1>(outgoing), message);
288
1044
      break;
289
    }
290
1028
  }
291
}
292
293
template<typename Transport>
294
67
void InspectorIo::ThreadMain() {
295
  uv_loop_t loop;
296
67
  loop.data = nullptr;
297
67
  int err = uv_loop_init(&loop);
298
67
  CHECK_EQ(err, 0);
299
67
  thread_req_.data = nullptr;
300
67
  err = uv_async_init(&loop, &thread_req_, IoThreadAsyncCb<Transport>);
301
67
  CHECK_EQ(err, 0);
302
67
  std::string script_path = ScriptPath(&loop, script_name_);
303
  InspectorIoDelegate delegate(this, script_path, script_name_,
304
80
                               wait_for_connect_);
305
67
  delegate_ = &delegate;
306
80
  Transport server(&delegate, &loop, options_.host_name(), options_.port());
307
67
  TransportAndIo<Transport> queue_transport(&server, this);
308
67
  thread_req_.data = &queue_transport;
309
67
  if (!server.Start()) {
310
2
    state_ = State::kError;  // Safe, main thread is waiting on semaphore
311
2
    CHECK_EQ(0, CloseAsyncAndLoop(&thread_req_));
312
2
    uv_sem_post(&thread_start_sem_);
313
17
    return;
314
  }
315
65
  port_ = server.Port();  // Safe, main thread is waiting on semaphore.
316
65
  if (!wait_for_connect_) {
317
56
    uv_sem_post(&thread_start_sem_);
318
  }
319
65
  uv_run(&loop, UV_RUN_DEFAULT);
320
13
  thread_req_.data = nullptr;
321
13
  CHECK_EQ(uv_loop_close(&loop), 0);
322
26
  delegate_ = nullptr;
323
}
324
325
template <typename ActionType>
326
1164
bool InspectorIo::AppendMessage(MessageQueue<ActionType>* queue,
327
                                ActionType action, int session_id,
328
                                std::unique_ptr<StringBuffer> buffer) {
329
1164
  Mutex::ScopedLock scoped_lock(state_lock_);
330
1164
  bool trigger_pumping = queue->empty();
331
1164
  queue->push_back(std::make_tuple(action, session_id, std::move(buffer)));
332
1164
  return trigger_pumping;
333
}
334
335
template <typename ActionType>
336
1212
void InspectorIo::SwapBehindLock(MessageQueue<ActionType>* vector1,
337
                                 MessageQueue<ActionType>* vector2) {
338
1212
  Mutex::ScopedLock scoped_lock(state_lock_);
339
1212
  vector1->swap(*vector2);
340
1212
}
341
342
107
void InspectorIo::PostIncomingMessage(InspectorAction action, int session_id,
343
                                      const std::string& message) {
344
214
  if (AppendMessage(&incoming_message_queue_, action, session_id,
345
214
                    Utf8ToStringView(message))) {
346
60
    Agent* agent = main_thread_req_->second;
347
60
    v8::Isolate* isolate = parent_env_->isolate();
348
    platform_->CallOnForegroundThread(isolate,
349
60
                                      new DispatchMessagesTask(agent));
350
60
    isolate->RequestInterrupt(InterruptCallback, agent);
351
60
    CHECK_EQ(0, uv_async_send(&main_thread_req_->first));
352
  }
353
107
  NotifyMessageReceived();
354
107
}
355
356
3
std::vector<std::string> InspectorIo::GetTargetIds() const {
357
3
  return delegate_ ? delegate_->GetTargetIds() : std::vector<std::string>();
358
}
359
360
45
void InspectorIo::WaitForFrontendMessageWhilePaused() {
361
45
  dispatching_messages_ = false;
362
45
  Mutex::ScopedLock scoped_lock(state_lock_);
363
45
  if (incoming_message_queue_.empty())
364
45
    incoming_message_cond_.Wait(scoped_lock);
365
45
}
366
367
107
void InspectorIo::NotifyMessageReceived() {
368
107
  Mutex::ScopedLock scoped_lock(state_lock_);
369
107
  incoming_message_cond_.Broadcast(scoped_lock);
370
107
}
371
372
126
void InspectorIo::DispatchMessages() {
373
  // This function can be reentered if there was an incoming message while
374
  // V8 was processing another inspector request (e.g. if the user is
375
  // evaluating a long-running JS code snippet). This can happen only at
376
  // specific points (e.g. the lines that call inspector_ methods)
377
126
  if (dispatching_messages_)
378
128
    return;
379
124
  dispatching_messages_ = true;
380
124
  bool had_messages = false;
381
184
  do {
382
184
    if (dispatching_message_queue_.empty())
383
184
      SwapBehindLock(&incoming_message_queue_, &dispatching_message_queue_);
384
184
    had_messages = !dispatching_message_queue_.empty();
385
475
    while (!dispatching_message_queue_.empty()) {
386
107
      MessageQueue<InspectorAction>::value_type task;
387
107
      std::swap(dispatching_message_queue_.front(), task);
388
107
      dispatching_message_queue_.pop_front();
389
107
      StringView message = std::get<2>(task)->string();
390

107
      switch (std::get<0>(task)) {
391
      case InspectorAction::kStartSession:
392
10
        CHECK_EQ(session_delegate_, nullptr);
393
10
        session_id_ = std::get<1>(task);
394
10
        state_ = State::kConnected;
395
10
        fprintf(stderr, "Debugger attached.\n");
396
30
        session_delegate_ = std::unique_ptr<InspectorSessionDelegate>(
397
20
            new IoSessionDelegate(this));
398
10
        parent_env_->inspector_agent()->Connect(session_delegate_.get());
399
10
        break;
400
      case InspectorAction::kEndSession:
401
10
        CHECK_NE(session_delegate_, nullptr);
402
10
        if (state_ == State::kShutDown) {
403
10
          state_ = State::kDone;
404
        } else {
405
          state_ = State::kAccepting;
406
        }
407
10
        parent_env_->inspector_agent()->Disconnect();
408
10
        session_delegate_.reset();
409
10
        break;
410
      case InspectorAction::kSendMessage:
411
87
        parent_env_->inspector_agent()->Dispatch(message);
412
87
        break;
413
      }
414
107
    }
415
  } while (had_messages);
416
124
  dispatching_messages_ = false;
417
}
418
419
// static
420
4
void InspectorIo::MainThreadReqAsyncCb(uv_async_t* req) {
421
4
  AsyncAndAgent* pair = node::ContainerOf(&AsyncAndAgent::first, req);
422
  // Note that this may be called after io was closed or even after a new
423
  // one was created and ran.
424
4
  InspectorIo* io = pair->second->io();
425
4
  if (io != nullptr)
426
4
    io->DispatchMessages();
427
4
}
428
429
1057
void InspectorIo::Write(TransportAction action, int session_id,
430
                        const StringView& inspector_message) {
431
  AppendMessage(&outgoing_message_queue_, action, session_id,
432
1057
                StringBuffer::create(inspector_message));
433
1057
  int err = uv_async_send(&thread_req_);
434
1057
  CHECK_EQ(0, err);
435
1057
}
436
437
67
InspectorIoDelegate::InspectorIoDelegate(InspectorIo* io,
438
                                         const std::string& script_path,
439
                                         const std::string& script_name,
440
                                         bool wait)
441
                                         : io_(io),
442
                                           connected_(false),
443
                                           session_id_(0),
444
                                           script_name_(script_name),
445
                                           script_path_(script_path),
446
                                           target_id_(GenerateID()),
447
67
                                           waiting_(wait) { }
448
449
450
10
bool InspectorIoDelegate::StartSession(int session_id,
451
                                       const std::string& target_id) {
452
10
  if (connected_)
453
    return false;
454
10
  connected_ = true;
455
10
  session_id_++;
456
10
  io_->PostIncomingMessage(InspectorAction::kStartSession, session_id, "");
457
10
  return true;
458
}
459
460
87
void InspectorIoDelegate::MessageReceived(int session_id,
461
                                          const std::string& message) {
462
  // TODO(pfeldman): Instead of blocking execution while debugger
463
  // engages, node should wait for the run callback from the remote client
464
  // and initiate its startup. This is a change to node.cc that should be
465
  // upstreamed separately.
466
87
  if (waiting_) {
467
43
    if (message.find("\"Runtime.runIfWaitingForDebugger\"") !=
468
        std::string::npos) {
469
9
      waiting_ = false;
470
9
      io_->ResumeStartup();
471
    }
472
  }
473
  io_->PostIncomingMessage(InspectorAction::kSendMessage, session_id,
474
87
                           message);
475
87
}
476
477
10
void InspectorIoDelegate::EndSession(int session_id) {
478
10
  connected_ = false;
479
10
  io_->PostIncomingMessage(InspectorAction::kEndSession, session_id, "");
480
10
}
481
482
93
std::vector<std::string> InspectorIoDelegate::GetTargetIds() {
483
93
  return { target_id_ };
484
}
485
486
13
std::string InspectorIoDelegate::GetTargetTitle(const std::string& id) {
487
13
  return script_name_.empty() ? GetProcessTitle() : script_name_;
488
}
489
490
13
std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) {
491
13
  return "file://" + script_path_;
492
}
493
494
45
bool IoSessionDelegate::WaitForFrontendMessageWhilePaused() {
495
45
  io_->WaitForFrontendMessageWhilePaused();
496
45
  return true;
497
}
498
499
1044
void IoSessionDelegate::SendMessageToFrontend(
500
    const v8_inspector::StringView& message) {
501
1044
  io_->Write(TransportAction::kSendMessage, io_->session_id_, message);
502
1044
}
503
504
}  // namespace inspector
505
}  // namespace node