1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "js_native_api_v8_inspector.h"
17
18 #include "js_native_api_v8.h"
19 #include "inspector_socket_server.h"
20 #include "inspector/main_thread_interface.h"
21 #include "inspector/node_string.h"
22 #include "inspector/runtime_agent.h"
23 #include "inspector/tracing_agent.h"
24 #include "inspector/worker_agent.h"
25 #include "inspector/worker_inspector.h"
26 #include "crypto/crypto_util.h"
27 #include "node/inspector/protocol/Protocol.h"
28 #include "node_errors.h"
29 #include "node_internals.h"
30 #include "node_mutex.h"
31 #include "node_options-inl.h"
32 #include "node_process-inl.h"
33 #include "node_url.h"
34 #include "permission/permission.h"
35 #include "util-inl.h"
36 #include "v8-inspector.h"
37 #include "v8-platform.h"
38
39 #include "libplatform/libplatform.h"
40
41 #ifdef __POSIX__
42 #include <pthread.h>
43 #include <climits> // PTHREAD_STACK_MIN
44 #endif // __POSIX__
45
46 #include <algorithm>
47 #include <cstring>
48 #include <sstream>
49 #include <unordered_map>
50 #include <vector>
51
52 namespace v8impl {
53
54 namespace {
55 using node::ConditionVariable;
56 using node::Mutex;
57 using node::inspector::Utf8ToStringView;
58 using v8_inspector::StringBuffer;
59 using v8_inspector::StringView;
60
61 class MainThreadInterface;
62
63 class Request {
64 public:
65 virtual void Call(MainThreadInterface*) = 0;
66 virtual ~Request() = default;
67 };
68
69 class Deletable {
70 public:
71 virtual ~Deletable() = default;
72 };
73
74 using MessageQueue = std::deque<std::unique_ptr<Request>>;
75
76 class MainThreadHandle : public std::enable_shared_from_this<MainThreadHandle> {
77 public:
MainThreadHandle(MainThreadInterface * main_thread)78 explicit MainThreadHandle(MainThreadInterface* main_thread)
79 : main_thread_(main_thread) {
80 }
~MainThreadHandle()81 ~MainThreadHandle() {
82 Mutex::ScopedLock scoped_lock(block_lock_);
83 CHECK_NULL(main_thread_); // main_thread_ should have called Reset
84 }
85 std::unique_ptr<InspectorSession> Connect(
86 std::unique_ptr<InspectorSessionDelegate> delegate,
87 bool prevent_shutdown);
newObjectId()88 int newObjectId() {
89 return ++next_object_id_;
90 }
91 bool Post(std::unique_ptr<Request> request);
92
93 private:
94 void Reset();
95
96 MainThreadInterface* main_thread_;
97 Mutex block_lock_;
98 int next_session_id_ = 0;
99 std::atomic_int next_object_id_ = {1};
100
101 friend class MainThreadInterface;
102 };
103
104 class MainThreadInterface :
105 public std::enable_shared_from_this<MainThreadInterface> {
106 public:
107 explicit MainThreadInterface(Agent* agent);
108 ~MainThreadInterface();
109
110 void DispatchMessages();
111 void Post(std::unique_ptr<Request> request);
112 bool WaitForFrontendEvent();
113 std::shared_ptr<MainThreadHandle> GetHandle();
inspector_agent()114 Agent* inspector_agent() {
115 return agent_;
116 }
117 void AddObject(int handle, std::unique_ptr<Deletable> object);
118 Deletable* GetObject(int id);
119 Deletable* GetObjectIfExists(int id);
120 void RemoveObject(int handle);
121
122 private:
123 MessageQueue requests_;
124 Mutex requests_lock_; // requests_ live across threads
125 // This queue is to maintain the order of the messages for the cases
126 // when we reenter the DispatchMessages function.
127 MessageQueue dispatching_message_queue_;
128 bool dispatching_messages_ = false;
129 ConditionVariable incoming_message_cond_;
130 // Used from any thread
131 Agent* const agent_;
132 std::shared_ptr<MainThreadHandle> handle_;
133 std::unordered_map<int, std::unique_ptr<Deletable>> managed_objects_;
134 };
135
136 template <typename T>
137 class DeletableWrapper : public Deletable {
138 public:
DeletableWrapper(std::unique_ptr<T> object)139 explicit DeletableWrapper(std::unique_ptr<T> object)
140 : object_(std::move(object)) {}
141 ~DeletableWrapper() override = default;
142
get(MainThreadInterface * thread,int id)143 static T* get(MainThreadInterface* thread, int id) {
144 return
145 static_cast<DeletableWrapper<T>*>(thread->GetObject(id))->object_.get();
146 }
147
148 private:
149 std::unique_ptr<T> object_;
150 };
151
152 template <typename T>
WrapInDeletable(std::unique_ptr<T> object)153 std::unique_ptr<Deletable> WrapInDeletable(std::unique_ptr<T> object) {
154 return std::unique_ptr<DeletableWrapper<T>>(
155 new DeletableWrapper<T>(std::move(object)));
156 }
157
158 template <typename Factory>
159 class CreateObjectRequest : public Request {
160 public:
CreateObjectRequest(int object_id,Factory factory)161 CreateObjectRequest(int object_id, Factory factory)
162 : object_id_(object_id), factory_(std::move(factory)) {}
163
Call(MainThreadInterface * thread)164 void Call(MainThreadInterface* thread) override {
165 thread->AddObject(object_id_, WrapInDeletable(factory_(thread)));
166 }
167
168 private:
169 int object_id_;
170 Factory factory_;
171 };
172
173 template <typename Factory>
NewCreateRequest(int object_id,Factory factory)174 std::unique_ptr<Request> NewCreateRequest(int object_id, Factory factory) {
175 return std::unique_ptr<Request>(
176 new CreateObjectRequest<Factory>(object_id, std::move(factory)));
177 }
178
179 class DeleteRequest : public Request {
180 public:
DeleteRequest(int object_id)181 explicit DeleteRequest(int object_id) : object_id_(object_id) {}
182
Call(MainThreadInterface * thread)183 void Call(MainThreadInterface* thread) override {
184 thread->RemoveObject(object_id_);
185 }
186
187 private:
188 int object_id_;
189 };
190
191 template <typename Target, typename Fn>
192 class CallRequest : public Request {
193 public:
CallRequest(int id,Fn fn)194 CallRequest(int id, Fn fn) : id_(id), fn_(std::move(fn)) {}
195
Call(MainThreadInterface * thread)196 void Call(MainThreadInterface* thread) override {
197 fn_(DeletableWrapper<Target>::get(thread, id_));
198 }
199
200 private:
201 int id_;
202 Fn fn_;
203 };
204
205 template <typename T>
206 class AnotherThreadObjectReference {
207 public:
AnotherThreadObjectReference(std::shared_ptr<MainThreadHandle> thread,int object_id)208 AnotherThreadObjectReference(
209 std::shared_ptr<MainThreadHandle> thread, int object_id)
210 : thread_(thread), object_id_(object_id) {}
211
212 template <typename Factory>
AnotherThreadObjectReference(std::shared_ptr<MainThreadHandle> thread,Factory factory)213 AnotherThreadObjectReference(
214 std::shared_ptr<MainThreadHandle> thread, Factory factory)
215 : AnotherThreadObjectReference(thread, thread->newObjectId()) {
216 thread_->Post(NewCreateRequest(object_id_, std::move(factory)));
217 }
218 AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete;
219
~AnotherThreadObjectReference()220 ~AnotherThreadObjectReference() {
221 // Disappearing thread may cause a memory leak
222 thread_->Post(std::make_unique<DeleteRequest>(object_id_));
223 }
224
225 template <typename Fn>
Call(Fn fn) const226 void Call(Fn fn) const {
227 using Request = CallRequest<T, Fn>;
228 thread_->Post(std::unique_ptr<Request>(
229 new Request(object_id_, std::move(fn))));
230 }
231
232 template <typename Arg>
Call(void (T::* fn)(Arg),Arg argument) const233 void Call(void (T::*fn)(Arg), Arg argument) const {
234 Call(std::bind(Apply<Arg>, std::placeholders::_1, fn, std::move(argument)));
235 }
236
237 private:
238 // This has to use non-const reference to support std::bind with non-copyable
239 // types
240 template <typename Argument>
Apply(T * target,void (T::* fn)(Argument),Argument & argument)241 static void Apply(T* target, void (T::*fn)(Argument),
242 /* NOLINT (runtime/references) */ Argument& argument) {
243 (target->*fn)(std::move(argument));
244 }
245
246 std::shared_ptr<MainThreadHandle> thread_;
247 const int object_id_;
248 };
249
250 class MainThreadSessionState {
251 public:
MainThreadSessionState(MainThreadInterface * thread,bool prevent_shutdown)252 MainThreadSessionState(MainThreadInterface* thread, bool prevent_shutdown)
253 : thread_(thread),
254 prevent_shutdown_(prevent_shutdown) {}
255
Create(MainThreadInterface * thread,bool prevent_shutdown)256 static std::unique_ptr<MainThreadSessionState> Create(
257 MainThreadInterface* thread, bool prevent_shutdown) {
258 return std::make_unique<MainThreadSessionState>(thread, prevent_shutdown);
259 }
260
Connect(std::unique_ptr<InspectorSessionDelegate> delegate)261 void Connect(std::unique_ptr<InspectorSessionDelegate> delegate) {
262 Agent* agent = thread_->inspector_agent();
263 if (agent != nullptr)
264 session_ = agent->Connect(std::move(delegate), prevent_shutdown_);
265 }
266
Dispatch(std::unique_ptr<StringBuffer> message)267 void Dispatch(std::unique_ptr<StringBuffer> message) {
268 session_->Dispatch(message->string());
269 }
270
271 private:
272 MainThreadInterface* thread_;
273 bool prevent_shutdown_;
274 std::unique_ptr<InspectorSession> session_;
275 };
276
277 class CrossThreadInspectorSession : public InspectorSession {
278 public:
CrossThreadInspectorSession(int id,std::shared_ptr<MainThreadHandle> thread,std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)279 CrossThreadInspectorSession(
280 int id,
281 std::shared_ptr<MainThreadHandle> thread,
282 std::unique_ptr<InspectorSessionDelegate> delegate,
283 bool prevent_shutdown)
284 : state_(thread, std::bind(MainThreadSessionState::Create,
285 std::placeholders::_1,
286 prevent_shutdown)) {
287 state_.Call(&MainThreadSessionState::Connect, std::move(delegate));
288 }
289
Dispatch(const StringView & message)290 void Dispatch(const StringView& message) override {
291 state_.Call(&MainThreadSessionState::Dispatch,
292 StringBuffer::create(message));
293 }
294
295 private:
296 AnotherThreadObjectReference<MainThreadSessionState> state_;
297 };
298
299 class ThreadSafeDelegate : public InspectorSessionDelegate {
300 public:
ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread,int object_id)301 ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread, int object_id)
302 : thread_(thread), delegate_(thread, object_id) {}
303
SendMessageToFrontend(const v8_inspector::StringView & message)304 void SendMessageToFrontend(const v8_inspector::StringView& message) override {
305 delegate_.Call(
306 [m = StringBuffer::create(message)]
307 (InspectorSessionDelegate* delegate) {
308 delegate->SendMessageToFrontend(m->string());
309 });
310 }
311
312 private:
313 std::shared_ptr<MainThreadHandle> thread_;
314 AnotherThreadObjectReference<InspectorSessionDelegate> delegate_;
315 };
316
MainThreadInterface(Agent * agent)317 MainThreadInterface::MainThreadInterface(Agent* agent) : agent_(agent) {}
318
~MainThreadInterface()319 MainThreadInterface::~MainThreadInterface() {
320 if (handle_)
321 handle_->Reset();
322 }
323
Post(std::unique_ptr<Request> request)324 void MainThreadInterface::Post(std::unique_ptr<Request> request) {
325 CHECK_NOT_NULL(agent_);
326 Mutex::ScopedLock scoped_lock(requests_lock_);
327 bool needs_notify = requests_.empty();
328 requests_.push_back(std::move(request));
329 if (needs_notify) {
330 std::weak_ptr<MainThreadInterface> weak_self {shared_from_this()};
331 agent_->env()->RequestInterrupt([weak_self](Environment*) {
332 if (auto iface = weak_self.lock()) iface->DispatchMessages();
333 });
334 }
335 incoming_message_cond_.Broadcast(scoped_lock);
336 }
337
WaitForFrontendEvent()338 bool MainThreadInterface::WaitForFrontendEvent() {
339 // We allow DispatchMessages reentry as we enter the pause. This is important
340 // to support debugging the code invoked by an inspector call, such
341 // as Runtime.evaluate
342 dispatching_messages_ = false;
343 if (dispatching_message_queue_.empty()) {
344 Mutex::ScopedLock scoped_lock(requests_lock_);
345 while (requests_.empty()) incoming_message_cond_.Wait(scoped_lock);
346 }
347 return true;
348 }
349
DispatchMessages()350 void MainThreadInterface::DispatchMessages() {
351 if (dispatching_messages_)
352 return;
353 dispatching_messages_ = true;
354 bool had_messages = false;
355 do {
356 if (dispatching_message_queue_.empty()) {
357 Mutex::ScopedLock scoped_lock(requests_lock_);
358 requests_.swap(dispatching_message_queue_);
359 }
360 had_messages = !dispatching_message_queue_.empty();
361 while (!dispatching_message_queue_.empty()) {
362 MessageQueue::value_type task;
363 std::swap(dispatching_message_queue_.front(), task);
364 dispatching_message_queue_.pop_front();
365
366 v8::SealHandleScope seal_handle_scope(agent_->env()->isolate);
367 task->Call(this);
368 }
369 } while (had_messages);
370 dispatching_messages_ = false;
371 }
372
GetHandle()373 std::shared_ptr<MainThreadHandle> MainThreadInterface::GetHandle() {
374 if (handle_ == nullptr)
375 handle_ = std::make_shared<MainThreadHandle>(this);
376 return handle_;
377 }
378
AddObject(int id,std::unique_ptr<Deletable> object)379 void MainThreadInterface::AddObject(int id,
380 std::unique_ptr<Deletable> object) {
381 CHECK_NOT_NULL(object);
382 managed_objects_[id] = std::move(object);
383 }
384
RemoveObject(int id)385 void MainThreadInterface::RemoveObject(int id) {
386 CHECK_EQ(1, managed_objects_.erase(id));
387 }
388
GetObject(int id)389 Deletable* MainThreadInterface::GetObject(int id) {
390 Deletable* pointer = GetObjectIfExists(id);
391 // This would mean the object is requested after it was disposed, which is
392 // a coding error.
393 CHECK_NOT_NULL(pointer);
394 return pointer;
395 }
396
GetObjectIfExists(int id)397 Deletable* MainThreadInterface::GetObjectIfExists(int id) {
398 auto iterator = managed_objects_.find(id);
399 if (iterator == managed_objects_.end()) {
400 return nullptr;
401 }
402 return iterator->second.get();
403 }
404
Connect(std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)405 std::unique_ptr<InspectorSession> MainThreadHandle::Connect(
406 std::unique_ptr<InspectorSessionDelegate> delegate,
407 bool prevent_shutdown) {
408 return std::unique_ptr<InspectorSession>(
409 new CrossThreadInspectorSession(++next_session_id_,
410 shared_from_this(),
411 std::move(delegate),
412 prevent_shutdown));
413 }
414
Post(std::unique_ptr<Request> request)415 bool MainThreadHandle::Post(std::unique_ptr<Request> request) {
416 Mutex::ScopedLock scoped_lock(block_lock_);
417 if (!main_thread_)
418 return false;
419 main_thread_->Post(std::move(request));
420 return true;
421 }
422
Reset()423 void MainThreadHandle::Reset() {
424 Mutex::ScopedLock scoped_lock(block_lock_);
425 main_thread_ = nullptr;
426 }
427 } // namespace
428
429 namespace {
430 using node::CheckedUvLoopClose;
431 using node::GetHumanReadableProcessName;
432 using node::InspectPublishUid;
433 using node::RegisterSignalHandler;
434 using node::inspector::FormatWsAddress;
435 using node::inspector::InspectorSocketServer;
436
437 namespace crypto = node::crypto;
438 namespace protocol = node::inspector::protocol;
439
440 // kKill closes connections and stops the server, kStop only stops the server
441 enum class TransportAction { kKill, kSendMessage, kStop };
442
ScriptPath(uv_loop_t * loop,const std::string & script_name)443 std::string ScriptPath(uv_loop_t* loop, const std::string& script_name) {
444 std::string script_path;
445
446 if (!script_name.empty()) {
447 uv_fs_t req;
448 req.ptr = nullptr;
449 if (0 == uv_fs_realpath(loop, &req, script_name.c_str(), nullptr)) {
450 CHECK_NOT_NULL(req.ptr);
451 script_path = std::string(static_cast<char*>(req.ptr));
452 }
453 uv_fs_req_cleanup(&req);
454 }
455
456 return script_path;
457 }
458
459 // UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
460 // Used ver 4 - with numbers
GenerateID()461 std::string GenerateID() {
462 uint16_t buffer[8];
463 CHECK(crypto::CSPRNG(buffer, sizeof(buffer)).is_ok());
464
465 char uuid[256];
466 snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
467 buffer[0], // time_low
468 buffer[1], // time_mid
469 buffer[2], // time_low
470 (buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version
471 (buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low
472 buffer[5], // node
473 buffer[6],
474 buffer[7]);
475 return uuid;
476 }
477
478 class RequestToServer {
479 public:
RequestToServer(TransportAction action,int session_id,std::unique_ptr<v8_inspector::StringBuffer> message)480 RequestToServer(TransportAction action,
481 int session_id,
482 std::unique_ptr<v8_inspector::StringBuffer> message)
483 : action_(action),
484 session_id_(session_id),
485 message_(std::move(message)) {}
486
Dispatch(InspectorSocketServer * server) const487 void Dispatch(InspectorSocketServer* server) const {
488 switch (action_) {
489 case TransportAction::kKill:
490 server->TerminateConnections();
491 [[fallthrough]];
492 case TransportAction::kStop:
493 server->Stop();
494 break;
495 case TransportAction::kSendMessage:
496 server->Send(
497 session_id_,
498 protocol::StringUtil::StringViewToUtf8(message_->string()));
499 break;
500 }
501 }
502
503 private:
504 TransportAction action_;
505 int session_id_;
506 std::unique_ptr<v8_inspector::StringBuffer> message_;
507 };
508
509 class RequestQueue;
510
511 class RequestQueueData {
512 public:
513 using MessageQueue = std::deque<RequestToServer>;
514
RequestQueueData(uv_loop_t * loop)515 explicit RequestQueueData(uv_loop_t* loop)
516 : handle_(std::make_shared<RequestQueue>(this)) {
517 int err = uv_async_init(loop, &async_, [](uv_async_t* async) {
518 RequestQueueData* wrapper =
519 node::ContainerOf(&RequestQueueData::async_, async);
520 wrapper->DoDispatch();
521 });
522 CHECK_EQ(0, err);
523 }
524
525 static void CloseAndFree(RequestQueueData* queue);
526
Post(int session_id,TransportAction action,std::unique_ptr<StringBuffer> message)527 void Post(int session_id,
528 TransportAction action,
529 std::unique_ptr<StringBuffer> message) {
530 Mutex::ScopedLock scoped_lock(state_lock_);
531 bool notify = messages_.empty();
532 messages_.emplace_back(action, session_id, std::move(message));
533 if (notify) {
534 CHECK_EQ(0, uv_async_send(&async_));
535 incoming_message_cond_.Broadcast(scoped_lock);
536 }
537 }
538
Wait()539 void Wait() {
540 Mutex::ScopedLock scoped_lock(state_lock_);
541 if (messages_.empty()) {
542 incoming_message_cond_.Wait(scoped_lock);
543 }
544 }
545
SetServer(InspectorSocketServer * server)546 void SetServer(InspectorSocketServer* server) {
547 server_ = server;
548 }
549
handle()550 std::shared_ptr<RequestQueue> handle() {
551 return handle_;
552 }
553
554 private:
555 ~RequestQueueData() = default;
556
GetMessages()557 MessageQueue GetMessages() {
558 Mutex::ScopedLock scoped_lock(state_lock_);
559 MessageQueue messages;
560 messages_.swap(messages);
561 return messages;
562 }
563
DoDispatch()564 void DoDispatch() {
565 if (server_ == nullptr)
566 return;
567 for (const auto& request : GetMessages()) {
568 request.Dispatch(server_);
569 }
570 }
571
572 std::shared_ptr<RequestQueue> handle_;
573 uv_async_t async_;
574 InspectorSocketServer* server_ = nullptr;
575 MessageQueue messages_;
576 Mutex state_lock_; // Locked before mutating the queue.
577 ConditionVariable incoming_message_cond_;
578 };
579
580 class RequestQueue {
581 public:
RequestQueue(RequestQueueData * data)582 explicit RequestQueue(RequestQueueData* data) : data_(data) {}
583
Reset()584 void Reset() {
585 Mutex::ScopedLock scoped_lock(lock_);
586 data_ = nullptr;
587 }
588
Post(int session_id,TransportAction action,std::unique_ptr<StringBuffer> message)589 void Post(int session_id,
590 TransportAction action,
591 std::unique_ptr<StringBuffer> message) {
592 Mutex::ScopedLock scoped_lock(lock_);
593 if (data_ != nullptr)
594 data_->Post(session_id, action, std::move(message));
595 }
596
Expired()597 bool Expired() {
598 Mutex::ScopedLock scoped_lock(lock_);
599 return data_ == nullptr;
600 }
601
602 private:
603 RequestQueueData* data_;
604 Mutex lock_;
605 };
606
607 class IoSessionDelegate : public InspectorSessionDelegate {
608 public:
IoSessionDelegate(std::shared_ptr<RequestQueue> queue,int id)609 explicit IoSessionDelegate(std::shared_ptr<RequestQueue> queue, int id)
610 : request_queue_(queue), id_(id) { }
SendMessageToFrontend(const v8_inspector::StringView & message)611 void SendMessageToFrontend(const v8_inspector::StringView& message) override {
612 request_queue_->Post(id_, TransportAction::kSendMessage,
613 StringBuffer::create(message));
614 }
615
616 private:
617 std::shared_ptr<RequestQueue> request_queue_;
618 int id_;
619 };
620
621 // Passed to InspectorSocketServer to handle WS inspector protocol events,
622 // mostly session start, message received, and session end.
623 class InspectorIoDelegate: public node::inspector::SocketServerDelegate {
624 public:
625 InspectorIoDelegate(std::shared_ptr<RequestQueueData> queue,
626 std::shared_ptr<MainThreadHandle> main_thread,
627 const std::string& target_id,
628 const std::string& script_path,
629 const std::string& script_name);
630 ~InspectorIoDelegate() override = default;
631
632 void StartSession(int session_id, const std::string& target_id) override;
633 void MessageReceived(int session_id, const std::string& message) override;
634 void EndSession(int session_id) override;
635
636 std::vector<std::string> GetTargetIds() override;
637 std::string GetTargetTitle(const std::string& id) override;
638 std::string GetTargetUrl(const std::string& id) override;
AssignServer(InspectorSocketServer * server)639 void AssignServer(InspectorSocketServer* server) override {
640 request_queue_->SetServer(server);
641 }
642
643 private:
644 std::shared_ptr<RequestQueueData> request_queue_;
645 std::shared_ptr<MainThreadHandle> main_thread_;
646 std::unordered_map<int, std::unique_ptr<InspectorSession>> sessions_;
647 const std::string script_name_;
648 const std::string script_path_;
649 const std::string target_id_;
650 };
651
InspectorIoDelegate(std::shared_ptr<RequestQueueData> queue,std::shared_ptr<MainThreadHandle> main_thread,const std::string & target_id,const std::string & script_path,const std::string & script_name)652 InspectorIoDelegate::InspectorIoDelegate(
653 std::shared_ptr<RequestQueueData> queue,
654 std::shared_ptr<MainThreadHandle> main_thread,
655 const std::string& target_id,
656 const std::string& script_path,
657 const std::string& script_name)
658 : request_queue_(queue), main_thread_(main_thread),
659 script_name_(script_name), script_path_(script_path),
660 target_id_(target_id) {}
661
StartSession(int session_id,const std::string & target_id)662 void InspectorIoDelegate::StartSession(int session_id,
663 const std::string& target_id) {
664 auto session = main_thread_->Connect(
665 std::unique_ptr<InspectorSessionDelegate>(
666 new IoSessionDelegate(request_queue_->handle(), session_id)), true);
667 if (session) {
668 sessions_[session_id] = std::move(session);
669 fprintf(stderr, "Debugger attached.\n");
670 }
671 }
672
MessageReceived(int session_id,const std::string & message)673 void InspectorIoDelegate::MessageReceived(int session_id,
674 const std::string& message) {
675 auto session = sessions_.find(session_id);
676 if (session != sessions_.end())
677 session->second->Dispatch(Utf8ToStringView(message)->string());
678 }
679
EndSession(int session_id)680 void InspectorIoDelegate::EndSession(int session_id) {
681 sessions_.erase(session_id);
682 }
683
GetTargetIds()684 std::vector<std::string> InspectorIoDelegate::GetTargetIds() {
685 return { target_id_ };
686 }
687
GetTargetTitle(const std::string & id)688 std::string InspectorIoDelegate::GetTargetTitle(const std::string& id) {
689 return script_name_.empty() ? GetHumanReadableProcessName() : script_name_;
690 }
691
GetTargetUrl(const std::string & id)692 std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) {
693 return "file://" + script_path_;
694 }
695
696 // static
CloseAndFree(RequestQueueData * queue)697 void RequestQueueData::CloseAndFree(RequestQueueData* queue) {
698 queue->handle_->Reset();
699 queue->handle_.reset();
700 uv_close(reinterpret_cast<uv_handle_t*>(&queue->async_),
701 [](uv_handle_t* handle) {
702 uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
703 RequestQueueData* wrapper =
704 node::ContainerOf(&RequestQueueData::async_, async);
705 delete wrapper;
706 });
707 }
708 } // namespace
709
710 class InspectorIo {
711 public:
712 // Start the inspector agent thread, waiting for it to initialize
713 // bool Start();
714 // Returns empty pointer if thread was not started
715 static std::unique_ptr<InspectorIo> Start(
716 std::shared_ptr<MainThreadHandle> main_thread,
717 const std::string& path,
718 std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
719 const node::InspectPublishUid& inspect_publish_uid);
720
721 // Will block till the transport thread shuts down
722 ~InspectorIo();
723
724 void StopAcceptingNewConnections();
725 std::string GetWsUrl() const;
726
727 private:
728 InspectorIo(std::shared_ptr<MainThreadHandle> handle,
729 const std::string& path,
730 std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
731 const node::InspectPublishUid& inspect_publish_uid);
732
733 // Wrapper for agent->ThreadMain()
734 static void ThreadMain(void* agent);
735
736 // Runs a uv_loop_t
737 void ThreadMain();
738
739 // This is a thread-safe object that will post async tasks. It lives as long
740 // as an Inspector object lives (almost as long as an Isolate).
741 std::shared_ptr<MainThreadHandle> main_thread_;
742 // Used to post on a frontend interface thread, lives while the server is
743 // running
744 std::shared_ptr<RequestQueue> request_queue_;
745 std::shared_ptr<ExclusiveAccess<HostPort>> host_port_;
746 node::InspectPublishUid inspect_publish_uid_;
747
748 // The IO thread runs its own uv_loop to implement the TCP server off
749 // the main thread.
750 uv_thread_t thread_;
751
752 // For setting up interthread communications
753 Mutex thread_start_lock_;
754 node::ConditionVariable thread_start_condition_;
755 std::string script_name_;
756 // May be accessed from any thread
757 const std::string id_;
758 };
759
760 // static
Start(std::shared_ptr<MainThreadHandle> main_thread,const std::string & path,std::shared_ptr<ExclusiveAccess<HostPort>> host_port,const InspectPublishUid & inspect_publish_uid)761 std::unique_ptr<InspectorIo> InspectorIo::Start(
762 std::shared_ptr<MainThreadHandle> main_thread,
763 const std::string& path,
764 std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
765 const InspectPublishUid& inspect_publish_uid) {
766 auto io = std::unique_ptr<InspectorIo>(
767 new InspectorIo(main_thread,
768 path,
769 host_port,
770 inspect_publish_uid));
771 if (io->request_queue_->Expired()) { // Thread is not running
772 return nullptr;
773 }
774 return io;
775 }
776
InspectorIo(std::shared_ptr<MainThreadHandle> main_thread,const std::string & path,std::shared_ptr<ExclusiveAccess<HostPort>> host_port,const InspectPublishUid & inspect_publish_uid)777 InspectorIo::InspectorIo(std::shared_ptr<MainThreadHandle> main_thread,
778 const std::string& path,
779 std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
780 const InspectPublishUid& inspect_publish_uid)
781 : main_thread_(main_thread),
782 host_port_(host_port),
783 inspect_publish_uid_(inspect_publish_uid),
784 thread_(),
785 script_name_(path),
786 id_(GenerateID()) {
787 Mutex::ScopedLock scoped_lock(thread_start_lock_);
788 CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain, this), 0);
789 thread_start_condition_.Wait(scoped_lock);
790 }
791
~InspectorIo()792 InspectorIo::~InspectorIo() {
793 request_queue_->Post(0, TransportAction::kKill, nullptr);
794 int err = uv_thread_join(&thread_);
795 CHECK_EQ(err, 0);
796 }
797
StopAcceptingNewConnections()798 void InspectorIo::StopAcceptingNewConnections() {
799 request_queue_->Post(0, TransportAction::kStop, nullptr);
800 }
801
802 // static
ThreadMain(void * io)803 void InspectorIo::ThreadMain(void* io) {
804 static_cast<InspectorIo*>(io)->ThreadMain();
805 }
806
ThreadMain()807 void InspectorIo::ThreadMain() {
808 uv_loop_t loop;
809 loop.data = nullptr;
810 int err = uv_loop_init(&loop);
811 CHECK_EQ(err, 0);
812 std::shared_ptr<RequestQueueData> queue(new RequestQueueData(&loop),
813 RequestQueueData::CloseAndFree);
814 std::string script_path = ScriptPath(&loop, script_name_);
815 std::unique_ptr<InspectorIoDelegate> delegate(
816 new InspectorIoDelegate(queue, main_thread_, id_,
817 script_path, script_name_));
818 std::string host;
819 int port;
820 int pid;
821 {
822 ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
823 host = host_port->host();
824 port = host_port->port();
825 pid = host_port->pid();
826 }
827 InspectorSocketServer server(std::move(delegate),
828 &loop,
829 std::move(host),
830 port,
831 inspect_publish_uid_,
832 stderr,
833 pid);
834 request_queue_ = queue->handle();
835 // Its lifetime is now that of the server delegate
836 queue.reset();
837 {
838 Mutex::ScopedLock scoped_lock(thread_start_lock_);
839 if (server.Start()) {
840 ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
841 host_port->set_port(server.Port());
842 }
843 thread_start_condition_.Broadcast(scoped_lock);
844 }
845 uv_run(&loop, UV_RUN_DEFAULT);
846 CheckedUvLoopClose(&loop);
847 }
848
GetWsUrl() const849 std::string InspectorIo::GetWsUrl() const {
850 ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
851 return FormatWsAddress(host_port->host(), host_port->port(), id_, true);
852 }
853
854 namespace {
855
856 using node::DebugCategory;
857 using node::TwoByteValue;
858
859 using v8::Context;
860 using v8::Function;
861 using v8::HandleScope;
862 using v8::Isolate;
863 using v8::Local;
864 using v8::Message;
865 using v8::Object;
866 using v8::Value;
867
868 using v8_inspector::StringBuffer;
869 using v8_inspector::StringView;
870 using v8_inspector::V8Inspector;
871 using v8_inspector::V8InspectorClient;
872
873 namespace per_process = node::per_process;
874
ToProtocolString(Isolate * isolate,Local<Value> value)875 std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
876 Local<Value> value) {
877 TwoByteValue buffer(isolate, value);
878 return StringBuffer::create(StringView(*buffer, buffer.length()));
879 }
880
881
882 const int CONTEXT_GROUP_ID = 1;
883
GetWorkerLabel(Environment * env)884 std::string GetWorkerLabel(Environment* env) {
885 std::ostringstream result;
886 // TODO: use thread ID as part of worker label.
887 result << "Worker[" << "env->thread_id()" << "]";
888 return result.str();
889 }
890
891 class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
892 public protocol::FrontendChannel {
893 public:
ChannelImpl(const std::unique_ptr<V8Inspector> & inspector,std::unique_ptr<InspectorSessionDelegate> delegate,std::shared_ptr<MainThreadHandle> main_thread_,bool prevent_shutdown)894 explicit ChannelImpl(const std::unique_ptr<V8Inspector>& inspector,
895 std::unique_ptr<InspectorSessionDelegate> delegate,
896 std::shared_ptr<MainThreadHandle> main_thread_,
897 bool prevent_shutdown)
898 : delegate_(std::move(delegate)), prevent_shutdown_(prevent_shutdown),
899 retaining_context_(false) {
900 session_ = inspector->connect(CONTEXT_GROUP_ID,
901 this,
902 StringView());
903 node_dispatcher_ = std::make_unique<protocol::UberDispatcher>(this);
904 runtime_agent_ = std::make_unique<protocol::RuntimeAgent>();
905 runtime_agent_->Wire(node_dispatcher_.get());
906 }
907
~ChannelImpl()908 ~ChannelImpl() override {
909 if (worker_agent_) {
910 worker_agent_->disable();
911 worker_agent_.reset(); // Dispose before the dispatchers
912 }
913 runtime_agent_->disable();
914 runtime_agent_.reset(); // Dispose before the dispatchers
915 }
916
dispatchProtocolMessage(const StringView & message)917 void dispatchProtocolMessage(const StringView& message) {
918 std::string raw_message = protocol::StringUtil::StringViewToUtf8(message);
919 per_process::Debug(DebugCategory::INSPECTOR_SERVER,
920 "[inspector received] %s\n",
921 raw_message);
922 std::unique_ptr<protocol::DictionaryValue> value =
923 protocol::DictionaryValue::cast(
924 protocol::StringUtil::parseJSON(message));
925 int call_id;
926 std::string method;
927 node_dispatcher_->parseCommand(value.get(), &call_id, &method);
928 if (v8_inspector::V8InspectorSession::canDispatchMethod(
929 Utf8ToStringView(method)->string())) {
930 session_->dispatchProtocolMessage(message);
931 } else {
932 node_dispatcher_->dispatch(call_id, method, std::move(value),
933 raw_message);
934 }
935 }
936
schedulePauseOnNextStatement(const std::string & reason)937 void schedulePauseOnNextStatement(const std::string& reason) {
938 std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
939 session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
940 }
941
preventShutdown()942 bool preventShutdown() {
943 return prevent_shutdown_;
944 }
945
notifyWaitingForDisconnect()946 bool notifyWaitingForDisconnect() {
947 retaining_context_ = runtime_agent_->notifyWaitingForDisconnect();
948 return retaining_context_;
949 }
950
retainingContext()951 bool retainingContext() {
952 return retaining_context_;
953 }
954
955 private:
sendResponse(int callId,std::unique_ptr<v8_inspector::StringBuffer> message)956 void sendResponse(
957 int callId,
958 std::unique_ptr<v8_inspector::StringBuffer> message) override {
959 sendMessageToFrontend(message->string());
960 }
961
sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message)962 void sendNotification(
963 std::unique_ptr<v8_inspector::StringBuffer> message) override {
964 sendMessageToFrontend(message->string());
965 }
966
flushProtocolNotifications()967 void flushProtocolNotifications() override { }
968
sendMessageToFrontend(const StringView & message)969 void sendMessageToFrontend(const StringView& message) {
970 if (per_process::enabled_debug_list.enabled(
971 DebugCategory::INSPECTOR_SERVER)) {
972 std::string raw_message = protocol::StringUtil::StringViewToUtf8(message);
973 per_process::Debug(DebugCategory::INSPECTOR_SERVER,
974 "[inspector send] %s\n",
975 raw_message);
976 }
977 delegate_->SendMessageToFrontend(message);
978 }
979
sendMessageToFrontend(const std::string & message)980 void sendMessageToFrontend(const std::string& message) {
981 sendMessageToFrontend(Utf8ToStringView(message)->string());
982 }
983
984 using Serializable = protocol::Serializable;
985
sendProtocolResponse(int callId,std::unique_ptr<Serializable> message)986 void sendProtocolResponse(int callId,
987 std::unique_ptr<Serializable> message) override {
988 sendMessageToFrontend(message->serializeToJSON());
989 }
sendProtocolNotification(std::unique_ptr<Serializable> message)990 void sendProtocolNotification(
991 std::unique_ptr<Serializable> message) override {
992 sendMessageToFrontend(message->serializeToJSON());
993 }
994
fallThrough(int callId,const std::string & method,const std::string & message)995 void fallThrough(int callId,
996 const std::string& method,
997 const std::string& message) override {
998 DCHECK(false);
999 }
1000
1001 std::unique_ptr<protocol::RuntimeAgent> runtime_agent_;
1002 std::unique_ptr<protocol::WorkerAgent> worker_agent_;
1003 std::unique_ptr<InspectorSessionDelegate> delegate_;
1004 std::unique_ptr<v8_inspector::V8InspectorSession> session_;
1005 std::unique_ptr<protocol::UberDispatcher> node_dispatcher_;
1006 bool prevent_shutdown_;
1007 bool retaining_context_;
1008 };
1009
1010 class SameThreadInspectorSession : public InspectorSession {
1011 public:
SameThreadInspectorSession(int session_id,std::shared_ptr<InspectorClient> client)1012 SameThreadInspectorSession(
1013 int session_id, std::shared_ptr<InspectorClient> client)
1014 : session_id_(session_id), client_(client) {}
1015 ~SameThreadInspectorSession() override;
1016 void Dispatch(const v8_inspector::StringView& message) override;
1017
1018 private:
1019 int session_id_;
1020 std::weak_ptr<InspectorClient> client_;
1021 };
1022
IsFilePath(const std::string & path)1023 bool IsFilePath(const std::string& path) {
1024 return !path.empty() && path[0] == '/';
1025 }
1026
ThrowUninitializedInspectorError(Environment * env)1027 void ThrowUninitializedInspectorError(Environment* env) {
1028 HandleScope scope(env->isolate);
1029
1030 const char* msg = "This Environment was initialized without a V8::Inspector";
1031 Local<Value> exception =
1032 v8::String::NewFromUtf8(env->isolate, msg).ToLocalChecked();
1033
1034 env->isolate->ThrowException(exception);
1035 }
1036
1037 } // namespace
1038
1039 class InspectorClient : public V8InspectorClient {
1040 public:
InspectorClient(Environment * env,bool is_main)1041 explicit InspectorClient(Environment* env, bool is_main)
1042 : env_(env), is_main_(is_main) {
1043 client_ = V8Inspector::create(env->isolate, this);
1044 // TODO(bnoordhuis) Make name configurable from src/node.cc.
1045 std::string name =
1046 is_main_ ? GetHumanReadableProcessName() : GetWorkerLabel(env);
1047 ContextInfo info(name);
1048 info.is_default = true;
1049 contextCreated(env->context(), info);
1050 }
1051
runMessageLoopOnPause(int context_group_id)1052 void runMessageLoopOnPause(int context_group_id) override {
1053 waiting_for_resume_ = true;
1054 runMessageLoop();
1055 }
1056
waitForSessionsDisconnect()1057 void waitForSessionsDisconnect() {
1058 waiting_for_sessions_disconnect_ = true;
1059 runMessageLoop();
1060 }
1061
waitForFrontend()1062 void waitForFrontend() {
1063 waiting_for_frontend_ = true;
1064 runMessageLoop();
1065 }
1066
maxAsyncCallStackDepthChanged(int depth)1067 void maxAsyncCallStackDepthChanged(int depth) override {
1068 if (waiting_for_sessions_disconnect_) {
1069 // V8 isolate is mostly done and is only letting Inspector protocol
1070 // clients gather data.
1071 return;
1072 }
1073 }
1074
contextCreated(Local<Context> context,const ContextInfo & info)1075 void contextCreated(Local<Context> context, const ContextInfo& info) {
1076 auto name_buffer = Utf8ToStringView(info.name);
1077 auto origin_buffer = Utf8ToStringView(info.origin);
1078 std::unique_ptr<StringBuffer> aux_data_buffer;
1079
1080 v8_inspector::V8ContextInfo v8info(
1081 context, CONTEXT_GROUP_ID, name_buffer->string());
1082 v8info.origin = origin_buffer->string();
1083
1084 if (info.is_default) {
1085 aux_data_buffer = Utf8ToStringView("{\"isDefault\":true}");
1086 } else {
1087 aux_data_buffer = Utf8ToStringView("{\"isDefault\":false}");
1088 }
1089 v8info.auxData = aux_data_buffer->string();
1090
1091 client_->contextCreated(v8info);
1092 }
1093
contextDestroyed(Local<Context> context)1094 void contextDestroyed(Local<Context> context) {
1095 client_->contextDestroyed(context);
1096 }
1097
quitMessageLoopOnPause()1098 void quitMessageLoopOnPause() override {
1099 waiting_for_resume_ = false;
1100 }
1101
runIfWaitingForDebugger(int context_group_id)1102 void runIfWaitingForDebugger(int context_group_id) override {
1103 waiting_for_frontend_ = false;
1104 }
1105
connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)1106 int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,
1107 bool prevent_shutdown) {
1108 int session_id = next_session_id_++;
1109 channels_[session_id] = std::make_unique<ChannelImpl>(client_,
1110 std::move(delegate),
1111 getThreadHandle(),
1112 prevent_shutdown);
1113 return session_id;
1114 }
1115
disconnectFrontend(int session_id)1116 void disconnectFrontend(int session_id) {
1117 auto it = channels_.find(session_id);
1118 if (it == channels_.end())
1119 return;
1120 bool retaining_context = it->second->retainingContext();
1121 channels_.erase(it);
1122 if (retaining_context) {
1123 for (const auto& id_channel : channels_) {
1124 if (id_channel.second->retainingContext())
1125 return;
1126 }
1127 contextDestroyed(env_->context());
1128 }
1129 if (waiting_for_sessions_disconnect_ && !is_main_)
1130 waiting_for_sessions_disconnect_ = false;
1131 }
1132
dispatchMessageFromFrontend(int session_id,const StringView & message)1133 void dispatchMessageFromFrontend(int session_id, const StringView& message) {
1134 channels_[session_id]->dispatchProtocolMessage(message);
1135 }
1136
ensureDefaultContextInGroup(int contextGroupId)1137 Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
1138 return env_->context();
1139 }
1140
ReportUncaughtException(Local<Value> error,Local<Message> message)1141 void ReportUncaughtException(Local<Value> error, Local<Message> message) {
1142 Isolate* isolate = env_->isolate;
1143 Local<Context> context = env_->context();
1144
1145 int script_id = message->GetScriptOrigin().ScriptId();
1146
1147 Local<v8::StackTrace> stack_trace = message->GetStackTrace();
1148
1149 if (!stack_trace.IsEmpty() && stack_trace->GetFrameCount() > 0 &&
1150 script_id == stack_trace->GetFrame(isolate, 0)->GetScriptId()) {
1151 script_id = 0;
1152 }
1153
1154 const uint8_t DETAILS[] = "Uncaught";
1155
1156 client_->exceptionThrown(
1157 context,
1158 StringView(DETAILS, sizeof(DETAILS) - 1),
1159 error,
1160 ToProtocolString(isolate, message->Get())->string(),
1161 ToProtocolString(isolate, message->GetScriptResourceName())->string(),
1162 message->GetLineNumber(context).FromMaybe(0),
1163 message->GetStartColumn(context).FromMaybe(0),
1164 client_->createStackTrace(stack_trace),
1165 script_id);
1166 }
1167
startRepeatingTimer(double interval_s,TimerCallback callback,void * data)1168 void startRepeatingTimer(double interval_s,
1169 TimerCallback callback,
1170 void* data) override {
1171 // TODO: implement this for supporting heap profiler.
1172 }
1173
cancelTimer(void * data)1174 void cancelTimer(void* data) override {
1175 // TODO: implement this for supporting heap profiler.
1176 }
1177
1178 // Async stack traces instrumentation.
AsyncTaskScheduled(const StringView & task_name,void * task,bool recurring)1179 void AsyncTaskScheduled(const StringView& task_name, void* task,
1180 bool recurring) {
1181 client_->asyncTaskScheduled(task_name, task, recurring);
1182 }
1183
AsyncTaskCanceled(void * task)1184 void AsyncTaskCanceled(void* task) {
1185 client_->asyncTaskCanceled(task);
1186 }
1187
AsyncTaskStarted(void * task)1188 void AsyncTaskStarted(void* task) {
1189 client_->asyncTaskStarted(task);
1190 }
1191
AsyncTaskFinished(void * task)1192 void AsyncTaskFinished(void* task) {
1193 client_->asyncTaskFinished(task);
1194 }
1195
AllAsyncTasksCanceled()1196 void AllAsyncTasksCanceled() {
1197 client_->allAsyncTasksCanceled();
1198 }
1199
schedulePauseOnNextStatement(const std::string & reason)1200 void schedulePauseOnNextStatement(const std::string& reason) {
1201 for (const auto& id_channel : channels_) {
1202 id_channel.second->schedulePauseOnNextStatement(reason);
1203 }
1204 }
1205
hasConnectedSessions()1206 bool hasConnectedSessions() {
1207 for (const auto& id_channel : channels_) {
1208 // Other sessions are "invisible" more most purposes
1209 if (id_channel.second->preventShutdown())
1210 return true;
1211 }
1212 return false;
1213 }
1214
notifyWaitingForDisconnect()1215 bool notifyWaitingForDisconnect() {
1216 bool retaining_context = false;
1217 for (const auto& id_channel : channels_) {
1218 if (id_channel.second->notifyWaitingForDisconnect())
1219 retaining_context = true;
1220 }
1221 return retaining_context;
1222 }
1223
getThreadHandle()1224 std::shared_ptr<MainThreadHandle> getThreadHandle() {
1225 if (!interface_) {
1226 interface_ = std::make_shared<MainThreadInterface>(
1227 env_->inspector_agent());
1228 }
1229 return interface_->GetHandle();
1230 }
1231
IsActive()1232 bool IsActive() {
1233 return !channels_.empty();
1234 }
1235
1236 private:
shouldRunMessageLoop()1237 bool shouldRunMessageLoop() {
1238 if (waiting_for_frontend_)
1239 return true;
1240 if (waiting_for_sessions_disconnect_ || waiting_for_resume_) {
1241 return hasConnectedSessions();
1242 }
1243 return false;
1244 }
1245
runMessageLoop()1246 void runMessageLoop() {
1247 if (running_nested_loop_)
1248 return;
1249
1250 running_nested_loop_ = true;
1251
1252 while (shouldRunMessageLoop()) {
1253 if (interface_) interface_->WaitForFrontendEvent();
1254 env_->RunAndClearInterrupts();
1255 }
1256 running_nested_loop_ = false;
1257 }
1258
currentTimeMS()1259 double currentTimeMS() override {
1260 return env_->platform()->CurrentClockTimeMillis();
1261 }
1262
resourceNameToUrl(const StringView & resource_name_view)1263 std::unique_ptr<StringBuffer> resourceNameToUrl(
1264 const StringView& resource_name_view) override {
1265 std::string resource_name =
1266 protocol::StringUtil::StringViewToUtf8(resource_name_view);
1267 if (!IsFilePath(resource_name))
1268 return nullptr;
1269
1270 std::string url = node::url::FromFilePath(resource_name);
1271 return Utf8ToStringView(url);
1272 }
1273
1274 Environment* env_;
1275 bool is_main_;
1276 bool running_nested_loop_ = false;
1277 std::unique_ptr<V8Inspector> client_;
1278 std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_;
1279 int next_session_id_ = 1;
1280 bool waiting_for_resume_ = false;
1281 bool waiting_for_frontend_ = false;
1282 bool waiting_for_sessions_disconnect_ = false;
1283 // Allows accessing Inspector from non-main threads
1284 std::shared_ptr<MainThreadInterface> interface_;
1285 };
1286
Agent(Environment * env)1287 Agent::Agent(Environment* env)
1288 : parent_env_(env) {}
1289
1290 Agent::~Agent() = default;
1291
Start(const std::string & path,std::shared_ptr<ExclusiveAccess<HostPort>> host_port,bool is_main,bool wait_for_connect)1292 bool Agent::Start(const std::string& path,
1293 std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
1294 bool is_main,
1295 bool wait_for_connect) {
1296 path_ = path;
1297 CHECK_NOT_NULL(host_port);
1298 host_port_ = host_port;
1299
1300 client_ = std::make_shared<InspectorClient>(parent_env_, is_main);
1301
1302 if (!StartIoThread()) {
1303 return false;
1304 }
1305
1306 if (wait_for_connect) {
1307 client_->waitForFrontend();
1308 }
1309 return true;
1310 }
1311
StartIoThread()1312 bool Agent::StartIoThread() {
1313 if (io_ != nullptr)
1314 return true;
1315
1316 if (!client_) {
1317 ThrowUninitializedInspectorError(parent_env_);
1318 return false;
1319 }
1320
1321 CHECK_NOT_NULL(client_);
1322
1323 io_ = InspectorIo::Start(client_->getThreadHandle(),
1324 path_,
1325 host_port_,
1326 { false, true });
1327 if (io_ == nullptr) {
1328 return false;
1329 }
1330 return true;
1331 }
1332
Stop()1333 void Agent::Stop() {
1334 io_.reset();
1335 }
1336
Connect(std::unique_ptr<InspectorSessionDelegate> delegate,bool prevent_shutdown)1337 std::unique_ptr<InspectorSession> Agent::Connect(
1338 std::unique_ptr<InspectorSessionDelegate> delegate,
1339 bool prevent_shutdown) {
1340 if (!client_) {
1341 ThrowUninitializedInspectorError(parent_env_);
1342 return std::unique_ptr<InspectorSession>{};
1343 }
1344
1345 CHECK_NOT_NULL(client_);
1346
1347 int session_id = client_->connectFrontend(std::move(delegate),
1348 prevent_shutdown);
1349 return std::unique_ptr<InspectorSession>(
1350 new SameThreadInspectorSession(session_id, client_));
1351 }
1352
WaitForDisconnect()1353 void Agent::WaitForDisconnect() {
1354 if (!client_) {
1355 ThrowUninitializedInspectorError(parent_env_);
1356 return;
1357 }
1358
1359 CHECK_NOT_NULL(client_);
1360 if (client_->hasConnectedSessions()) {
1361 fprintf(stderr, "Waiting for the debugger to disconnect...\n");
1362 fflush(stderr);
1363 }
1364 if (!client_->notifyWaitingForDisconnect()) {
1365 client_->contextDestroyed(parent_env_->context());
1366 }
1367 if (io_ != nullptr) {
1368 io_->StopAcceptingNewConnections();
1369 client_->waitForSessionsDisconnect();
1370 }
1371 }
1372
ReportUncaughtException(Local<Value> error,Local<Message> message)1373 void Agent::ReportUncaughtException(Local<Value> error,
1374 Local<Message> message) {
1375 if (!IsListening())
1376 return;
1377 client_->ReportUncaughtException(error, message);
1378 WaitForDisconnect();
1379 }
1380
PauseOnNextJavascriptStatement(const std::string & reason)1381 void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
1382 client_->schedulePauseOnNextStatement(reason);
1383 }
1384
AsyncTaskScheduled(const StringView & task_name,void * task,bool recurring)1385 void Agent::AsyncTaskScheduled(const StringView& task_name, void* task,
1386 bool recurring) {
1387 client_->AsyncTaskScheduled(task_name, task, recurring);
1388 }
1389
AsyncTaskCanceled(void * task)1390 void Agent::AsyncTaskCanceled(void* task) {
1391 client_->AsyncTaskCanceled(task);
1392 }
1393
AsyncTaskStarted(void * task)1394 void Agent::AsyncTaskStarted(void* task) {
1395 client_->AsyncTaskStarted(task);
1396 }
1397
AsyncTaskFinished(void * task)1398 void Agent::AsyncTaskFinished(void* task) {
1399 client_->AsyncTaskFinished(task);
1400 }
1401
AllAsyncTasksCanceled()1402 void Agent::AllAsyncTasksCanceled() {
1403 client_->AllAsyncTasksCanceled();
1404 }
1405
ContextCreated(Local<Context> context,const ContextInfo & info)1406 void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
1407 if (client_ == nullptr) // This happens for a main context
1408 return;
1409 client_->contextCreated(context, info);
1410 }
1411
IsActive()1412 bool Agent::IsActive() {
1413 if (client_ == nullptr)
1414 return false;
1415 return io_ != nullptr || client_->IsActive();
1416 }
1417
WaitForConnect()1418 void Agent::WaitForConnect() {
1419 if (!client_) {
1420 ThrowUninitializedInspectorError(parent_env_);
1421 return;
1422 }
1423
1424 CHECK_NOT_NULL(client_);
1425 client_->waitForFrontend();
1426 }
1427
GetWsUrl() const1428 std::string Agent::GetWsUrl() const {
1429 if (io_ == nullptr)
1430 return "";
1431 return io_->GetWsUrl();
1432 }
1433
~SameThreadInspectorSession()1434 SameThreadInspectorSession::~SameThreadInspectorSession() {
1435 auto client = client_.lock();
1436 if (client)
1437 client->disconnectFrontend(session_id_);
1438 }
1439
Dispatch(const v8_inspector::StringView & message)1440 void SameThreadInspectorSession::Dispatch(
1441 const v8_inspector::StringView& message) {
1442 auto client = client_.lock();
1443 if (client)
1444 client->dispatchMessageFromFrontend(session_id_, message);
1445 }
1446
1447 } // namespace v8impl
1448