GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/inspector_io.cc Lines: 166 166 100.0 %
Date: 2019-02-23 22:23:05 Branches: 36 50 72.0 %

Line Branch Exec Source
1
#include "inspector_io.h"
2
3
#include "inspector_socket_server.h"
4
#include "inspector/main_thread_interface.h"
5
#include "inspector/node_string.h"
6
#include "env-inl.h"
7
#include "debug_utils.h"
8
#include "node.h"
9
#include "node_crypto.h"
10
#include "node_mutex.h"
11
#include "v8-inspector.h"
12
#include "util.h"
13
#include "zlib.h"
14
15
#include <deque>
16
#include <string.h>
17
#include <vector>
18
19
namespace node {
20
namespace inspector {
21
namespace {
22
using v8_inspector::StringBuffer;
23
using v8_inspector::StringView;
24
25
74
std::string ScriptPath(uv_loop_t* loop, const std::string& script_name) {
26
74
  std::string script_path;
27
28
74
  if (!script_name.empty()) {
29
    uv_fs_t req;
30
59
    req.ptr = nullptr;
31
59
    if (0 == uv_fs_realpath(loop, &req, script_name.c_str(), nullptr)) {
32
58
      CHECK_NOT_NULL(req.ptr);
33
58
      script_path = std::string(static_cast<char*>(req.ptr));
34
    }
35
59
    uv_fs_req_cleanup(&req);
36
  }
37
38
74
  return script_path;
39
}
40
41
// UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
42
// Used ver 4 - with numbers
43
74
std::string GenerateID() {
44
  uint16_t buffer[8];
45
74
  CHECK(crypto::EntropySource(reinterpret_cast<unsigned char*>(buffer),
46
                              sizeof(buffer)));
47
48
  char uuid[256];
49
  snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
50
74
           buffer[0],  // time_low
51
74
           buffer[1],  // time_mid
52
74
           buffer[2],  // time_low
53
74
           (buffer[3] & 0x0fff) | 0x4000,  // time_hi_and_version
54
74
           (buffer[4] & 0x3fff) | 0x8000,  // clk_seq_hi clk_seq_low
55
74
           buffer[5],  // node
56
74
           buffer[6],
57
592
           buffer[7]);
58
74
  return uuid;
59
}
60
61
975
class RequestToServer {
62
 public:
63
975
  RequestToServer(TransportAction action,
64
                  int session_id,
65
                  std::unique_ptr<v8_inspector::StringBuffer> message)
66
                  : action_(action),
67
                    session_id_(session_id),
68
975
                    message_(std::move(message)) {}
69
70
975
  void Dispatch(InspectorSocketServer* server) const {
71

975
    switch (action_) {
72
      case TransportAction::kKill:
73
4
        server->TerminateConnections();
74
        // Fallthrough
75
      case TransportAction::kStop:
76
73
        server->Stop();
77
73
        break;
78
      case TransportAction::kSendMessage:
79
        server->Send(
80
            session_id_,
81
902
            protocol::StringUtil::StringViewToUtf8(message_->string()));
82
902
        break;
83
    }
84
975
  }
85
86
 private:
87
  TransportAction action_;
88
  int session_id_;
89
  std::unique_ptr<v8_inspector::StringBuffer> message_;
90
};
91
92
class RequestQueueData {
93
 public:
94
  using MessageQueue = std::deque<RequestToServer>;
95
96
74
  explicit RequestQueueData(uv_loop_t* loop)
97
74
                            : handle_(std::make_shared<RequestQueue>(this)) {
98
1962
    int err = uv_async_init(loop, &async_, [](uv_async_t* async) {
99
      RequestQueueData* wrapper =
100
944
          node::ContainerOf(&RequestQueueData::async_, async);
101
944
      wrapper->DoDispatch();
102
2036
    });
103
74
    CHECK_EQ(0, err);
104
74
  }
105
106
  static void CloseAndFree(RequestQueueData* queue);
107
108
975
  void Post(int session_id,
109
            TransportAction action,
110
            std::unique_ptr<StringBuffer> message) {
111
975
    Mutex::ScopedLock scoped_lock(state_lock_);
112
975
    bool notify = messages_.empty();
113
975
    messages_.emplace_back(action, session_id, std::move(message));
114
975
    if (notify) {
115
944
      CHECK_EQ(0, uv_async_send(&async_));
116
944
      incoming_message_cond_.Broadcast(scoped_lock);
117
975
    }
118
975
  }
119
120
  void Wait() {
121
    Mutex::ScopedLock scoped_lock(state_lock_);
122
    if (messages_.empty()) {
123
      incoming_message_cond_.Wait(scoped_lock);
124
    }
125
  }
126
127
74
  void SetServer(InspectorSocketServer* server) {
128
74
    server_ = server;
129
74
  }
130
131
87
  std::shared_ptr<RequestQueue> handle() {
132
87
    return handle_;
133
  }
134
135
 private:
136
74
  ~RequestQueueData() = default;
137
138
944
  MessageQueue GetMessages() {
139
944
    Mutex::ScopedLock scoped_lock(state_lock_);
140
944
    MessageQueue messages;
141
944
    messages_.swap(messages);
142
944
    return messages;
143
  }
144
145
944
  void DoDispatch() {
146
944
    if (server_ == nullptr)
147
944
      return;
148
1919
    for (const auto& request : GetMessages()) {
149
975
      request.Dispatch(server_);
150
944
    }
151
  }
152
153
  std::shared_ptr<RequestQueue> handle_;
154
  uv_async_t async_;
155
  InspectorSocketServer* server_ = nullptr;
156
  MessageQueue messages_;
157
  Mutex state_lock_;  // Locked before mutating the queue.
158
  ConditionVariable incoming_message_cond_;
159
};
160
}  // namespace
161
162
41
class RequestQueue {
163
 public:
164
74
  explicit RequestQueue(RequestQueueData* data) : data_(data) {}
165
166
74
  void Reset() {
167
74
    Mutex::ScopedLock scoped_lock(lock_);
168
74
    data_ = nullptr;
169
74
  }
170
171
1012
  void Post(int session_id,
172
            TransportAction action,
173
            std::unique_ptr<StringBuffer> message) {
174
1012
    Mutex::ScopedLock scoped_lock(lock_);
175
1012
    if (data_ != nullptr)
176
975
      data_->Post(session_id, action, std::move(message));
177
1012
  }
178
179
  void SetServer(InspectorSocketServer* server) {
180
    Mutex::ScopedLock scoped_lock(lock_);
181
    if (data_ != nullptr)
182
      data_->SetServer(server);
183
  }
184
185
74
  bool Expired() {
186
74
    Mutex::ScopedLock scoped_lock(lock_);
187
74
    return data_ == nullptr;
188
  }
189
190
 private:
191
  RequestQueueData* data_;
192
  Mutex lock_;
193
};
194
195
26
class IoSessionDelegate : public InspectorSessionDelegate {
196
 public:
197
13
  explicit IoSessionDelegate(std::shared_ptr<RequestQueue> queue, int id)
198
13
                             : request_queue_(queue), id_(id) { }
199
902
  void SendMessageToFrontend(const v8_inspector::StringView& message) override {
200
    request_queue_->Post(id_, TransportAction::kSendMessage,
201
902
                         StringBuffer::create(message));
202
902
  }
203
204
 private:
205
  std::shared_ptr<RequestQueue> request_queue_;
206
  int id_;
207
};
208
209
// Passed to InspectorSocketServer to handle WS inspector protocol events,
210
// mostly session start, message received, and session end.
211
class InspectorIoDelegate: public node::inspector::SocketServerDelegate {
212
 public:
213
  InspectorIoDelegate(std::shared_ptr<RequestQueueData> queue,
214
                      std::shared_ptr<MainThreadHandle> main_threade,
215
                      const std::string& target_id,
216
                      const std::string& script_path,
217
                      const std::string& script_name);
218
148
  ~InspectorIoDelegate() override {
219
148
  }
220
221
  void StartSession(int session_id, const std::string& target_id) override;
222
  void MessageReceived(int session_id, const std::string& message) override;
223
  void EndSession(int session_id) override;
224
225
  std::vector<std::string> GetTargetIds() override;
226
  std::string GetTargetTitle(const std::string& id) override;
227
  std::string GetTargetUrl(const std::string& id) override;
228
74
  void AssignServer(InspectorSocketServer* server) override {
229
74
    request_queue_->SetServer(server);
230
74
  }
231
232
 private:
233
  std::shared_ptr<RequestQueueData> request_queue_;
234
  std::shared_ptr<MainThreadHandle> main_thread_;
235
  std::unordered_map<int, std::unique_ptr<InspectorSession>> sessions_;
236
  const std::string script_name_;
237
  const std::string script_path_;
238
  const std::string target_id_;
239
};
240
241
// static
242
74
std::unique_ptr<InspectorIo> InspectorIo::Start(
243
    std::shared_ptr<MainThreadHandle> main_thread,
244
    const std::string& path,
245
    std::shared_ptr<HostPort> host_port) {
246
  auto io = std::unique_ptr<InspectorIo>(
247
74
      new InspectorIo(main_thread, path, host_port));
248
74
  if (io->request_queue_->Expired()) {  // Thread is not running
249
1
    return nullptr;
250
  }
251
73
  return io;
252
}
253
254
74
InspectorIo::InspectorIo(std::shared_ptr<MainThreadHandle> main_thread,
255
                         const std::string& path,
256
                         std::shared_ptr<HostPort> host_port)
257
    : main_thread_(main_thread),
258
      host_port_(host_port),
259
      thread_(),
260
      script_name_(path),
261
74
      id_(GenerateID()) {
262
74
  Mutex::ScopedLock scoped_lock(thread_start_lock_);
263
74
  CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain, this), 0);
