From f92f8d697e50c31f8a9276d584129c89561c50b2 Mon Sep 17 00:00:00 2001 From: liuweili Date: Sat, 11 Jan 2025 15:04:09 +0800 Subject: [PATCH 1/6] add js_native_api_v8_inspector Signed-off-by: liuweili --- src/inspector/js_native_api_v8_inspector.cpp | 1330 ++++++++++++++++++ src/inspector/js_native_api_v8_inspector.h | 139 ++ 2 files changed, 1469 insertions(+) create mode 100644 src/inspector/js_native_api_v8_inspector.cpp create mode 100644 src/inspector/js_native_api_v8_inspector.h diff --git a/src/inspector/js_native_api_v8_inspector.cpp b/src/inspector/js_native_api_v8_inspector.cpp new file mode 100644 index 0000000..4b982a7 --- /dev/null +++ b/src/inspector/js_native_api_v8_inspector.cpp @@ -0,0 +1,1330 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_native_api_v8_inspector.h" + +#include + +#include "inspector_socket_server.h" +#include "inspector_utils.h" +#include "js_native_api_v8.h" +#include "jsvm_mutex.h" +#include "libplatform/libplatform.h" +#include "v8-inspector.h" +#include "v8-platform.h" + +#ifdef __POSIX__ +#include // PTHREAD_STACK_MIN +#include +#endif // __POSIX__ + +#include +#include +#include +#include +#include + +namespace v8impl { + +namespace { +using jsvm::ConditionVariable; +using jsvm::Mutex; +using jsvm::inspector::StringViewToUtf8; +using jsvm::inspector::Utf8ToStringView; +using v8_inspector::StringBuffer; +using v8_inspector::StringView; + +class MainThreadInterface; + +class Request { +public: + virtual void Call(MainThreadInterface*) = 0; + virtual ~Request() = default; +}; + +class Deletable { +public: + virtual ~Deletable() = default; +}; + +using MessageQueue = std::deque>; + +class MainThreadHandle : public std::enable_shared_from_this { +public: + explicit MainThreadHandle(MainThreadInterface* mainThread) : mainThread(mainThread) {} + ~MainThreadHandle() + { + Mutex::ScopedLock scopedLock(blockLock); + CHECK_NULL(mainThread); // mainThread should have called Reset + } + std::unique_ptr Connect(std::unique_ptr delegate, bool preventShutdown); + int NewObjectId() + { + return ++nextObjectId; + } + bool Post(std::unique_ptr request); + +private: + void Reset(); + + MainThreadInterface* mainThread; + Mutex blockLock; + int nextSessionId = 0; + std::atomic_int nextObjectId = { 1 }; + + friend class MainThreadInterface; +}; + +class MainThreadInterface : public std::enable_shared_from_this { +public: + explicit MainThreadInterface(Agent* agent); + ~MainThreadInterface(); + + void DispatchMessages(); + void Post(std::unique_ptr request); + bool WaitForFrontendEvent(); + std::shared_ptr GetHandle(); + Agent* inspector_agent() + { + return agent; + } + void AddObject(int handle, std::unique_ptr object); + Deletable* GetObject(int id); + Deletable* GetObjectIfExists(int id); + void RemoveObject(int handle); + +private: + MessageQueue requests; + Mutex requestsLock; // requests live across threads + // This queue is to maintain the order of the messages for the cases + // when we reenter the DispatchMessages function. + MessageQueue dispatchingMessageQueue; + bool dispatchingMessages = false; + ConditionVariable incomingMessageCond; + // Used from any thread + Agent* const agent; + std::shared_ptr handle; + std::unordered_map> managedObjects; +}; + +template +class DeletableWrapper : public Deletable { +public: + explicit DeletableWrapper(std::unique_ptr object) : object(std::move(object)) {} + ~DeletableWrapper() override = default; + + static T* Get(MainThreadInterface* thread, int id) + { + return static_cast*>(thread->GetObject(id))->object.get(); + } + +private: + std::unique_ptr object; +}; + +template +std::unique_ptr WrapInDeletable(std::unique_ptr object) +{ + return std::unique_ptr>(new DeletableWrapper(std::move(object))); +} + +template +class CreateObjectRequest : public Request { +public: + CreateObjectRequest(int objectId, Factory factory) : objectId(objectId), factory(std::move(factory)) {} + + void Call(MainThreadInterface* thread) override + { + thread->AddObject(objectId, WrapInDeletable(factory(thread))); + } + +private: + int objectId; + Factory factory; +}; + +template +std::unique_ptr NewCreateRequest(int objectId, Factory factory) +{ + return std::unique_ptr(new CreateObjectRequest(objectId, std::move(factory))); +} + +class DeleteRequest : public Request { +public: + explicit DeleteRequest(int objectId) : objectId(objectId) {} + + void Call(MainThreadInterface* thread) override + { + thread->RemoveObject(objectId); + } + +private: + int objectId; +}; + +template +class CallRequest : public Request { +public: + CallRequest(int id, Fn fn) : id(id), fn(std::move(fn)) {} + + void Call(MainThreadInterface* thread) override + { + fn(DeletableWrapper::Get(thread, id)); + } + +private: + int id; + Fn fn; +}; + +template +class AnotherThreadObjectReference { +public: + AnotherThreadObjectReference(std::shared_ptr thread, int objectId) + : thread(thread), objectId(objectId) + {} + + template + AnotherThreadObjectReference(std::shared_ptr thread, Factory factory) + : AnotherThreadObjectReference(thread, thread->NewObjectId()) + { + thread->Post(NewCreateRequest(objectId, std::move(factory))); + } + AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete; + + ~AnotherThreadObjectReference() + { + // Disappearing thread may cause a memory leak + thread->Post(std::make_unique(objectId)); + } + + template + void Call(Fn fn) const + { + using Request = CallRequest; + thread->Post(std::unique_ptr(new Request(objectId, std::move(fn)))); + } + + template + void Call(void (T::*fn)(Arg), Arg argument) const + { + Call(std::bind(Apply, std::placeholders::_1, fn, std::move(argument))); + } + +private: + // This has to use non-const reference to support std::bind with non-copyable + // types + template + static void Apply(T* target, void (T::*fn)(Argument), Argument& argument) /* NOLINT (runtime/references) */ + { + (target->*fn)(std::move(argument)); + } + + std::shared_ptr thread; + const int objectId; +}; + +class MainThreadSessionState { +public: + MainThreadSessionState(MainThreadInterface* thread, bool preventShutdown) + : thread(thread), preventShutdown(preventShutdown) + {} + + static std::unique_ptr Create(MainThreadInterface* thread, bool preventShutdown) + { + return std::make_unique(thread, preventShutdown); + } + + void Connect(std::unique_ptr delegate) + { + Agent* agent = thread->inspector_agent(); + if (agent != nullptr) { + session = agent->Connect(std::move(delegate), preventShutdown); + } + } + + void Dispatch(std::unique_ptr message) + { + session->Dispatch(message->string()); + } + +private: + MainThreadInterface* thread; + bool preventShutdown; + std::unique_ptr session; +}; + +class CrossThreadInspectorSession : public InspectorSession { +public: + CrossThreadInspectorSession(int id, + std::shared_ptr thread, + std::unique_ptr delegate, + bool preventShutdown) + : state(thread, std::bind(MainThreadSessionState::Create, std::placeholders::_1, preventShutdown)) + { + state.Call(&MainThreadSessionState::Connect, std::move(delegate)); + } + + void Dispatch(const StringView& message) override + { + state.Call(&MainThreadSessionState::Dispatch, StringBuffer::create(message)); + } + +private: + AnotherThreadObjectReference state; +}; + +class ThreadSafeDelegate : public InspectorSessionDelegate { +public: + ThreadSafeDelegate(std::shared_ptr thread, int objectId) + : thread(thread), delegate(thread, objectId) + {} + + void SendMessageToFrontend(const v8_inspector::StringView& message) override + { + delegate.Call([m = StringBuffer::create(message)](InspectorSessionDelegate* delegate) { + delegate->SendMessageToFrontend(m->string()); + }); + } + +private: + std::shared_ptr thread; + AnotherThreadObjectReference delegate; +}; + +MainThreadInterface::MainThreadInterface(Agent* agent) : agent(agent) {} + +MainThreadInterface::~MainThreadInterface() +{ + if (handle) { + handle->Reset(); + } +} + +void MainThreadInterface::Post(std::unique_ptr request) +{ + CHECK_NOT_NULL(agent); + Mutex::ScopedLock scopedLock(requestsLock); + bool needsNotify = requests.empty(); + requests.push_back(std::move(request)); + if (needsNotify) { + std::weak_ptr weakSelf { shared_from_this() }; + agent->env()->RequestInterrupt([weakSelf](Environment*) { + if (auto iface = weakSelf.lock()) { + iface->DispatchMessages(); + } + }); + } + incomingMessageCond.Broadcast(scopedLock); +} + +bool MainThreadInterface::WaitForFrontendEvent() +{ + // We allow DispatchMessages reentry as we enter the pause. This is important + // to support debugging the code invoked by an inspector call, such + // as Runtime.evaluate + dispatchingMessages = false; + if (dispatchingMessageQueue.empty()) { + Mutex::ScopedLock scopedLock(requestsLock); + while (requests.empty()) + incomingMessageCond.Wait(scopedLock); + } + return true; +} + +void MainThreadInterface::DispatchMessages() +{ + if (dispatchingMessages) { + return; + } + dispatchingMessages = true; + bool hadMessages = false; + do { + if (dispatchingMessageQueue.empty()) { + Mutex::ScopedLock scopedLock(requestsLock); + requests.swap(dispatchingMessageQueue); + } + hadMessages = !dispatchingMessageQueue.empty(); + while (!dispatchingMessageQueue.empty()) { + MessageQueue::value_type task; + std::swap(dispatchingMessageQueue.front(), task); + dispatchingMessageQueue.pop_front(); + + v8::SealHandleScope sealHandleScope(agent->env()->isolate); + task->Call(this); + } + } while (hadMessages); + dispatchingMessages = false; +} + +std::shared_ptr MainThreadInterface::GetHandle() +{ + if (handle == nullptr) { + handle = std::make_shared(this); + } + return handle; +} + +void MainThreadInterface::AddObject(int id, std::unique_ptr object) +{ + CHECK_NOT_NULL(object); + managedObjects[id] = std::move(object); +} + +void MainThreadInterface::RemoveObject(int handle) +{ + CHECK_EQ(1, managedObjects.erase(handle)); +} + +Deletable* MainThreadInterface::GetObject(int id) +{ + Deletable* pointer = GetObjectIfExists(id); + // This would mean the object is requested after it was disposed, which is + // a coding error. + CHECK_NOT_NULL(pointer); + return pointer; +} + +Deletable* MainThreadInterface::GetObjectIfExists(int id) +{ + auto iterator = managedObjects.find(id); + if (iterator == managedObjects.end()) { + return nullptr; + } + return iterator->second.get(); +} + +std::unique_ptr MainThreadHandle::Connect(std::unique_ptr delegate, + bool preventShutdown) +{ + return std::unique_ptr( + new CrossThreadInspectorSession(++nextSessionId, shared_from_this(), std::move(delegate), preventShutdown)); +} + +bool MainThreadHandle::Post(std::unique_ptr request) +{ + Mutex::ScopedLock scopedLock(blockLock); + if (!mainThread) { + return false; + } + mainThread->Post(std::move(request)); + return true; +} + +void MainThreadHandle::Reset() +{ + Mutex::ScopedLock scopedLock(blockLock); + mainThread = nullptr; +} +} // namespace + +namespace { +using jsvm::InspectPublishUid; +using jsvm::inspector::CheckedUvLoopClose; +using jsvm::inspector::CSPRNG; +using jsvm::inspector::FormatWsAddress; +using jsvm::inspector::GetHumanReadableProcessName; +using jsvm::inspector::InspectorSocketServer; + +// K_KILL closes connections and stops the server, K_STOP only stops the server +enum class TransportAction { K_KILL, K_SEND_MESSAGE, K_STOP }; + +std::string ScriptPath(uv_loop_t* loop, const std::string& scriptName) +{ + std::string scriptPath; + + if (!scriptName.empty()) { + uv_fs_t req; + req.ptr = nullptr; + if (0 == uv_fs_realpath(loop, &req, scriptName.c_str(), nullptr)) { + CHECK_NOT_NULL(req.ptr); + scriptPath = std::string(static_cast(req.ptr)); + } + uv_fs_req_cleanup(&req); + } + + return scriptPath; +} + +// UUID RFC: https://www.ietf.org/rfc/rfc4122.txt +// Used ver 4 - with numbers +std::string GenerateID() +{ + uint16_t buffer[8]; + CHECK(CSPRNG(buffer, sizeof(buffer))); + + char uuid[256]; + snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + buffer[0], // time_low + buffer[1], // time_mid + buffer[2], // time_low + (buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version + (buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low + buffer[5], // node + buffer[6], buffer[7]); + return uuid; +} + +class RequestToServer { +public: + RequestToServer(TransportAction action, int sessionId, std::unique_ptr message) + : action(action), sessionId(sessionId), message(std::move(message)) + {} + + void Dispatch(InspectorSocketServer* server) const + { + switch (action) { + case TransportAction::K_KILL: + server->TerminateConnections(); + [[fallthrough]]; + case TransportAction::K_STOP: + server->Stop(); + break; + case TransportAction::K_SEND_MESSAGE: + server->Send(sessionId, StringViewToUtf8(message->string())); + break; + } + } + +private: + TransportAction action; + int sessionId; + std::unique_ptr message; +}; + +class RequestQueue; + +class RequestQueueData { +public: + using MessageQueue = std::deque; + + explicit RequestQueueData(uv_loop_t* loop) : handle(std::make_shared(this)) + { + int err = uv_async_init(loop, &async, [](uv_async_t* async) { + RequestQueueData* wrapper = jsvm::inspector::ContainerOf(&RequestQueueData::async, async); + wrapper->DoDispatch(); + }); + CHECK_EQ(0, err); + } + + static void CloseAndFree(RequestQueueData* queue); + + void Post(int sessionId, TransportAction action, std::unique_ptr message) + { + Mutex::ScopedLock scopedLock(stateLock); + bool notify = messages.empty(); + messages.emplace_back(action, sessionId, std::move(message)); + if (notify) { + CHECK_EQ(0, uv_async_send(&async)); + incomingMessageCond.Broadcast(scopedLock); + } + } + + void Wait() + { + Mutex::ScopedLock scopedLock(stateLock); + if (messages.empty()) { + incomingMessageCond.Wait(scopedLock); + } + } + + void SetServer(InspectorSocketServer* serverParam) + { + server = serverParam; + } + + std::shared_ptr GetHandle() + { + return handle; + } + +private: + ~RequestQueueData() = default; + + MessageQueue GetMessages() + { + Mutex::ScopedLock scopedLock(stateLock); + MessageQueue messagesQ; + messages.swap(messagesQ); + return messagesQ; + } + + void DoDispatch() + { + if (server == nullptr) { + return; + } + for (const auto& request : GetMessages()) { + request.Dispatch(server); + } + } + + std::shared_ptr handle; + uv_async_t async; + InspectorSocketServer* server = nullptr; + MessageQueue messages; + Mutex stateLock; // Locked before mutating the queue. + ConditionVariable incomingMessageCond; +}; + +class RequestQueue { +public: + explicit RequestQueue(RequestQueueData* data) : data(data) {} + + void Reset() + { + Mutex::ScopedLock scopedLock(lock); + data = nullptr; + } + + void Post(int sessionId, TransportAction action, std::unique_ptr message) + { + Mutex::ScopedLock scopedLock(lock); + if (data != nullptr) { + data->Post(sessionId, action, std::move(message)); + } + } + + bool Expired() + { + Mutex::ScopedLock scopedLock(lock); + return data == nullptr; + } + +private: + RequestQueueData* data; + Mutex lock; +}; + +class IoSessionDelegate : public InspectorSessionDelegate { +public: + explicit IoSessionDelegate(std::shared_ptr queue, int id) : requestQueue(queue), id(id) {} + void SendMessageToFrontend(const v8_inspector::StringView& message) override + { + requestQueue->Post(id, TransportAction::K_SEND_MESSAGE, StringBuffer::create(message)); + } + +private: + std::shared_ptr requestQueue; + int id; +}; + +// Passed to InspectorSocketServer to handle WS inspector protocol events, +// mostly session start, message received, and session end. +class InspectorIoDelegate : public jsvm::inspector::SocketServerDelegate { +public: + InspectorIoDelegate(std::shared_ptr queue, + std::shared_ptr mainThread, + const std::string& targetId, + const std::string& scriptPath, + const std::string& scriptName); + ~InspectorIoDelegate() override = default; + + void StartSession(int sessionId, const std::string& inTargetId) override; + void MessageReceived(int sessionId, const std::string& message) override; + void EndSession(int sessionId) override; + + std::vector GetTargetIds() override; + std::string GetTargetTitle(const std::string& id) override; + std::string GetTargetUrl(const std::string& id) override; + void AssignServer(InspectorSocketServer* server) override + { + requestQueue->SetServer(server); + } + +private: + std::shared_ptr requestQueue; + std::shared_ptr mainThread; + std::unordered_map> sessions; + const std::string scriptName; + const std::string scriptPath; + const std::string targetId; +}; + +InspectorIoDelegate::InspectorIoDelegate(std::shared_ptr queue, + std::shared_ptr mainThread, + const std::string& targetId, + const std::string& scriptPath, + const std::string& scriptName) + : requestQueue(queue), mainThread(mainThread), scriptName(scriptName), scriptPath(scriptPath), targetId(targetId) +{} + +void InspectorIoDelegate::StartSession(int sessionId, const std::string& inTargetId) +{ + auto session = mainThread->Connect( + std::unique_ptr(new IoSessionDelegate(requestQueue->GetHandle(), sessionId)), true); + if (session) { + sessions[sessionId] = std::move(session); + fprintf(stderr, "Debugger attached.\n"); + } +} + +void InspectorIoDelegate::MessageReceived(int sessionId, const std::string& message) +{ + auto session = sessions.find(sessionId); + if (session != sessions.end()) { + session->second->Dispatch(Utf8ToStringView(message)->string()); + } +} + +void InspectorIoDelegate::EndSession(int sessionId) +{ + sessions.erase(sessionId); +} + +std::vector InspectorIoDelegate::GetTargetIds() +{ + return { targetId }; +} + +std::string InspectorIoDelegate::GetTargetTitle(const std::string& id) +{ + return scriptName.empty() ? GetHumanReadableProcessName() : scriptName; +} + +std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) +{ + return "file://" + scriptPath; +} + +// static +void RequestQueueData::CloseAndFree(RequestQueueData* queue) +{ + queue->handle->Reset(); + queue->handle.reset(); + uv_close(reinterpret_cast(&queue->async), [](uv_handle_t* handle) { + uv_async_t* async = reinterpret_cast(handle); + RequestQueueData* wrapper = jsvm::inspector::ContainerOf(&RequestQueueData::async, async); + delete wrapper; + }); +} +} // namespace + +class InspectorIo { +public: + // Start the inspector agent thread, waiting for it to initialize. + // Returns empty pointer if thread was not started. + static std::unique_ptr Start(std::shared_ptr mainThread, + const std::string& path, + std::shared_ptr> hostPortParam, + const jsvm::InspectPublishUid& inspectPublishUid); + + // Will block till the transport thread shuts down + ~InspectorIo(); + + void StopAcceptingNewConnections(); + std::string GetWsUrl() const; + +private: + InspectorIo(std::shared_ptr handle, + const std::string& path, + std::shared_ptr> hostPortParam, + const jsvm::InspectPublishUid& inspectPublishUid); + + // Wrapper for agent->ThreadMain() + static void ThreadMain(void* io); + + // Runs a uv_loop_t + void ThreadMain(); + + // This is a thread-safe object that will post async tasks. It lives as long + // as an Inspector object lives (almost as long as an Isolate). + std::shared_ptr mainThread; + // Used to post on a frontend interface thread, lives while the server is + // running + std::shared_ptr requestQueue; + std::shared_ptr> hostPort; + jsvm::InspectPublishUid inspectPublishUid; + + // The IO thread runs its own uv_loop to implement the TCP server off + // the main thread. + uv_thread_t thread; + + // For setting up interthread communications + Mutex threadStartLock; + jsvm::ConditionVariable threadStartCondition; + std::string scriptName; + // May be accessed from any thread + const std::string id; +}; + +// static +std::unique_ptr InspectorIo::Start(std::shared_ptr mainThread, + const std::string& path, + std::shared_ptr> hostPortParam, + const InspectPublishUid& inspectPublishUid) +{ + auto io = std::unique_ptr(new InspectorIo(mainThread, path, hostPortParam, inspectPublishUid)); + if (io->requestQueue->Expired()) { // Thread is not running + return nullptr; + } + return io; +} + +InspectorIo::InspectorIo(std::shared_ptr mainThread, + const std::string& path, + std::shared_ptr> hostPortParam, + const InspectPublishUid& inspectPublishUid) + : mainThread(mainThread), hostPort(hostPortParam), inspectPublishUid(inspectPublishUid), thread(), scriptName(path), + id(GenerateID()) +{ + Mutex::ScopedLock scopedLock(threadStartLock); + CHECK_EQ(uv_thread_create(&thread, InspectorIo::ThreadMain, this), 0); + threadStartCondition.Wait(scopedLock); +} + +InspectorIo::~InspectorIo() +{ + requestQueue->Post(0, TransportAction::K_KILL, nullptr); + int err = uv_thread_join(&thread); + CHECK_EQ(err, 0); +} + +void InspectorIo::StopAcceptingNewConnections() +{ + requestQueue->Post(0, TransportAction::K_STOP, nullptr); +} + +// static +void InspectorIo::ThreadMain(void* io) +{ + static_cast(io)->ThreadMain(); +} + +void InspectorIo::ThreadMain() +{ + uv_loop_t loop; + loop.data = nullptr; + int err = uv_loop_init(&loop); + CHECK_EQ(err, 0); + std::shared_ptr queue(new RequestQueueData(&loop), RequestQueueData::CloseAndFree); + std::string scriptPath = ScriptPath(&loop, scriptName); + std::unique_ptr delegate( + new InspectorIoDelegate(queue, mainThread, id, scriptPath, scriptName)); + std::string host; + int port; + int pid; + { + ExclusiveAccess::Scoped scopedHostPort(hostPort); + host = scopedHostPort->GetHost(); + port = scopedHostPort->GetPort(); + pid = scopedHostPort->GetPid(); + } + InspectorSocketServer server(std::move(delegate), &loop, std::move(host), port, inspectPublishUid, stderr, pid); + requestQueue = queue->GetHandle(); + // Its lifetime is now that of the server delegate + queue.reset(); + { + Mutex::ScopedLock scopedLock(threadStartLock); + if (server.Start()) { + ExclusiveAccess::Scoped scopedHostPort(hostPort); + scopedHostPort->SetPort(server.GetPort()); + } + threadStartCondition.Broadcast(scopedLock); + } + uv_run(&loop, UV_RUN_DEFAULT); + CheckedUvLoopClose(&loop); +} + +std::string InspectorIo::GetWsUrl() const +{ + ExclusiveAccess::Scoped scopedHostPort(hostPort); + return FormatWsAddress(scopedHostPort->GetHost(), scopedHostPort->GetPort(), id, true); +} + +namespace { + +using jsvm::inspector::TwoByteValue; + +using v8::Context; +using v8::Function; +using v8::HandleScope; +using v8::Isolate; +using v8::Local; +using v8::Message; +using v8::Object; +using v8::Value; + +using v8_inspector::StringBuffer; +using v8_inspector::StringView; +using v8_inspector::V8Inspector; +using v8_inspector::V8InspectorClient; + +std::unique_ptr ToProtocolString(Isolate* isolate, Local value) +{ + TwoByteValue buffer(isolate, value); + return StringBuffer::create(StringView(*buffer, buffer.GetLength())); +} + +const int CONTEXT_GROUP_ID = 1; + +std::string GetWorkerLabel(Environment* env) +{ + std::ostringstream result; + // TODO: use thread ID as part of worker label. + result << "Worker[" + << "env->thread_id()" + << "]"; + return result.str(); +} + +class ChannelImpl final : public v8_inspector::V8Inspector::Channel { +public: + explicit ChannelImpl(const std::unique_ptr& inspector, + std::unique_ptr delegate, + std::shared_ptr mainThread, + bool preventShutdown) + : delegate(std::move(delegate)), preventShutdown(preventShutdown) + { + session = + inspector->connect(CONTEXT_GROUP_ID, this, StringView(), V8Inspector::ClientTrustLevel::kFullyTrusted); + } + + ~ChannelImpl() = default; + + void dispatchProtocolMessage(const StringView& message) + { + session->dispatchProtocolMessage(message); + } + + void schedulePauseOnNextStatement(const std::string& reason) + { + std::unique_ptr buffer = Utf8ToStringView(reason); + session->schedulePauseOnNextStatement(buffer->string(), buffer->string()); + } + + bool PreventShutdown() + { + return preventShutdown; + } + +private: + void sendResponse(int callId, std::unique_ptr message) override + { + sendMessageToFrontend(message->string()); + } + + void sendNotification(std::unique_ptr message) override + { + sendMessageToFrontend(message->string()); + } + + void flushProtocolNotifications() override {} + + void sendMessageToFrontend(const StringView& message) + { + delegate->SendMessageToFrontend(message); + } + + void sendMessageToFrontend(const std::string& message) + { + sendMessageToFrontend(Utf8ToStringView(message)->string()); + } + + std::unique_ptr delegate; + std::unique_ptr session; + bool preventShutdown; +}; + +class SameThreadInspectorSession : public InspectorSession { +public: + SameThreadInspectorSession(int sessionId, std::shared_ptr client) + : sessionId(sessionId), client(client) + {} + ~SameThreadInspectorSession() override; + void Dispatch(const v8_inspector::StringView& message) override; + +private: + int sessionId; + std::weak_ptr client; +}; + +} // namespace + +class InspectorClient : public V8InspectorClient { +public: + explicit InspectorClient(Environment* env, bool isMain) : env(env), isMain(isMain) + { + client = V8Inspector::create(env->isolate, this); + std::string name = isMain ? GetHumanReadableProcessName() : GetWorkerLabel(env); + ContextInfo info(name); + info.isDefault = true; + contextCreated(env->context(), info); + } + + void runMessageLoopOnPause(int contextGroupId) override + { + waitingForResume = true; + runMessageLoop(); + } + + void waitForSessionsDisconnect() + { + waitingForSessionsDisconnect = true; + runMessageLoop(); + } + + void waitForFrontend() + { + waitingForFrontend = true; + runMessageLoop(); + } + + void maxAsyncCallStackDepthChanged(int depth) override + { + if (waitingForSessionsDisconnect) { + // V8 isolate is mostly done and is only letting Inspector protocol + // clients gather data. + return; + } + } + + void contextCreated(Local context, const ContextInfo& info) + { + auto nameBuffer = Utf8ToStringView(info.name); + auto originBuffer = Utf8ToStringView(info.origin); + std::unique_ptr auxDataBuffer; + + v8_inspector::V8ContextInfo v8info(context, CONTEXT_GROUP_ID, nameBuffer->string()); + v8info.origin = originBuffer->string(); + + if (info.isDefault) { + auxDataBuffer = Utf8ToStringView("{\"isDefault\":true}"); + } else { + auxDataBuffer = Utf8ToStringView("{\"isDefault\":false}"); + } + v8info.auxData = auxDataBuffer->string(); + + client->contextCreated(v8info); + } + + void contextDestroyed(Local context) + { + client->contextDestroyed(context); + } + + void quitMessageLoopOnPause() override + { + waitingForResume = false; + } + + void runIfWaitingForDebugger(int contextGroupId) override + { + waitingForFrontend = false; + } + + int connectFrontend(std::unique_ptr delegate, bool preventShutdown) + { + int sessionId = nextSessionId++; + channels[sessionId] = + std::make_unique(client, std::move(delegate), getThreadHandle(), preventShutdown); + return sessionId; + } + + void disconnectFrontend(int sessionId) + { + auto it = channels.find(sessionId); + if (it == channels.end()) { + return; + } + channels.erase(it); + if (waitingForSessionsDisconnect && !isMain) { + waitingForSessionsDisconnect = false; + } + } + + void dispatchMessageFromFrontend(int sessionId, const StringView& message) + { + channels[sessionId]->dispatchProtocolMessage(message); + } + + Local ensureDefaultContextInGroup(int contextGroupId) override + { + return env->context(); + } + + void ReportUncaughtException(Local error, Local message) + { + Isolate* isolate = env->isolate; + Local context = env->context(); + + int scriptId = message->GetScriptOrigin().ScriptId(); + + Local stackTrace = message->GetStackTrace(); + + if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0 && + scriptId == stackTrace->GetFrame(isolate, 0)->GetScriptId()) { + scriptId = 0; + } + + const uint8_t DETAILS[] = "Uncaught"; + + client->exceptionThrown(context, StringView(DETAILS, sizeof(DETAILS) - 1), error, + ToProtocolString(isolate, message->Get())->string(), + ToProtocolString(isolate, message->GetScriptResourceName())->string(), + message->GetLineNumber(context).FromMaybe(0), + message->GetStartColumn(context).FromMaybe(0), client->createStackTrace(stackTrace), + scriptId); + } + + void startRepeatingTimer(double interval, TimerCallback callback, void* data) override + { + // TODO: implement this for supporting heap profiler. + } + + void cancelTimer(void* data) override + { + // TODO: implement this for supporting heap profiler. + } + + void schedulePauseOnNextStatement(const std::string& reason) + { + for (const auto& idChannel : channels) { + idChannel.second->schedulePauseOnNextStatement(reason); + } + } + + bool hasConnectedSessions() + { + for (const auto& idChannel : channels) { + // Other sessions are "invisible" more most purposes + if (idChannel.second->PreventShutdown()) { + return true; + } + } + return false; + } + + std::shared_ptr getThreadHandle() + { + if (!interface) { + interface = std::make_shared(static_cast(env->GetInspectorAgent())); + } + return interface->GetHandle(); + } + + bool IsActive() + { + return !channels.empty(); + } + +private: + bool shouldRunMessageLoop() + { + if (waitingForFrontend) { + return true; + } + if (waitingForSessionsDisconnect || waitingForResume) { + return hasConnectedSessions(); + } + return false; + } + + void runMessageLoop() + { + if (runningNestedLoop) { + return; + } + + runningNestedLoop = true; + + while (shouldRunMessageLoop()) { + if (interface) { + interface->WaitForFrontendEvent(); + } + env->RunAndClearInterrupts(); + } + runningNestedLoop = false; + } + + double currentTimeMS() override + { + return env->platform()->CurrentClockTimeMillis(); + } + + Environment* env; + bool isMain; + bool runningNestedLoop = false; + std::unique_ptr client; + std::unordered_map> channels; + int nextSessionId = 1; + bool waitingForResume = false; + bool waitingForFrontend = false; + bool waitingForSessionsDisconnect = false; + // Allows accessing Inspector from non-main threads + std::shared_ptr interface; +}; + +Agent::Agent(Environment* env) : parentEnv(env) {} + +Agent::~Agent() = default; + +bool Agent::Start(const std::string& pathParam, + std::shared_ptr> hostPortParam, + bool isMain, + bool waitForConnect) +{ + path = pathParam; + CHECK_NOT_NULL(hostPortParam); + hostPort = hostPortParam; + + client = std::make_shared(parentEnv, isMain); + + if (!StartIoThread()) { + return false; + } + + if (waitForConnect) { + client->waitForFrontend(); + } + return true; +} + +int FindAvailablePort() +{ + constexpr int startPort = 9229; + constexpr int endPort = 9999; + constexpr int invalidPort = -1; + int sockfd = -1; + + for (auto port = startPort; port <= endPort; ++port) { + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + continue; + } + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + + if (bind(sockfd, reinterpret_cast(&addr), sizeof(addr)) < 0) { + close(sockfd); + if (errno == EADDRINUSE) { + continue; + } else { + break; + } + } + close(sockfd); + return port; + } + return invalidPort; +} + +bool Agent::Start(const std::string& pathParam, int pid) +{ + int port = FindAvailablePort(); + if (port < 0) { + return false; + } + auto hostPort = std::make_shared>("localhost", port, pid); + return Start(pathParam, hostPort, true, false); +} + +bool Agent::StartIoThread() +{ + if (io != nullptr) { + return true; + } + + if (!client) { + return false; + } + + CHECK_NOT_NULL(client); + + io = InspectorIo::Start(client->getThreadHandle(), path, hostPort, { false, true }); + if (io == nullptr) { + return false; + } + return true; +} + +void Agent::Stop() +{ + io.reset(); +} + +std::unique_ptr Agent::Connect(std::unique_ptr delegate, + bool preventShutdown) +{ + if (!client) { + return std::unique_ptr {}; + } + + CHECK_NOT_NULL(client); + + int sessionId = client->connectFrontend(std::move(delegate), preventShutdown); + return std::unique_ptr(new SameThreadInspectorSession(sessionId, client)); +} + +void Agent::WaitForDisconnect() +{ + CHECK_NOT_NULL(client); + if (client->hasConnectedSessions()) { + fprintf(stderr, "Waiting for the debugger to disconnect...\n"); + fflush(stderr); + } + + // TODO: if client->notifyWaitingForDisconnect() + client->contextDestroyed(parentEnv->context()); + + if (io != nullptr) { + io->StopAcceptingNewConnections(); + client->waitForSessionsDisconnect(); + } +} + +void Agent::PauseOnNextJavascriptStatement(const std::string& reason) +{ + client->schedulePauseOnNextStatement(reason); +} + +bool Agent::IsActive() +{ + if (client == nullptr) { + return false; + } + return io != nullptr || client->IsActive(); +} + +void Agent::WaitForConnect() +{ + CHECK_NOT_NULL(client); + client->waitForFrontend(); +} + +SameThreadInspectorSession::~SameThreadInspectorSession() +{ + auto clientLock = client.lock(); + if (clientLock) { + clientLock->disconnectFrontend(sessionId); + } +} + +void SameThreadInspectorSession::Dispatch(const v8_inspector::StringView& message) +{ + auto clientLock = client.lock(); + if (clientLock) { + clientLock->dispatchMessageFromFrontend(sessionId, message); + } +} + +} // namespace v8impl + +jsvm::InspectorAgent* jsvm::InspectorAgent::New(JSVM_Env env) +{ + return new v8impl::Agent(env); +} \ No newline at end of file diff --git a/src/inspector/js_native_api_v8_inspector.h b/src/inspector/js_native_api_v8_inspector.h new file mode 100644 index 0000000..238b4bb --- /dev/null +++ b/src/inspector/js_native_api_v8_inspector.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JS_NATIVE_API_V8_INSPECTOR_H +#define JS_NATIVE_API_V8_INSPECTOR_H + +#include +#include + +#include "js_native_api_v8.h" +#include "jsvm_host_port.h" +#include "jsvm_inspector_agent.h" +#include "v8.h" + +#ifndef ENABLE_INSPECTOR +#error("This header can only be used when inspector is enabled") +#endif + +namespace v8_inspector { +class StringView; +} // namespace v8_inspector + +namespace jsvm { +namespace inspector { +class InspectorSessionDelegate; +class InspectorSession; + +class InspectorSession { +public: + virtual ~InspectorSession() = default; + virtual void Dispatch(const v8_inspector::StringView& message) = 0; +}; + +class InspectorSessionDelegate { +public: + virtual ~InspectorSessionDelegate() = default; + virtual void SendMessageToFrontend(const v8_inspector::StringView& message) = 0; +}; +} // namespace inspector +} // namespace jsvm + +namespace v8impl { + +using jsvm::ExclusiveAccess; +using jsvm::HostPort; +using jsvm::inspector::InspectorSession; +using jsvm::inspector::InspectorSessionDelegate; + +class IsolateData; +using Environment = JSVM_Env__; + +class InspectorClient; +class InspectorIo; + +struct ContextInfo { + explicit ContextInfo(const std::string& name) : name(name) {} + const std::string name; + std::string origin; + bool isDefault = false; +}; + +class Agent : public jsvm::InspectorAgent { +public: + explicit Agent(Environment* env); + ~Agent(); + +public: + bool Start(const std::string& pathParam, const std::string& hostName, int port, int pid = -1) override + { + auto hostPort = std::make_shared>(hostName, port, pid); + return Start(pathParam, hostPort, true, false); + } + + bool Start(const std::string& pathParam, int pid) override; + + // Stop and destroy io_ + void Stop() override; + + // Returns true if the inspector is actually in use. + bool IsActive() override; + + // Blocks till frontend connects and sends "runIfWaitingForDebugger" + void WaitForConnect() override; + + // Blocks till all the sessions with "WaitForDisconnectOnShutdown" disconnect + void WaitForDisconnect() override; + + void PauseOnNextJavascriptStatement(const std::string& reason) override; + +public: + // Called to create inspector sessions that can be used from the same thread. + // The inspector responds by using the delegate to send messages back. + std::unique_ptr Connect(std::unique_ptr delegate, bool preventShutdown); + + // Can only be called from the main thread. + bool StartIoThread(); + + std::shared_ptr> GetHostPort() + { + return hostPort; + } + + inline Environment* env() const + { + return parentEnv; + } + +private: + // Create client_, may create io if option enabled + bool Start(const std::string& pathParam, + std::shared_ptr> hostPortParam, + bool isMain, + bool waitForConnect); + + Environment* parentEnv; + // Encapsulates majority of the Inspector functionality + std::shared_ptr client; + // Interface for transports, e.g. WebSocket server + std::unique_ptr io; + std::string path; + + std::shared_ptr> hostPort; +}; + +} // namespace v8impl + +#endif \ No newline at end of file -- Gitee From b64e597ca508e670d8083e6178ad301fea795d24 Mon Sep 17 00:00:00 2001 From: liuweili Date: Sat, 11 Jan 2025 15:33:18 +0800 Subject: [PATCH 2/6] fix code check Signed-off-by: liuweili --- src/inspector/js_native_api_v8_inspector.cpp | 167 ++++++++++--------- 1 file changed, 84 insertions(+), 83 deletions(-) diff --git a/src/inspector/js_native_api_v8_inspector.cpp b/src/inspector/js_native_api_v8_inspector.cpp index 4b982a7..2f562e7 100644 --- a/src/inspector/js_native_api_v8_inspector.cpp +++ b/src/inspector/js_native_api_v8_inspector.cpp @@ -35,6 +35,7 @@ #include #include #include +#include namespace v8impl { @@ -96,7 +97,7 @@ public: void Post(std::unique_ptr request); bool WaitForFrontendEvent(); std::shared_ptr GetHandle(); - Agent* inspector_agent() + Agent* InspectorAgent() { return agent; } @@ -137,7 +138,7 @@ private: template std::unique_ptr WrapInDeletable(std::unique_ptr object) { - return std::unique_ptr>(new DeletableWrapper(std::move(object))); + return std::make_unique>(std::move(object)); } template @@ -158,7 +159,7 @@ private: template std::unique_ptr NewCreateRequest(int objectId, Factory factory) { - return std::unique_ptr(new CreateObjectRequest(objectId, std::move(factory))); + return std::make_unique>(objectId, std::move(factory)); } class DeleteRequest : public Request { @@ -214,7 +215,7 @@ public: void Call(Fn fn) const { using Request = CallRequest; - thread->Post(std::unique_ptr(new Request(objectId, std::move(fn)))); + thread->Post(std::make_unique(objectId, std::move(fn))); } template @@ -249,7 +250,7 @@ public: void Connect(std::unique_ptr delegate) { - Agent* agent = thread->inspector_agent(); + Agent* agent = thread->InspectorAgent(); if (agent != nullptr) { session = agent->Connect(std::move(delegate), preventShutdown); } @@ -466,14 +467,15 @@ std::string GenerateID() CHECK(CSPRNG(buffer, sizeof(buffer))); char uuid[256]; - snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", - buffer[0], // time_low - buffer[1], // time_mid - buffer[2], // time_low - (buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version - (buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low - buffer[5], // node - buffer[6], buffer[7]); + int ret = snprintf_s(uuid, sizeof(uuid), sizeof(uuid) - 1, "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + buffer[0], // time_low + buffer[1], // time_mid + buffer[2], // time_low + (buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version + (buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low + buffer[5], // node + buffer[6], buffer[7]); + CHECK(ret >= 0); return uuid; } @@ -664,10 +666,12 @@ InspectorIoDelegate::InspectorIoDelegate(std::shared_ptr queue void InspectorIoDelegate::StartSession(int sessionId, const std::string& inTargetId) { auto session = mainThread->Connect( - std::unique_ptr(new IoSessionDelegate(requestQueue->GetHandle(), sessionId)), true); + std::make_unique(requestQueue->GetHandle(), sessionId), true); if (session) { sessions[sessionId] = std::move(session); - fprintf(stderr, "Debugger attached.\n"); + if (fprintf(stderr, "Debugger attached.\n") < 0) { + return; + } } } @@ -766,7 +770,7 @@ std::unique_ptr InspectorIo::Start(std::shared_ptr> hostPortParam, const InspectPublishUid& inspectPublishUid) { - auto io = std::unique_ptr(new InspectorIo(mainThread, path, hostPortParam, inspectPublishUid)); + auto io = std::make_unique(mainThread, path, hostPortParam, inspectPublishUid); if (io->requestQueue->Expired()) { // Thread is not running return nullptr; } @@ -809,7 +813,8 @@ void InspectorIo::ThreadMain() loop.data = nullptr; int err = uv_loop_init(&loop); CHECK_EQ(err, 0); - std::shared_ptr queue(new RequestQueueData(&loop), RequestQueueData::CloseAndFree); + std::shared_ptr queue = + std::make_shared(&loop, RequestQueueData::CloseAndFree); std::string scriptPath = ScriptPath(&loop, scriptName); std::unique_ptr delegate( new InspectorIoDelegate(queue, mainThread, id, scriptPath, scriptName)); @@ -873,7 +878,6 @@ const int CONTEXT_GROUP_ID = 1; std::string GetWorkerLabel(Environment* env) { std::ostringstream result; - // TODO: use thread ID as part of worker label. result << "Worker[" << "env->thread_id()" << "]"; @@ -894,15 +898,15 @@ public: ~ChannelImpl() = default; - void dispatchProtocolMessage(const StringView& message) + void DispatchProtocolMessage(const StringView& message) { - session->dispatchProtocolMessage(message); + session->DispatchProtocolMessage(message); } - void schedulePauseOnNextStatement(const std::string& reason) + void SchedulePauseOnNextStatement(const std::string& reason) { std::unique_ptr buffer = Utf8ToStringView(reason); - session->schedulePauseOnNextStatement(buffer->string(), buffer->string()); + session->SchedulePauseOnNextStatement(buffer->string(), buffer->string()); } bool PreventShutdown() @@ -911,26 +915,26 @@ public: } private: - void sendResponse(int callId, std::unique_ptr message) override + void SendResponse(int callId, std::unique_ptr message) override { - sendMessageToFrontend(message->string()); + SendMessageToFrontend(message->string()); } - void sendNotification(std::unique_ptr message) override + void SendNotification(std::unique_ptr message) override { - sendMessageToFrontend(message->string()); + SendMessageToFrontend(message->string()); } - void flushProtocolNotifications() override {} + void FlushProtocolNotifications() override {} - void sendMessageToFrontend(const StringView& message) + void SendMessageToFrontend(const StringView& message) { delegate->SendMessageToFrontend(message); } - void sendMessageToFrontend(const std::string& message) + void SendMessageToFrontend(const std::string& message) { - sendMessageToFrontend(Utf8ToStringView(message)->string()); + SendMessageToFrontend(Utf8ToStringView(message)->string()); } std::unique_ptr delegate; @@ -961,28 +965,28 @@ public: std::string name = isMain ? GetHumanReadableProcessName() : GetWorkerLabel(env); ContextInfo info(name); info.isDefault = true; - contextCreated(env->context(), info); + ContextCreated(env->context(), info); } - void runMessageLoopOnPause(int contextGroupId) override + void RunMessageLoopOnPause(int contextGroupId) override { waitingForResume = true; - runMessageLoop(); + RunMessageLoop(); } - void waitForSessionsDisconnect() + void WaitForSessionsDisconnect() { waitingForSessionsDisconnect = true; - runMessageLoop(); + RunMessageLoop(); } - void waitForFrontend() + void WaitForFrontend() { waitingForFrontend = true; - runMessageLoop(); + RunMessageLoop(); } - void maxAsyncCallStackDepthChanged(int depth) override + void MaxAsyncCallStackDepthChanged(int depth) override { if (waitingForSessionsDisconnect) { // V8 isolate is mostly done and is only letting Inspector protocol @@ -991,7 +995,7 @@ public: } } - void contextCreated(Local context, const ContextInfo& info) + void ContextCreated(Local context, const ContextInfo& info) { auto nameBuffer = Utf8ToStringView(info.name); auto originBuffer = Utf8ToStringView(info.origin); @@ -1007,33 +1011,33 @@ public: } v8info.auxData = auxDataBuffer->string(); - client->contextCreated(v8info); + client->ContextCreated(v8info); } - void contextDestroyed(Local context) + void ContextDestroyed(Local context) { - client->contextDestroyed(context); + client->ContextDestroyed(context); } - void quitMessageLoopOnPause() override + void QuitMessageLoopOnPause() override { waitingForResume = false; } - void runIfWaitingForDebugger(int contextGroupId) override + void RunIfWaitingForDebugger(int contextGroupId) override { waitingForFrontend = false; } - int connectFrontend(std::unique_ptr delegate, bool preventShutdown) + int ConnectFrontend(std::unique_ptr delegate, bool preventShutdown) { int sessionId = nextSessionId++; channels[sessionId] = - std::make_unique(client, std::move(delegate), getThreadHandle(), preventShutdown); + std::make_unique(client, std::move(delegate), GetThreadHandle(), preventShutdown); return sessionId; } - void disconnectFrontend(int sessionId) + void DisconnectFrontend(int sessionId) { auto it = channels.find(sessionId); if (it == channels.end()) { @@ -1045,12 +1049,12 @@ public: } } - void dispatchMessageFromFrontend(int sessionId, const StringView& message) + void DispatchMessageFromFrontend(int sessionId, const StringView& message) { - channels[sessionId]->dispatchProtocolMessage(message); + channels[sessionId]->DispatchProtocolMessage(message); } - Local ensureDefaultContextInGroup(int contextGroupId) override + Local EnsureDefaultContextInGroup(int contextGroupId) override { return env->context(); } @@ -1069,9 +1073,9 @@ public: scriptId = 0; } - const uint8_t DETAILS[] = "Uncaught"; + const uint8_t details[] = "Uncaught"; - client->exceptionThrown(context, StringView(DETAILS, sizeof(DETAILS) - 1), error, + client->exceptionThrown(context, StringView(details, sizeof(details) - 1), error, ToProtocolString(isolate, message->Get())->string(), ToProtocolString(isolate, message->GetScriptResourceName())->string(), message->GetLineNumber(context).FromMaybe(0), @@ -1079,24 +1083,18 @@ public: scriptId); } - void startRepeatingTimer(double interval, TimerCallback callback, void* data) override - { - // TODO: implement this for supporting heap profiler. - } + void StartRepeatingTimer(double interval, TimerCallback callback, void* data) override {} - void cancelTimer(void* data) override - { - // TODO: implement this for supporting heap profiler. - } + void CancelTimer(void* data) override {} - void schedulePauseOnNextStatement(const std::string& reason) + void SchedulePauseOnNextStatement(const std::string& reason) { for (const auto& idChannel : channels) { - idChannel.second->schedulePauseOnNextStatement(reason); + idChannel.second->SchedulePauseOnNextStatement(reason); } } - bool hasConnectedSessions() + bool HasConnectedSessions() { for (const auto& idChannel : channels) { // Other sessions are "invisible" more most purposes @@ -1107,7 +1105,7 @@ public: return false; } - std::shared_ptr getThreadHandle() + std::shared_ptr GetThreadHandle() { if (!interface) { interface = std::make_shared(static_cast(env->GetInspectorAgent())); @@ -1121,18 +1119,18 @@ public: } private: - bool shouldRunMessageLoop() + bool ShouldRunMessageLoop() { if (waitingForFrontend) { return true; } if (waitingForSessionsDisconnect || waitingForResume) { - return hasConnectedSessions(); + return HasConnectedSessions(); } return false; } - void runMessageLoop() + void RunMessageLoop() { if (runningNestedLoop) { return; @@ -1140,7 +1138,7 @@ private: runningNestedLoop = true; - while (shouldRunMessageLoop()) { + while (ShouldRunMessageLoop()) { if (interface) { interface->WaitForFrontendEvent(); } @@ -1149,7 +1147,7 @@ private: runningNestedLoop = false; } - double currentTimeMS() override + double CurrentTimeMS() override { return env->platform()->CurrentClockTimeMillis(); } @@ -1187,7 +1185,7 @@ bool Agent::Start(const std::string& pathParam, } if (waitForConnect) { - client->waitForFrontend(); + client->WaitForFrontend(); } return true; } @@ -1245,7 +1243,7 @@ bool Agent::StartIoThread() CHECK_NOT_NULL(client); - io = InspectorIo::Start(client->getThreadHandle(), path, hostPort, { false, true }); + io = InspectorIo::Start(client->GetThreadHandle(), path, hostPort, { false, true }); if (io == nullptr) { return false; } @@ -1267,29 +1265,32 @@ std::unique_ptr Agent::Connect(std::unique_ptrconnectFrontend(std::move(delegate), preventShutdown); - return std::unique_ptr(new SameThreadInspectorSession(sessionId, client)); + return std::make_unique(sessionId, client); } void Agent::WaitForDisconnect() { CHECK_NOT_NULL(client); - if (client->hasConnectedSessions()) { - fprintf(stderr, "Waiting for the debugger to disconnect...\n"); - fflush(stderr); + if (client->HasConnectedSessions()) { + if (fprintf(stderr, "Waiting for the debugger to disconnect...\n") < 0) { + return; + } + if (fflush(stderr) != 0) { + return; + } } - // TODO: if client->notifyWaitingForDisconnect() - client->contextDestroyed(parentEnv->context()); + client->ContextDestroyed(parentEnv->context()); if (io != nullptr) { io->StopAcceptingNewConnections(); - client->waitForSessionsDisconnect(); + client->WaitForSessionsDisconnect(); } } void Agent::PauseOnNextJavascriptStatement(const std::string& reason) { - client->schedulePauseOnNextStatement(reason); + client->SchedulePauseOnNextStatement(reason); } bool Agent::IsActive() @@ -1303,14 +1304,14 @@ bool Agent::IsActive() void Agent::WaitForConnect() { CHECK_NOT_NULL(client); - client->waitForFrontend(); + client->WaitForFrontend(); } SameThreadInspectorSession::~SameThreadInspectorSession() { auto clientLock = client.lock(); if (clientLock) { - clientLock->disconnectFrontend(sessionId); + clientLock->DisconnectFrontend(sessionId); } } @@ -1318,7 +1319,7 @@ void SameThreadInspectorSession::Dispatch(const v8_inspector::StringView& messag { auto clientLock = client.lock(); if (clientLock) { - clientLock->dispatchMessageFromFrontend(sessionId, message); + clientLock->DispatchMessageFromFrontend(sessionId, message); } } @@ -1327,4 +1328,4 @@ void SameThreadInspectorSession::Dispatch(const v8_inspector::StringView& messag jsvm::InspectorAgent* jsvm::InspectorAgent::New(JSVM_Env env) { return new v8impl::Agent(env); -} \ No newline at end of file +} -- Gitee From 36de4d9e744add8901b1d9d91849b5eec00daa75 Mon Sep 17 00:00:00 2001 From: wangyimin Date: Sat, 11 Jan 2025 18:55:35 +0800 Subject: [PATCH 3/6] fix compile error Signed-off-by: wangyimin --- src/inspector/js_native_api_v8_inspector.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/inspector/js_native_api_v8_inspector.cpp b/src/inspector/js_native_api_v8_inspector.cpp index 2f562e7..21a13c0 100644 --- a/src/inspector/js_native_api_v8_inspector.cpp +++ b/src/inspector/js_native_api_v8_inspector.cpp @@ -32,10 +32,10 @@ #include #include +#include #include #include #include -#include namespace v8impl { @@ -665,8 +665,7 @@ InspectorIoDelegate::InspectorIoDelegate(std::shared_ptr queue void InspectorIoDelegate::StartSession(int sessionId, const std::string& inTargetId) { - auto session = mainThread->Connect( - std::make_unique(requestQueue->GetHandle(), sessionId), true); + auto session = mainThread->Connect(std::make_unique(requestQueue->GetHandle(), sessionId), true); if (session) { sessions[sessionId] = std::move(session); if (fprintf(stderr, "Debugger attached.\n") < 0) { @@ -770,7 +769,7 @@ std::unique_ptr InspectorIo::Start(std::shared_ptr> hostPortParam, const InspectPublishUid& inspectPublishUid) { - auto io = std::make_unique(mainThread, path, hostPortParam, inspectPublishUid); + auto io = std::unique_ptr(new InspectorIo(mainThread, path, hostPortParam, inspectPublishUid)); if (io->requestQueue->Expired()) { // Thread is not running return nullptr; } @@ -813,8 +812,7 @@ void InspectorIo::ThreadMain() loop.data = nullptr; int err = uv_loop_init(&loop); CHECK_EQ(err, 0); - std::shared_ptr queue = - std::make_shared(&loop, RequestQueueData::CloseAndFree); + std::shared_ptr queue(new RequestQueueData(&loop), RequestQueueData::CloseAndFree); std::string scriptPath = ScriptPath(&loop, scriptName); std::unique_ptr delegate( new InspectorIoDelegate(queue, mainThread, id, scriptPath, scriptName)); @@ -900,7 +898,7 @@ public: void DispatchProtocolMessage(const StringView& message) { - session->DispatchProtocolMessage(message); + session->dispatchProtocolMessage(message); } void SchedulePauseOnNextStatement(const std::string& reason) @@ -1011,12 +1009,12 @@ public: } v8info.auxData = auxDataBuffer->string(); - client->ContextCreated(v8info); + client->contextCreated(v8info); } void ContextDestroyed(Local context) { - client->ContextDestroyed(context); + client->contextDestroyed(context); } void QuitMessageLoopOnPause() override @@ -1264,7 +1262,7 @@ std::unique_ptr Agent::Connect(std::unique_ptrconnectFrontend(std::move(delegate), preventShutdown); + int sessionId = client->ConnectFrontend(std::move(delegate), preventShutdown); return std::make_unique(sessionId, client); } -- Gitee From e07699ba8582024a73529602bd21e8b2c9ebcc42 Mon Sep 17 00:00:00 2001 From: liuweili Date: Sat, 11 Jan 2025 17:56:55 +0800 Subject: [PATCH 4/6] fix codecheck Signed-off-by: liuweili --- src/inspector/js_native_api_v8_inspector.cpp | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/inspector/js_native_api_v8_inspector.cpp b/src/inspector/js_native_api_v8_inspector.cpp index 21a13c0..e4e714c 100644 --- a/src/inspector/js_native_api_v8_inspector.cpp +++ b/src/inspector/js_native_api_v8_inspector.cpp @@ -913,24 +913,24 @@ public: } private: - void SendResponse(int callId, std::unique_ptr message) override + void sendResponse(int callId, std::unique_ptr message) override { SendMessageToFrontend(message->string()); } - void SendNotification(std::unique_ptr message) override + void sendNotification(std::unique_ptr message) override { SendMessageToFrontend(message->string()); } - void FlushProtocolNotifications() override {} + void flushProtocolNotifications() override {} void SendMessageToFrontend(const StringView& message) { delegate->SendMessageToFrontend(message); } - void SendMessageToFrontend(const std::string& message) + void sendMessageToFrontend(const std::string& message) { SendMessageToFrontend(Utf8ToStringView(message)->string()); } @@ -966,7 +966,7 @@ public: ContextCreated(env->context(), info); } - void RunMessageLoopOnPause(int contextGroupId) override + void runMessageLoopOnPause(int contextGroupId) override { waitingForResume = true; RunMessageLoop(); @@ -984,7 +984,7 @@ public: RunMessageLoop(); } - void MaxAsyncCallStackDepthChanged(int depth) override + void maxAsyncCallStackDepthChanged(int depth) override { if (waitingForSessionsDisconnect) { // V8 isolate is mostly done and is only letting Inspector protocol @@ -1017,12 +1017,12 @@ public: client->contextDestroyed(context); } - void QuitMessageLoopOnPause() override + void quitMessageLoopOnPause() override { waitingForResume = false; } - void RunIfWaitingForDebugger(int contextGroupId) override + void runIfWaitingForDebugger(int contextGroupId) override { waitingForFrontend = false; } @@ -1052,7 +1052,7 @@ public: channels[sessionId]->DispatchProtocolMessage(message); } - Local EnsureDefaultContextInGroup(int contextGroupId) override + Local ensureDefaultContextInGroup(int contextGroupId) override { return env->context(); } @@ -1081,9 +1081,9 @@ public: scriptId); } - void StartRepeatingTimer(double interval, TimerCallback callback, void* data) override {} + void startRepeatingTimer(double interval, TimerCallback callback, void* data) override {} - void CancelTimer(void* data) override {} + void cancelTimer(void* data) override {} void SchedulePauseOnNextStatement(const std::string& reason) { @@ -1145,7 +1145,7 @@ private: runningNestedLoop = false; } - double CurrentTimeMS() override + double currentTimeMS() override { return env->platform()->CurrentClockTimeMillis(); } -- Gitee From 2665af9c3db45e67a5cff8f36c79db18f9b5f47e Mon Sep 17 00:00:00 2001 From: wangyimin Date: Sat, 11 Jan 2025 19:04:41 +0800 Subject: [PATCH 5/6] fix compile error Signed-off-by: wangyimin --- src/inspector/js_native_api_v8_inspector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inspector/js_native_api_v8_inspector.cpp b/src/inspector/js_native_api_v8_inspector.cpp index e4e714c..0b077e3 100644 --- a/src/inspector/js_native_api_v8_inspector.cpp +++ b/src/inspector/js_native_api_v8_inspector.cpp @@ -904,7 +904,7 @@ public: void SchedulePauseOnNextStatement(const std::string& reason) { std::unique_ptr buffer = Utf8ToStringView(reason); - session->SchedulePauseOnNextStatement(buffer->string(), buffer->string()); + session->schedulePauseOnNextStatement(buffer->string(), buffer->string()); } bool PreventShutdown() -- Gitee From cd5a29ae86144424067b273a8166d8292e0062b0 Mon Sep 17 00:00:00 2001 From: wangyimin Date: Sat, 11 Jan 2025 19:08:01 +0800 Subject: [PATCH 6/6] fix codecheck Signed-off-by: wangyimin --- src/inspector/js_native_api_v8_inspector.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/inspector/js_native_api_v8_inspector.cpp b/src/inspector/js_native_api_v8_inspector.cpp index 0b077e3..215a2ae 100644 --- a/src/inspector/js_native_api_v8_inspector.cpp +++ b/src/inspector/js_native_api_v8_inspector.cpp @@ -203,8 +203,11 @@ public: { thread->Post(NewCreateRequest(objectId, std::move(factory))); } + AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete; + AnotherThreadObjectReference& operator=(const AnotherThreadObjectReference&) = delete; + ~AnotherThreadObjectReference() { // Disappearing thread may cause a memory leak -- Gitee