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: 257 278 92.4 %
Date: 2017-06-14 Branches: 79 120 65.8 %

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

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

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

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

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