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: 166 166 100.0 %
Date: 2019-01-07 12:15:22 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
75
std::string ScriptPath(uv_loop_t* loop, const std::string& script_name) {
26
75
  std::string script_path;
27
28
75
  if (!script_name.empty()) {
29
    uv_fs_t req;
30
60
    req.ptr = nullptr;
31
60
    if (0 == uv_fs_realpath(loop, &req, script_name.c_str(), nullptr)) {
32
59
      CHECK_NOT_NULL(req.ptr);
33
59
      script_path = std::string(static_cast<char*>(req.ptr));
34
    }
35
60
    uv_fs_req_cleanup(&req);
36
  }
37
38
75
  return script_path;
39
}
40
41
// UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
42
// Used ver 4 - with numbers
43
75
std::string GenerateID() {
44
  uint16_t buffer[8];
45
75
  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
75
           buffer[0],  // time_low
51
75
           buffer[1],  // time_mid
52
75
           buffer[2],  // time_low
53
75
           (buffer[3] & 0x0fff) | 0x4000,  // time_hi_and_version
54
75
           (buffer[4] & 0x3fff) | 0x8000,  // clk_seq_hi clk_seq_low
55
75
           buffer[5],  // node
56
75
           buffer[6],
57
600
           buffer[7]);
58
75
  return uuid;
59
}
60
61
1529
class RequestToServer {
62
 public:
63
1529
  RequestToServer(TransportAction action,
64
                  int session_id,
65
                  std::unique_ptr<v8_inspector::StringBuffer> message)
66
                  : action_(action),
67
                    session_id_(session_id),
68
1529
                    message_(std::move(message)) {}
69
70
1529
  void Dispatch(InspectorSocketServer* server) const {
71

1529
    switch (action_) {
72
      case TransportAction::kKill:
73
4
        server->TerminateConnections();
74
        // Fallthrough
75
      case TransportAction::kStop:
76
74
        server->Stop();
77
74
        break;
78
      case TransportAction::kSendMessage:
79
        server->Send(
80
            session_id_,
81
1455
            protocol::StringUtil::StringViewToUtf8(message_->string()));
82
1455
        break;
83
    }
84
1529
  }
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
75
  explicit RequestQueueData(uv_loop_t* loop)
97
75
                            : handle_(std::make_shared<RequestQueue>(this)) {
98
3047
    int err = uv_async_init(loop, &async_, [](uv_async_t* async) {
99
      RequestQueueData* wrapper =
100
1486
          node::ContainerOf(&RequestQueueData::async_, async);
101
1486
      wrapper->DoDispatch();
102
3122
    });
103
75
    CHECK_EQ(0, err);
104
75
  }
105
106
  static void CloseAndFree(RequestQueueData* queue);
107
108
1529
  void Post(int session_id,
109
            TransportAction action,
110
            std::unique_ptr<StringBuffer> message) {
111
1529
    Mutex::ScopedLock scoped_lock(state_lock_);
112
1529
    bool notify = messages_.empty();
113
1529
    messages_.emplace_back(action, session_id, std::move(message));
114
1529
    if (notify) {
115
1486
      CHECK_EQ(0, uv_async_send(&async_));
116
1486
      incoming_message_cond_.Broadcast(scoped_lock);
117
1529
    }
118
1529
  }
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
75
  void SetServer(InspectorSocketServer* server) {
128
75
    server_ = server;
129
75
  }
130
131
89
  std::shared_ptr<RequestQueue> handle() {
132
89
    return handle_;
133
  }
134
135
 private:
136
75
  ~RequestQueueData() = default;
137
138
1486
  MessageQueue GetMessages() {
139
1486
    Mutex::ScopedLock scoped_lock(state_lock_);
140
1486
    MessageQueue messages;
141
1486
    messages_.swap(messages);
142
1486
    return messages;
143
  }
144
145
1486
  void DoDispatch() {
146
1486
    if (server_ == nullptr)
147
1486
      return;
148
3015
    for (const auto& request : GetMessages()) {
149
1529
      request.Dispatch(server_);
150
1486
    }
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
75
  explicit RequestQueue(RequestQueueData* data) : data_(data) {}
165
166
75
  void Reset() {
167
75
    Mutex::ScopedLock scoped_lock(lock_);
168
75
    data_ = nullptr;
169
75
  }
170
171
1566
  void Post(int session_id,
172
            TransportAction action,
173
            std::unique_ptr<StringBuffer> message) {
174
1566
    Mutex::ScopedLock scoped_lock(lock_);
175
1566
    if (data_ != nullptr)
176
1529
      data_->Post(session_id, action, std::move(message));
177
1566
  }
178
179
  void SetServer(InspectorSocketServer* server) {
180
    Mutex::ScopedLock scoped_lock(lock_);
181
    if (data_ != nullptr)
182
      data_->SetServer(server);
183
  }
184
185
75
  bool Expired() {
186
75
    Mutex::ScopedLock scoped_lock(lock_);
187
75
    return data_ == nullptr;
188
  }
189
190
 private:
191
  RequestQueueData* data_;
192
  Mutex lock_;
193
};
194
195
28
class IoSessionDelegate : public InspectorSessionDelegate {
196
 public:
197
14
  explicit IoSessionDelegate(std::shared_ptr<RequestQueue> queue, int id)
198
14
                             : request_queue_(queue), id_(id) { }
199
1455
  void SendMessageToFrontend(const v8_inspector::StringView& message) override {
200
    request_queue_->Post(id_, TransportAction::kSendMessage,
201
1455
                         StringBuffer::create(message));
202
1455
  }
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
150
  ~InspectorIoDelegate() {
219
150
  }
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
75
  void AssignServer(InspectorSocketServer* server) override {
229
75
    request_queue_->SetServer(server);
230
75
  }
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
75
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
75
      new InspectorIo(main_thread, path, host_port));
248
75
  if (io->request_queue_->Expired()) {  // Thread is not running
249
1
    return nullptr;
250
  }
251
74
  return io;
252
}
253
254
75
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
75
      id_(GenerateID()) {
262
75
  Mutex::ScopedLock scoped_lock(thread_start_lock_);
263
75
  CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain, this), 0);