264
74
  thread_start_condition_.Wait(scoped_lock);
265
74
}
266
267
82
InspectorIo::~InspectorIo() {
268
41
  request_queue_->Post(0, TransportAction::kKill, nullptr);
269
41
  int err = uv_thread_join(&thread_);
270
41
  CHECK_EQ(err, 0);
271
41
}
272
273
69
void InspectorIo::StopAcceptingNewConnections() {
274
69
  request_queue_->Post(0, TransportAction::kStop, nullptr);
275
69
}
276
277
// static
278
74
void InspectorIo::ThreadMain(void* io) {
279
74
  static_cast<InspectorIo*>(io)->ThreadMain();
280
74
}
281
282
74
void InspectorIo::ThreadMain() {
283
  uv_loop_t loop;
284
74
  loop.data = nullptr;
285
74
  int err = uv_loop_init(&loop);
286
74
  CHECK_EQ(err, 0);
287
74
  std::shared_ptr<RequestQueueData> queue(new RequestQueueData(&loop),
288
148
                                          RequestQueueData::CloseAndFree);
289
148
  std::string script_path = ScriptPath(&loop, script_name_);
290
  std::unique_ptr<InspectorIoDelegate> delegate(
291
      new InspectorIoDelegate(queue, main_thread_, id_,
292
148
                              script_path, script_name_));
293
74
  InspectorSocketServer server(std::move(delegate),
294
                               &loop,
295
74
                               host_port_->host().c_str(),
296
296
                               host_port_->port());
297
74
  request_queue_ = queue->handle();
298
  // Its lifetime is now that of the server delegate
299
74
  queue.reset();
300
  {
301
74
    Mutex::ScopedLock scoped_lock(thread_start_lock_);
302
74
    if (server.Start()) {
303
73
      host_port_->set_port(server.Port());
304
    }
305
74
    thread_start_condition_.Broadcast(scoped_lock);
306
  }
307
74
  uv_run(&loop, UV_RUN_DEFAULT);
308
148
  CheckedUvLoopClose(&loop);
309
74
}
310
311
3
std::vector<std::string> InspectorIo::GetTargetIds() const {
312
3
  return { id_ };
313
}
314
315
74
InspectorIoDelegate::InspectorIoDelegate(
316
    std::shared_ptr<RequestQueueData> queue,
317
    std::shared_ptr<MainThreadHandle> main_thread,
318
    const std::string& target_id,
319
    const std::string& script_path,
320
    const std::string& script_name)
