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: 176 177 99.4 %
Date: 2020-09-03 22:13:26 Branches: 34 48 70.8 %

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

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

13416
}  // namespace node