diff --git a/inspector/inspector.cpp b/inspector/inspector.cpp index 8f8359e..28fc643 100644 --- a/inspector/inspector.cpp +++ b/inspector/inspector.cpp @@ -14,3 +14,256 @@ */ #include "inspector/inspector.h" + +#include +#include +#include +#include + +#include "base/log/log.h" +#include "inspector/ws_server.h" + +namespace OHOS::ArkCompiler::Toolchain { +namespace { +enum DispatchStatus : int32_t { + UNKNOWN = 0, + DISPATCHING, + DISPATCHED +}; + +using InitializeDebugger = void(*)(void*, const std::function&); +using UninitializeDebugger = void(*)(void*); +using WaitForDebugger = void(*)(void*); +using DispatchMessage = void(*)(void*, std::string&&); +using ProcessMessage = void(*)(void*); +using GetDispatchStatus = int32_t(*)(void*); + +DispatchMessage g_dispatchMessage = nullptr; +InitializeDebugger g_initializeDebugger = nullptr; +UninitializeDebugger g_uninitializeDebugger = nullptr; +WaitForDebugger g_waitForDebugger = nullptr; +ProcessMessage g_processMessage = nullptr; +GetDispatchStatus g_getDispatchStatus = nullptr; + +std::atomic g_hasArkFuncsInited = false; +std::unordered_map g_inspectors; +std::shared_mutex g_mutex; + +thread_local void* g_handle = nullptr; +thread_local void* g_vm = nullptr; + +constexpr char ARK_DEBUGGER_SHARED_LIB[] = "libark_ecma_debugger.so"; + +void* HandleClient(void* const server) +{ + LOGI("HandleClient"); + if (server == nullptr) { + LOGE("HandleClient server nullptr"); + return nullptr; + } + static_cast(server)->RunServer(); + return nullptr; +} + +bool LoadArkDebuggerLibrary() +{ + if (g_handle != nullptr) { + LOGE("Already opened"); + return false; + } + g_handle = dlopen(ARK_DEBUGGER_SHARED_LIB, RTLD_LAZY); + if (g_handle == nullptr) { + LOGE("Failed to open %{public}s, reason: %{public}sn", ARK_DEBUGGER_SHARED_LIB, dlerror()); + return false; + } + return true; +} + +void* GetArkDynFunction(const char* symbol) +{ + auto function = dlsym(g_handle, symbol); + if (function == nullptr) { + LOGE("Failed to get symbol %{public}s in %{public}s", symbol, ARK_DEBUGGER_SHARED_LIB); + } + return function; +} + +void SendReply(const void* vm, const std::string& message) +{ + std::shared_lock lock(g_mutex); + auto iter = g_inspectors.find(vm); + if (iter != g_inspectors.end() && iter->second != nullptr && + iter->second->websocketServer_ != nullptr) { + iter->second->websocketServer_->SendReply(message); + } +} + +void ResetServiceLocked() +{ + auto iter = g_inspectors.find(g_vm); + if (iter != g_inspectors.end() && iter->second != nullptr && + iter->second->websocketServer_ != nullptr) { + iter->second->websocketServer_->StopServer(); + delete iter->second; + iter->second = nullptr; + g_inspectors.erase(iter); + } + if (g_handle != nullptr) { + dlclose(g_handle); + g_handle = nullptr; + } +} + +bool InitializeInspector(void* vm, const std::string& componentName, int32_t instanceId, + const DebuggerPostTask& debuggerPostTask) +{ + std::unique_lock lock(g_mutex); + auto iter = g_inspectors.find(vm); + if (iter != g_inspectors.end()) { + LOGE("Already have the same vm in the map"); + return false; + } + + Inspector *newInspector = new Inspector(); + if (!g_inspectors.emplace(vm, newInspector).second) { + delete newInspector; + return false; + } + + newInspector->tid_ = pthread_self(); + newInspector->vm_ = vm; + newInspector->debuggerPostTask_ = debuggerPostTask; + newInspector->websocketServer_ = std::make_unique(componentName, + std::bind(&Inspector::OnMessage, newInspector, std::placeholders::_1), instanceId); + + pthread_t tid; + if (pthread_create(&tid, nullptr, &HandleClient, static_cast( + newInspector->websocketServer_.get())) != 0) { + LOGE("Create inspector thread failed"); + return false; + } + + return true; +} + +bool InitializeArkFunctions() +{ + // no need to initialize again in case of multi-instance + if (g_hasArkFuncsInited) { + return true; + } + + std::unique_lock lock(g_mutex); + if (g_hasArkFuncsInited) { + return true; + } + g_initializeDebugger = reinterpret_cast( + GetArkDynFunction("InitializeDebugger")); + if (g_initializeDebugger == nullptr) { + ResetServiceLocked(); + return false; + } + g_uninitializeDebugger = reinterpret_cast( + GetArkDynFunction("UninitializeDebugger")); + if (g_uninitializeDebugger == nullptr) { + ResetServiceLocked(); + return false; + } + g_waitForDebugger = reinterpret_cast( + GetArkDynFunction("WaitForDebugger")); + if (g_waitForDebugger == nullptr) { + ResetServiceLocked(); + return false; + } + g_dispatchMessage = reinterpret_cast( + GetArkDynFunction("DispatchMessage")); + if (g_dispatchMessage == nullptr) { + ResetServiceLocked(); + return false; + } + g_getDispatchStatus = reinterpret_cast( + GetArkDynFunction("GetDispatchStatus")); + if (g_getDispatchStatus == nullptr) { + ResetServiceLocked(); + return false; + } + g_processMessage = reinterpret_cast( + GetArkDynFunction("ProcessMessage")); + if (g_processMessage == nullptr) { + ResetServiceLocked(); + return false; + } + + g_hasArkFuncsInited = true; + return true; +} +} // namespace + +void Inspector::OnMessage(std::string&& msg) +{ + g_dispatchMessage(vm_, std::move(msg)); + + // message will be processed soon if the debugger thread is in running or waiting status + if (g_getDispatchStatus(vm_) != DispatchStatus::UNKNOWN) { + return; + } + usleep(DELAY_CHECK_DISPATCH_STATUS); + if (g_getDispatchStatus(vm_) != DispatchStatus::UNKNOWN) { + return; + } + + // the debugger thread maybe in idle status, so try to post a task to wake it up + if (debuggerPostTask_ != nullptr) { + debuggerPostTask_([tid = tid_, vm = vm_] { + if (tid != pthread_self()) { + LOGE("Task not in debugger thread"); + return; + } + g_processMessage(vm); + }); + } else { + LOGW("No debuggerPostTask provided"); + } +} + +bool StartDebug(const std::string& componentName, void* vm, bool isDebugMode, int32_t instanceId, + const DebuggerPostTask& debuggerPostTask) +{ + LOGI("StartDebug: %{private}s", componentName.c_str()); + g_vm = vm; + if (!LoadArkDebuggerLibrary()) { + return false; + } + if (!InitializeArkFunctions()) { + LOGE("Initialize ark functions failed"); + return false; + } + + g_initializeDebugger(vm, std::bind(&SendReply, vm, std::placeholders::_2)); + + if (!InitializeInspector(vm, componentName, instanceId, debuggerPostTask)) { + LOGE("Initialize inspector failed"); + return false; + } + + if (isDebugMode) { + g_waitForDebugger(vm); + } + LOGI("StartDebug Continue"); + return true; +} + +void StopDebug(const std::string& componentName) +{ + LOGI("StopDebug: %{private}s", componentName.c_str()); + std::unique_lock lock(g_mutex); + auto iter = g_inspectors.find(g_vm); + if (iter == g_inspectors.end() || iter->second == nullptr) { + return; + } + + g_uninitializeDebugger(g_vm); + ResetServiceLocked(); + LOGI("StopDebug end"); +} +} // namespace OHOS::ArkCompiler::Toolchain diff --git a/inspector/inspector.h b/inspector/inspector.h index 6f642cd..5bf3219 100644 --- a/inspector/inspector.h +++ b/inspector/inspector.h @@ -16,4 +16,49 @@ #ifndef ARKCOMPILER_TOOLCHAIN_INSPECTOR_INSPECTOR_H #define ARKCOMPILER_TOOLCHAIN_INSPECTOR_INSPECTOR_H +#include "ws_server.h" + +#include + +namespace panda::ecmascript { +class EcmaVM; +} // namespace panda::ecmascript + +namespace OHOS::ArkCompiler::Toolchain { +using EcmaVM = panda::ecmascript::EcmaVM; +using DebuggerPostTask = std::function&&)>; + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +bool StartDebug(const std::string& componentName, void* vm, bool isDebugMode, int32_t instanceId, + const DebuggerPostTask& debuggerPostTask); + +void StopDebug(const std::string& componentName); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +class Inspector { +public: + Inspector() = default; + ~Inspector() = default; + + void OnMessage(std::string&& msg); + + static constexpr int32_t DELAY_CHECK_DISPATCH_STATUS = 100; + + pthread_t tid_ = 0; + void* vm_ = nullptr; + std::unique_ptr websocketServer_; + DebuggerPostTask debuggerPostTask_; +}; +} // namespace OHOS::ArkCompiler::Toolchain + #endif // ARKCOMPILER_TOOLCHAIN_INSPECTOR_INSPECTOR_H \ No newline at end of file