264
75
  thread_start_condition_.Wait(scoped_lock);
265
75
}
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
70
void InspectorIo::StopAcceptingNewConnections() {
274
70
  request_queue_->Post(0, TransportAction::kStop, nullptr);
275
70
}
276
277
// static
278
75
void InspectorIo::ThreadMain(void* io) {
279
75
  static_cast<InspectorIo*>(io)->ThreadMain();
280
75
}
281
282
75
void InspectorIo::ThreadMain() {
283
  uv_loop_t loop;
284
75
  loop.data = nullptr;
285
75
  int err = uv_loop_init(&loop);
286
75
  CHECK_EQ(err, 0);
287
75
  std::shared_ptr<RequestQueueData> queue(new RequestQueueData(&loop),
288
150
                                          RequestQueueData::CloseAndFree);
289
150
  std::string script_path = ScriptPath(&loop, script_name_);
290
  std::unique_ptr<InspectorIoDelegate> delegate(
291
      new InspectorIoDelegate(queue, main_thread_, id_,
292
150
                              script_path, script_name_));
293
75
  InspectorSocketServer server(std::move(delegate),
294
                               &loop,
295
75
                               host_port_->host().c_str(),
296
300
                               host_port_->port());
297
75
  request_queue_ = queue->handle();
298
  // Its lifetime is now that of the server delegate
299
75
  queue.reset();
300
  {
301
75
    Mutex::ScopedLock scoped_lock(thread_start_lock_);
302
75
    if (server.Start()) {
303
74
      host_port_->set_port(server.Port());
304
    }
305
75
    thread_start_condition_.Broadcast(scoped_lock);
306
  }
307
75
  uv_run(&loop, UV_RUN_DEFAULT);
308
150
  CheckedUvLoopClose(&loop);
309
75
}
310
311
3
std::vector<std::string> InspectorIo::GetTargetIds() const {
312
3
  return { id_ };
313
}
314
315
75
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
75
      target_id_(target_id) {}
324
325
14
void InspectorIoDelegate::StartSession(int session_id,
326
                                       const std::string& target_id) {
327
  auto session = main_thread_->Connect(
328
      std::unique_ptr<InspectorSessionDelegate>(
329
14
          new IoSessionDelegate(request_queue_->handle(), session_id)), true);
330
14
  if (session) {
331
14
    sessions_[session_id] = std::move(session);
332
14
    fprintf(stderr, "Debugger attached.\n");
333
14
  }
334
14
}
335
336
122
void InspectorIoDelegate::MessageReceived(int session_id,
337
                                          const std::string& message) {
338
122
  auto session = sessions_.find(session_id);
339
122
  if (session != sessions_.end())
340
122
    session->second->Dispatch(Utf8ToStringView(message)->string());
341
122
}
342
343
14
void InspectorIoDelegate::EndSession(int session_id) {
344
14
  sessions_.erase(session_id);
345
14
}
346
347
105
std::vector<std::string> InspectorIoDelegate::GetTargetIds() {
348
105
  return { target_id_ };
349
}
350
351
16
std::string InspectorIoDelegate::GetTargetTitle(const std::string& id) {
352
16
  return script_name_.empty() ? GetHumanReadableProcessName() : script_name_;
353
}
354
355
16
std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) {
356
16
  return "file://" + script_path_;
357
}
358
359
// static
360
75
void RequestQueueData::CloseAndFree(RequestQueueData* queue) {
361
75
  queue->handle_->Reset();
362
75
  queue->handle_.reset();
363
  uv_close(reinterpret_cast<uv_handle_t*>(&queue->async_),
364
225
           [](uv_handle_t* handle) {
365
75
    uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
366
    RequestQueueData* wrapper =
367
75
        node::ContainerOf(&RequestQueueData::async_, async);
368
75
    delete wrapper;
369
300
  });
370
75
}
371
}  // namespace inspector
372
}  // namespace node