321
    : request_queue_(queue), main_thread_(main_thread),
322
      script_name_(script_name), script_path_(script_path),
323
74
      target_id_(target_id) {}
324
325
13
void InspectorIoDelegate::StartSession(int session_id,
326
                                       const std::string& target_id) {
327
  auto session = main_thread_->Connect(
328
      std::unique_ptr<InspectorSessionDelegate>(
329
13
          new IoSessionDelegate(request_queue_->handle(), session_id)), true);
330
13
  if (session) {
331
13
    sessions_[session_id] = std::move(session);
332
13
    fprintf(stderr, "Debugger attached.\n");
333
13
  }
334
13
}
335
336
113
void InspectorIoDelegate::MessageReceived(int session_id,
337
                                          const std::string& message) {
338
113
  auto session = sessions_.find(session_id);
339
113
  if (session != sessions_.end())
340
113
    session->second->Dispatch(Utf8ToStringView(message)->string());
341
113
}
342
343
13
void InspectorIoDelegate::EndSession(int session_id) {
344
13
  sessions_.erase(session_id);
345
13
}
346
347
102
std::vector<std::string> InspectorIoDelegate::GetTargetIds() {
348
102
  return { target_id_ };
349
}
350
351
15
std::string InspectorIoDelegate::GetTargetTitle(const std::string& id) {
352
15
  return script_name_.empty() ? GetHumanReadableProcessName() : script_name_;
353
}
354
355
15
std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) {
356
15
  return "file://" + script_path_;
357
}
358
359
// static
360
74
void RequestQueueData::CloseAndFree(RequestQueueData* queue) {
361
74
  queue->handle_->Reset();
362
74
  queue->handle_.reset();
363
  uv_close(reinterpret_cast<uv_handle_t*>(&queue->async_),
364
222
           [](uv_handle_t* handle) {
365
74
    uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
366
    RequestQueueData* wrapper =
367
74
        node::ContainerOf(&RequestQueueData::async_, async);
368
74
    delete wrapper;
369
296
  });
370
74
}
371
}  // namespace inspector
372
}  // namespace node