GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: inspector_io.cc Lines: 178 179 99.4 %
Date: 2022-05-20 04:15:46 Branches: 32 44 72.7 %

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

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