9 #include "v8-inspector.h" 14 #include <unicode/unistr.h> 23 using AsyncAndAgent = std::pair<uv_async_t, Agent*>;
24 using v8_inspector::StringBuffer;
25 using v8_inspector::StringView;
27 template<
typename Transport>
28 using TransportAndIo = std::pair<Transport*, InspectorIo*>;
30 std::string GetProcessTitle() {
32 int err = uv_get_process_title(title,
sizeof(title));
41 std::string ScriptPath(uv_loop_t* loop,
const std::string& script_name) {
42 std::string script_path;
44 if (!script_name.empty()) {
47 if (0 == uv_fs_realpath(loop, &req, script_name.c_str(),
nullptr)) {
48 CHECK_NE(req.ptr,
nullptr);
49 script_path = std::string(static_cast<char*>(req.ptr));
51 uv_fs_req_cleanup(&req);
59 std::string GenerateID() {
65 snprintf(uuid,
sizeof(uuid),
"%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
69 (buffer[3] & 0x0fff) | 0x4000,
70 (buffer[4] & 0x3fff) | 0x8000,
77 std::string StringViewToUtf8(
const StringView& view) {
79 return std::string(reinterpret_cast<const char*>(view.characters8()),
82 const uint16_t*
source = view.characters16();
83 const UChar* unicodeSource =
reinterpret_cast<const UChar*
>(
source);
84 static_assert(
sizeof(*source) ==
sizeof(*unicodeSource),
85 "sizeof(*source) == sizeof(*unicodeSource)");
87 size_t result_length = view.length() *
sizeof(*source);
88 std::string result(result_length,
'\0');
89 UnicodeString utf16(unicodeSource, view.length());
93 CheckedArrayByteSink sink(&result[0], result_length);
95 result_length = sink.NumberOfBytesAppended();
96 result.resize(result_length);
97 done = !sink.Overflowed();
102 void HandleSyncCloseCb(uv_handle_t* handle) {
103 *
static_cast<bool*
>(handle->data) =
true;
106 int CloseAsyncAndLoop(uv_async_t* async) {
107 bool is_closed =
false;
108 async->data = &is_closed;
109 uv_close(reinterpret_cast<uv_handle_t*>(async), HandleSyncCloseCb);
111 uv_run(async->loop, UV_RUN_ONCE);
112 async->data =
nullptr;
113 return uv_loop_close(async->loop);
117 void ReleasePairOnAsyncClose(uv_handle_t* async) {
118 AsyncAndAgent* pair = node::ContainerOf(&AsyncAndAgent::first,
119 reinterpret_cast<uv_async_t*>(async));
126 UnicodeString utf16 =
127 UnicodeString::fromUTF8(StringPiece(message.data(), message.length()));
128 StringView view(reinterpret_cast<const uint16_t*>(utf16.getBuffer()),
130 return StringBuffer::create(view);
148 const std::string& script_name,
bool wait);
151 bool StartSession(
int session_id,
const std::string& target_id)
override;
153 void MessageReceived(
int session_id,
const std::string& message)
override;
155 void EndSession(
int session_id)
override;
157 std::vector<std::string> GetTargetIds()
override;
158 std::string GetTargetTitle(
const std::string&
id)
override;
159 std::string GetTargetUrl(
const std::string&
id)
override;
169 const std::string script_name_;
170 const std::string script_path_;
171 const std::string target_id_;
178 io->DispatchMessages();
188 io->DispatchMessages();
197 bool wait_for_connect)
198 : options_(options), thread_(), delegate_(
nullptr),
199 state_(State::kNew), parent_env_(env),
200 thread_req_(), platform_(platform),
201 dispatching_messages_(false), session_id_(0),
203 wait_for_connect_(wait_for_connect), port_(-1) {
204 main_thread_req_ =
new AsyncAndAgent({uv_async_t(), env->inspector_agent()});
205 CHECK_EQ(0, uv_async_init(env->event_loop(), &main_thread_req_->first,
206 InspectorIo::MainThreadReqAsyncCb));
207 uv_unref(reinterpret_cast<uv_handle_t*>(&main_thread_req_->first));
208 CHECK_EQ(0, uv_sem_init(&thread_start_sem_, 0));
212 uv_sem_destroy(&thread_start_sem_);
213 uv_close(reinterpret_cast<uv_handle_t*>(&main_thread_req_->first),
214 ReleasePairOnAsyncClose);
218 CHECK_EQ(state_, State::kNew);
219 CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain,
this), 0);
220 uv_sem_wait(&thread_start_sem_);
222 if (state_ == State::kError) {
225 state_ = State::kAccepting;
226 if (wait_for_connect_) {
233 CHECK(state_ == State::kAccepting || state_ == State::kConnected);
235 int err = uv_thread_join(&thread_);
237 state_ = State::kShutDown;
242 return delegate_ !=
nullptr && delegate_->
IsConnected();
246 return platform_ !=
nullptr;
250 if (state_ == State::kAccepting)
251 state_ = State::kDone;
252 if (state_ == State::kConnected) {
253 state_ = State::kShutDown;
255 fprintf(stderr,
"Waiting for the debugger to disconnect...\n");
257 parent_env_->inspector_agent()->RunMessageLoop();
262 void InspectorIo::ThreadMain(
void* io) {
263 static_cast<InspectorIo*
>(io)->ThreadMain<InspectorSocketServer>();
267 template <
typename Transport>
268 void InspectorIo::IoThreadAsyncCb(uv_async_t* async) {
269 TransportAndIo<Transport>* transport_and_io =
270 static_cast<TransportAndIo<Transport>*
>(async->data);
271 if (transport_and_io ==
nullptr) {
274 Transport* transport = transport_and_io->first;
276 MessageQueue<TransportAction> outgoing_message_queue;
277 io->SwapBehindLock(&io->outgoing_message_queue_, &outgoing_message_queue);
278 for (
const auto& outgoing : outgoing_message_queue) {
279 switch (std::get<0>(outgoing)) {
281 transport->TerminateConnections();
284 transport->Stop(
nullptr);
287 std::string message = StringViewToUtf8(std::get<2>(outgoing)->
string());
288 transport->Send(std::get<1>(outgoing), message);
294 template<
typename Transport>
295 void InspectorIo::ThreadMain() {
298 int err = uv_loop_init(&loop);
300 thread_req_.data =
nullptr;
301 err = uv_async_init(&loop, &thread_req_, IoThreadAsyncCb<Transport>);
303 std::string script_path = ScriptPath(&loop, script_name_);
306 delegate_ = &delegate;
307 Transport server(&delegate, &loop, options_.
host_name(), options_.
port());
308 TransportAndIo<Transport> queue_transport(&server,
this);
309 thread_req_.data = &queue_transport;
310 if (!server.Start()) {
311 state_ = State::kError;
312 CHECK_EQ(0, CloseAsyncAndLoop(&thread_req_));
313 uv_sem_post(&thread_start_sem_);
316 port_ = server.Port();
317 if (!wait_for_connect_) {
318 uv_sem_post(&thread_start_sem_);
320 uv_run(&loop, UV_RUN_DEFAULT);
321 thread_req_.data =
nullptr;
322 CHECK_EQ(uv_loop_close(&loop), 0);
326 template <
typename ActionType>
327 bool InspectorIo::AppendMessage(MessageQueue<ActionType>* queue,
328 ActionType action,
int session_id,
329 std::unique_ptr<StringBuffer> buffer) {
330 Mutex::ScopedLock scoped_lock(state_lock_);
331 bool trigger_pumping = queue->empty();
332 queue->push_back(std::make_tuple(action, session_id, std::move(buffer)));
333 return trigger_pumping;
336 template <
typename ActionType>
337 void InspectorIo::SwapBehindLock(MessageQueue<ActionType>* vector1,
338 MessageQueue<ActionType>* vector2) {
339 Mutex::ScopedLock scoped_lock(state_lock_);
340 vector1->swap(*vector2);
344 const std::string& message) {
345 if (AppendMessage(&incoming_message_queue_, action, session_id,
347 Agent* agent = main_thread_req_->second;
348 v8::Isolate* isolate = parent_env_->isolate();
349 platform_->CallOnForegroundThread(isolate,
352 CHECK_EQ(0, uv_async_send(&main_thread_req_->first));
354 NotifyMessageReceived();
358 return delegate_ ? delegate_->
GetTargetIds() : std::vector<std::string>();
361 void InspectorIo::WaitForFrontendMessageWhilePaused() {
362 dispatching_messages_ =
false;
363 Mutex::ScopedLock scoped_lock(state_lock_);
364 if (incoming_message_queue_.empty())
365 incoming_message_cond_.
Wait(scoped_lock);
368 void InspectorIo::NotifyMessageReceived() {
369 Mutex::ScopedLock scoped_lock(state_lock_);
370 incoming_message_cond_.
Broadcast(scoped_lock);
373 void InspectorIo::DispatchMessages() {
378 if (dispatching_messages_)
380 dispatching_messages_ =
true;
381 bool had_messages =
false;
383 if (dispatching_message_queue_.empty())
384 SwapBehindLock(&incoming_message_queue_, &dispatching_message_queue_);
385 had_messages = !dispatching_message_queue_.empty();
386 while (!dispatching_message_queue_.empty()) {
387 MessageQueue<InspectorAction>::value_type task;
388 std::swap(dispatching_message_queue_.front(), task);
389 dispatching_message_queue_.pop_front();
390 StringView message = std::get<2>(task)->
string();
391 switch (std::get<0>(task)) {
393 CHECK_EQ(session_delegate_,
nullptr);
394 session_id_ = std::get<1>(task);
395 state_ = State::kConnected;
396 fprintf(stderr,
"Debugger attached.\n");
397 session_delegate_ = std::unique_ptr<InspectorSessionDelegate>(
399 parent_env_->inspector_agent()->Connect(session_delegate_.get());
402 CHECK_NE(session_delegate_,
nullptr);
403 if (state_ == State::kShutDown) {
404 state_ = State::kDone;
406 state_ = State::kAccepting;
408 parent_env_->inspector_agent()->Disconnect();
409 session_delegate_.reset();
412 parent_env_->inspector_agent()->Dispatch(message);
416 }
while (had_messages);
417 dispatching_messages_ =
false;
421 void InspectorIo::MainThreadReqAsyncCb(uv_async_t* req) {
422 AsyncAndAgent* pair = node::ContainerOf(&AsyncAndAgent::first, req);
427 io->DispatchMessages();
431 const StringView& inspector_message) {
432 AppendMessage(&outgoing_message_queue_, action, session_id,
433 StringBuffer::create(inspector_message));
434 int err = uv_async_send(&thread_req_);
439 const std::string& script_path,
440 const std::string& script_name,
445 script_name_(script_name),
446 script_path_(script_path),
447 target_id_(GenerateID()),
452 const std::string& target_id) {
462 const std::string& message) {
468 if (message.find(
"\"Runtime.runIfWaitingForDebugger\"") !=
484 return { target_id_ };
488 return script_name_.empty() ? GetProcessTitle() : script_name_;
492 return "file://" + script_path_;
496 io_->WaitForFrontendMessageWhilePaused();
501 const v8_inspector::StringView& message) {
std::unique_ptr< StringBuffer > Utf8ToStringView(const std::string &message)
InspectorIo(node::Environment *env, v8::Platform *platform, const std::string &path, const DebugOptions &options, bool wait_for_connect)
friend class IoSessionDelegate
friend class DispatchMessagesTask
DispatchMessagesTask(Agent *agent)
IoSessionDelegate(InspectorIo *io)
void InterruptCallback(v8::Isolate *, void *agent)
std::vector< std::string > GetTargetIds() override
void Broadcast(const ScopedLock &)
InspectorIoDelegate(InspectorIo *io, const std::string &script_path, const std::string &script_name, bool wait)
std::string GetTargetUrl(const std::string &id) override
void SendMessageToFrontend(const v8_inspector::StringView &message) override
void PostIncomingMessage(InspectorAction action, int session_id, const std::string &message)
void EndSession(int session_id) override
friend void InterruptCallback(v8::Isolate *, void *agent)
bool WaitForFrontendMessageWhilePaused() override
void MessageReceived(int session_id, const std::string &message) override
std::vector< std::string > GetTargetIds() const
void Wait(const ScopedLock &scoped_lock)
void ServerDone() override
std::string GetTargetTitle(const std::string &id) override
bool StartSession(int session_id, const std::string &target_id) override
bool EntropySource(unsigned char *buffer, size_t length)
std::string host_name() const