GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: inspector_io.cc Lines: 178 179 99.4 %
Date: 2022-12-07 04:23:16 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
115
std::string ScriptPath(uv_loop_t* loop, const std::string& script_name) {
30
115
  std::string script_path;
31
32
115
  if (!script_name.empty()) {
33
    uv_fs_t req;
34
98
    req.ptr = nullptr;
35
98
    if (0 == uv_fs_realpath(loop, &req, script_name.c_str(), nullptr)) {
36
97
      CHECK_NOT_NULL(req.ptr);
37
97
      script_path = std::string(static_cast<char*>(req.ptr));
38
    }
39
98
    uv_fs_req_cleanup(&req);
40
  }
41
42
115
  return script_path;
43
}
44
45
// UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
46
// Used ver 4 - with numbers
47
115
std::string GenerateID() {
48
  uint16_t buffer[8];
49
115
  CHECK(crypto::CSPRNG(buffer, sizeof(buffer)).is_ok());
50
51
  char uuid[256];
52
920
  snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
53
115
           buffer[0],  // time_low
54
115
           buffer[1],  // time_mid
55
115
           buffer[2],  // time_low
56
115
           (buffer[3] & 0x0fff) | 0x4000,  // time_hi_and_version
57
115
           (buffer[4] & 0x3fff) | 0x8000,  // clk_seq_hi clk_seq_low
58
115
           buffer[5],  // node
59
115
           buffer[6],
60
115
           buffer[7]);
61
115
  return uuid;
62
}
63
64
class RequestToServer {
65
 public:
66
2403
  RequestToServer(TransportAction action,
67
                  int session_id,
68
                  std::unique_ptr<v8_inspector::StringBuffer> message)
69
2403
                  : action_(action),
70
                    session_id_(session_id),
71
2403
                    message_(std::move(message)) {}
72
73
2396
  void Dispatch(InspectorSocketServer* server) const {
74

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