From 89ea26265dc9e387ceb55920ad6bff186b17d39d Mon Sep 17 00:00:00 2001 From: jiachong Date: Mon, 19 May 2025 16:37:15 +0800 Subject: [PATCH] Debugger normalization Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/IC8P9M Signed-off-by: jiachong Change-Id: Ib69f76b6a3ff7c54d113fbb1c0f2d23069de9683 --- bundle.json | 8 + debugger/runtime_notification.h | 759 ++++++++++++++++++++++++++++++++ tooling/BUILD.gn | 36 ++ tooling/base/pt_json.cpp | 9 +- tooling/base/pt_json.h | 204 +++++---- tooling/debugger_service.cpp | 19 +- 6 files changed, 938 insertions(+), 97 deletions(-) create mode 100644 debugger/runtime_notification.h diff --git a/bundle.json b/bundle.json index 19e3ee61..715582c2 100644 --- a/bundle.json +++ b/bundle.json @@ -55,8 +55,16 @@ ], "header_base": "//arkcompiler/toolchain/websocket" } + }, + { + "name": "//arkcompiler/toolchain/tooling:libark_common_debugger", + "header": { + "header_files": [], + "header_base": "//arkcompiler/toolchain/tooling" + } } ], + "test": [ "//arkcompiler/toolchain:ark_toolchain_unittest" ] diff --git a/debugger/runtime_notification.h b/debugger/runtime_notification.h new file mode 100644 index 00000000..d27323b5 --- /dev/null +++ b/debugger/runtime_notification.h @@ -0,0 +1,759 @@ +/** + * Copyright (c) 2021-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 PANDA_RUNTIME_RUNTIME_NOTIFICATION_H_ +#define PANDA_RUNTIME_RUNTIME_NOTIFICATION_H_ + +#include +#include +#include + +#include "libpandabase/os/mutex.h" +#include "runtime/include/console_call_type.h" +#include "runtime/include/coretypes/tagged_value.h" +#include "runtime/include/locks.h" +#include "runtime/include/mem/panda_containers.h" +#include "runtime/include/mem/panda_string.h" +#include "runtime/include/runtime.h" +#include "runtime/handle_scope-inl.h" + +#include +#include "ecmascript/js_handle.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/method.h" +#include "ecmascript/debugger/js_debugger_manager.h" + +namespace ark { + +class Method; +class Class; +class Rendezvous; + +class RuntimeListener { +public: + RuntimeListener() = default; + virtual ~RuntimeListener() = default; + DEFAULT_COPY_SEMANTIC(RuntimeListener); + DEFAULT_MOVE_SEMANTIC(RuntimeListener); + + virtual void LoadModule([[maybe_unused]] std::string_view name) {} + virtual void LoadModule(std::string_view name, std::string_view) = 0; + + virtual void ThreadStart([[maybe_unused]] ManagedThread *managedThread) {} + virtual void ThreadEnd([[maybe_unused]] ManagedThread *managedThread) {} + + virtual void BytecodePcChanged([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Method *method, + [[maybe_unused]] uint32_t bcOffset) + { + } + virtual void BytecodePcChanged(JSThread *thread, JSHandle method, + uint32_t bcOffset) = 0; + + virtual bool HandleDebuggerStmt(JSHandle method, uint32_t bcOffset) = 0; + + virtual void GarbageCollectorStart() {} + virtual void GarbageCollectorFinish() {} + + virtual void ExceptionThrow([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Method *method, + [[maybe_unused]] ObjectHeader *exceptionObject, [[maybe_unused]] uint32_t bcOffset) + { + } + + virtual void ExceptionCatch([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Method *method, + [[maybe_unused]] ObjectHeader *exceptionObject, [[maybe_unused]] uint32_t bcOffset) + { + } + + virtual void ConsoleCall([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] ConsoleCallType type, + [[maybe_unused]] uint64_t timestamp, + [[maybe_unused]] const PandaVector &arguments) + { + } + + virtual void VmStart() {} + virtual void VmInitialization([[maybe_unused]] ManagedThread *managedThread) {} + virtual void VmDeath() {} + + virtual void MethodEntry([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Method *method) {} + virtual void MethodExit([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Method *method) {} + virtual void MethodEntry(JSHandle method, JSHandle envHandle) = 0; + virtual void MethodExit(JSHandle method) = 0; + + virtual void NativeCalling(const void *nativeAddress) = 0; + virtual void NativeReturn(const void *nativeAddress) = 0; + + + virtual void ClassLoad([[maybe_unused]] Class *klass) {} + virtual void ClassPrepare([[maybe_unused]] Class *klass) {} + + virtual void MonitorWait([[maybe_unused]] ObjectHeader *object, [[maybe_unused]] int64_t timeout) {} + virtual void MonitorWaited([[maybe_unused]] ObjectHeader *object, [[maybe_unused]] bool timedOut) {} + virtual void MonitorContendedEnter([[maybe_unused]] ObjectHeader *object) {} + virtual void MonitorContendedEntered([[maybe_unused]] ObjectHeader *object) {} + + virtual void ObjectAlloc([[maybe_unused]] BaseClass *klass, [[maybe_unused]] ObjectHeader *object, + [[maybe_unused]] ManagedThread *thread, [[maybe_unused]] size_t size) + { + } + + // Deprecated events + virtual void ThreadStart([[maybe_unused]] ManagedThread::ThreadId threadId) {} + virtual void ThreadEnd([[maybe_unused]] ManagedThread::ThreadId threadId) {} + virtual void VmInitialization([[maybe_unused]] ManagedThread::ThreadId threadId) {} + virtual void ExceptionCatch([[maybe_unused]] const ManagedThread *thread, [[maybe_unused]] const Method *method, + [[maybe_unused]] uint32_t bcOffset) + { + } +}; + +class DebuggerListener { +public: + DebuggerListener() = default; + virtual ~DebuggerListener() = default; + DEFAULT_COPY_SEMANTIC(DebuggerListener); + DEFAULT_MOVE_SEMANTIC(DebuggerListener); + + virtual void StartDebugger() = 0; + virtual void StopDebugger() = 0; + virtual bool IsDebuggerConfigured() = 0; +}; + +class NotificationManager { +public: + enum Event : uint32_t { + BYTECODE_PC_CHANGED = 0x01, + LOAD_MODULE = 0x02, + THREAD_EVENTS = 0x04, + GARBAGE_COLLECTOR_EVENTS = 0x08, + EXCEPTION_EVENTS = 0x10, + VM_EVENTS = 0x20, + METHOD_EVENTS = 0x40, + CLASS_EVENTS = 0x80, + MONITOR_EVENTS = 0x100, + ALLOCATION_EVENTS = 0x200, + CONSOLE_EVENTS = 0x400, + LOAD_MODULE_JS = 0x800, + BYTECODE_PC_CHANGED_JS = 0x1000, + HANDLE_DEBUGGER_STMT_JS = 0x2000, + NATIVEEVENT_JS = 0x4000, + METHOD_EVENT_JS = 0x8000, + VM_EVENTS_JS = 0x10000, + ALL = 0xFFFFFFFF + }; + + explicit NotificationManager(mem::AllocatorPtr allocator) + : bytecodePcListeners_(allocator->Adapter()), + loadModuleListeners_(allocator->Adapter()), + threadEventsListeners_(allocator->Adapter()), + garbageCollectorListeners_(allocator->Adapter()), + exceptionListeners_(allocator->Adapter()), + vmEventsListeners_(allocator->Adapter()), + methodListeners_(allocator->Adapter()), + classListeners_(allocator->Adapter()), + monitorListeners_(allocator->Adapter()), + loadModulejsListeners_(allocator->Adapter()), + bytecodePcJsListeners_(allocator->Adapter()), + handleDebuggerStmtListeners_(allocator->Adapter()), + nativeEventJsListeners_(allocator->Adapter()), + methodEventJsListeners_(allocator->Adapter()) + { + } + + + std::function listenerAddCallback = + [this](RuntimeListener *listener, uint32_t eventMask){ + ScopedSuspendAllThreads ssat(rendezvous_); + AddListenerIfMatches(listener, eventMask, &bytecodePcListeners_, Event::BYTECODE_PC_CHANGED, + &hasBytecodePcListeners_); + + AddListenerIfMatches(listener, eventMask, &loadModuleListeners_, Event::LOAD_MODULE, &hasLoadModuleListeners_); + + AddListenerIfMatches(listener, eventMask, &threadEventsListeners_, Event::THREAD_EVENTS, + &hasThreadEventsListeners_); + + AddListenerIfMatches(listener, eventMask, &exceptionListeners_, Event::EXCEPTION_EVENTS, + &hasExceptionListeners_); + + AddListenerIfMatches(listener, eventMask, &vmEventsListeners_, Event::VM_EVENTS, &hasVmEventsListeners_); + + AddListenerIfMatches(listener, eventMask, &methodListeners_, Event::METHOD_EVENTS, &hasMethodListeners_); + + AddListenerIfMatches(listener, eventMask, &classListeners_, Event::CLASS_EVENTS, &hasClassListeners_); + + AddListenerIfMatches(listener, eventMask, &monitorListeners_, Event::MONITOR_EVENTS, &hasMonitorListeners_); + + AddListenerIfMatches(listener, eventMask, &allocationListeners_, Event::ALLOCATION_EVENTS, + &hasAllocationListeners_); + + AddListenerIfMatches(listener, eventMask, &loadModulejsListeners_, Event::LOAD_MODULE_JS, &hasLoadModulejsListeners_); + + AddListenerIfMatches(listener, eventMask, &bytecodePcJsListeners_, Event::BYTECODE_PC_CHANGED_JS, &hasBytecodePcJsListeners_); + + AddListenerIfMatches(listener, eventMask, &handleDebuggerStmtListeners_, Event::HANDLE_DEBUGGER_STMT_JS, &hasHandleDebuggerStmtListeners_); + + AddListenerIfMatches(listener, eventMask, &nativeEventJsListeners_, Event::NATIVEEVENT_JS, &hasNativeEventJsListeners_); + + AddListenerIfMatches(listener, eventMask, &methodEventJsListeners_, Event::METHOD_EVENT_JS, &hasMethodEventJsListeners_); + + AddListenerIfMatches(listener, eventMask, &vmEventJsListeners_, Event::VM_EVENTS_JS, &hasVmEventJsListeners_); + + AddListenerIfMatches(listener, eventMask, &consoleListeners_, Event::CONSOLE_EVENTS, &hasConsoleListeners_); + + { + // We cannot stop GC thread, so holding lock to avoid data race + os::memory::WriteLockHolder rwlock(gcEventLock_); + AddListenerIfMatches(listener, eventMask, &garbageCollectorListeners_, Event::GARBAGE_COLLECTOR_EVENTS, + &hasGarbageCollectorListeners_); + } + } + + std::function listenerRemoveCallback = + [this](RuntimeListener *listener, uint32_t eventMask){ + { + ScopedSuspendAllThreads ssat(rendezvous_); + RemoveListenerIfMatches(listener, eventMask, &bytecodePcListeners_, Event::BYTECODE_PC_CHANGED, + &hasBytecodePcListeners_); + + RemoveListenerIfMatches(listener, eventMask, &loadModuleListeners_, Event::LOAD_MODULE, + &hasLoadModuleListeners_); + + RemoveListenerIfMatches(listener, eventMask, &threadEventsListeners_, Event::THREAD_EVENTS, + &hasThreadEventsListeners_); + + RemoveListenerIfMatches(listener, eventMask, &exceptionListeners_, Event::EXCEPTION_EVENTS, + &hasExceptionListeners_); + + RemoveListenerIfMatches(listener, eventMask, &vmEventsListeners_, Event::VM_EVENTS, &hasVmEventsListeners_); + + RemoveListenerIfMatches(listener, eventMask, &methodListeners_, Event::METHOD_EVENTS, &hasMethodListeners_); + + RemoveListenerIfMatches(listener, eventMask, &classListeners_, Event::CLASS_EVENTS, &hasClassListeners_); + + RemoveListenerIfMatches(listener, eventMask, &monitorListeners_, Event::MONITOR_EVENTS, &hasMonitorListeners_); + + RemoveListenerIfMatches(listener, eventMask, &allocationListeners_, Event::ALLOCATION_EVENTS, + &hasAllocationListeners_); + + RemoveListenerIfMatches(listener, eventMask, &loadModulejsListeners_, Event::LOAD_MODULE_JS, &hasLoadModulejsListeners_); + + RemoveListenerIfMatches(listener, eventMask, &bytecodePcJsListeners_, Event::BYTECODE_PC_CHANGED_JS, &hasBytecodePcJsListeners_); + + RemoveListenerIfMatches(listener, eventMask, &handleDebuggerStmtListeners_, Event::HANDLE_DEBUGGER_STMT_JS, &hasHandleDebuggerStmtListeners_); + + RemoveListenerIfMatches(listener, eventMask, &nativeEventJsListeners_, Event::NATIVEEVENT_JS, &hasNativeEventJsListeners_); + + RemoveListenerIfMatches(listener, eventMask, &methodEventJsListeners_, Event::METHOD_EVENT_JS, &hasMethodEventJsListeners_); + + RemoveListenerIfMatches(listener, eventMask, &consoleListeners_, Event::CONSOLE_EVENTS, &hasConsoleListeners_); + + { + // We cannot stop GC thread, so holding lock to avoid data race + os::memory::WriteLockHolder rwlock(gcEventLock_); + RemoveListenerIfMatches(listener, eventMask, &garbageCollectorListeners_, Event::GARBAGE_COLLECTOR_EVENTS, + &hasGarbageCollectorListeners_); + } + }) + + std::function moduleEventLoadCallback = + [this](std::string_view name, std::string_view entryPoint){ + if (UNLIKELY(hasLoadModulejsListeners_)) { + for (auto listener: loadModulejsListeners_) { + if (listener != nullptr) { + listener->LoadModule(name, entryPoint); + } + } + } + } + + auto LoadModuleEventCallback2= ([name]{ + void LoadModuleEvent(std::string_view name) + { + if (UNLIKELY(hasLoadModuleListeners_)) { + for (auto *listener : loadModuleListeners_) { + if (listener != nullptr) { + listener->LoadModule(name); + } + } + } + } + }) + + void ThreadStartEvent(ManagedThread *managedThread) + { + if (UNLIKELY(hasThreadEventsListeners_)) { + for (auto *listener : threadEventsListeners_) { + if (listener != nullptr) { + listener->ThreadStart(managedThread); + } + } + } + } + + void ThreadEndEvent(ManagedThread *managedThread) + { + if (UNLIKELY(hasThreadEventsListeners_)) { + for (auto *listener : threadEventsListeners_) { + if (listener != nullptr) { + listener->ThreadEnd(managedThread); + } + } + } + } + + std::function bytecodePcChangedEventCallback = + [this](JSThread *thread, Method *method, uint32_t bcOffset) const{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle methodHandle(thread, method); + if(UNLIKELY(hasBytecodePcJsListeners_)) { + for (auto it: bytecodePcJsListeners_) { + it->BytecodePcChanged(thread, methodHandle, bcOffset); + } + } + } + + BytecodePcChangedEventCallback2([this](ManagedThread *thread, Method *method, uint32_t bcOffset) + { + if (UNLIKELY(hasBytecodePcListeners_)) { + for (auto *listener : bytecodePcListeners_) { + if (listener != nullptr) { + listener->BytecodePcChanged(thread, method, bcOffset); + } + } + } + }) + + void GarbageCollectorStartEvent() + { + if (UNLIKELY(hasGarbageCollectorListeners_)) { + os::memory::ReadLockHolder rwlock(gcEventLock_); + for (auto *listener : garbageCollectorListeners_) { + if (listener != nullptr) { + listener->GarbageCollectorStart(); + } + } + } + } + + void GarbageCollectorFinishEvent() + { + if (UNLIKELY(hasGarbageCollectorListeners_)) { + os::memory::ReadLockHolder rwlock(gcEventLock_); + for (auto *listener : garbageCollectorListeners_) { + if (listener != nullptr) { + listener->GarbageCollectorFinish(); + } + } + } + } + + void ExceptionThrowEvent(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, uint32_t bcOffset) + { + if (UNLIKELY(hasExceptionListeners_)) { + [[maybe_unused]] HandleScope scope(thread); + VMHandle objHandle(thread, exceptionObject); + for (auto *listener : exceptionListeners_) { + if (listener != nullptr) { + listener->ExceptionThrow(thread, method, objHandle.GetPtr(), bcOffset); + } + } + } + } + + void ExceptionCatchEvent(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, uint32_t bcOffset) + { + if (UNLIKELY(hasExceptionListeners_)) { + [[maybe_unused]] HandleScope scope(thread); + VMHandle objHandle(thread, exceptionObject); + for (auto *listener : exceptionListeners_) { + if (listener != nullptr) { + listener->ExceptionCatch(thread, method, objHandle.GetPtr(), bcOffset); + } + } + } + } + + void VmStartEvent() + { + if (UNLIKELY(hasVmEventsListeners_)) { + for (auto *listener : vmEventsListeners_) { + if (listener != nullptr) { + listener->VmStart(); + } + } + } + } + + std::function debuggerStmtEventCallback = + [this](JSThread *thread, Method *method, uint32_t bcOffset) const{ + JSHandle methodHandle(thread, method); + if (UNLIKELY(hasHandleDebuggerStmtListeners_)) { + for (auto it: handleDebuggerStmtListeners_) { + it->HandleDebuggerStmt(methodHandle, bcOffset); + } + } + } + + std::function nativeCallingEventCallback = + [](const void *nativeAddress) const{ + if (UNLIKELY(hasNativeEventJsListeners_)) { + for (auto it: nativeEventJsListeners_) { + it->NativeCalling(nativeAddress); + } + } + } + + std::function nativeReturnEventCallback + [](const void *nativeAddress) const{ + if (UNLIKELY(hasNativeEventJsListeners_)) { + for (auto it: nativeEventJsListeners_) { + it->NativeReturn(nativeAddress); + } + } + } + + void VmInitializationEvent(ManagedThread *managedThread) + { + if (UNLIKELY(hasVmEventsListeners_)) { + for (auto *listener : vmEventsListeners_) { + if (listener != nullptr) { + listener->VmInitialization(managedThread); + } + } + } + } + + // Deprecated API + void VmInitializationEvent(ManagedThread::ThreadId threadId) + { + if (UNLIKELY(hasVmEventsListeners_)) { + for (auto *listener : vmEventsListeners_) { + if (listener != nullptr) { + listener->VmInitialization(threadId); + } + } + } + } + + void VmDeathEvent() + { + if (UNLIKELY(hasVmEventsListeners_)) { + for (auto *listener : vmEventsListeners_) { + if (listener != nullptr) { + listener->VmDeath(); + } + } + } + } + + void VmStartEvent() const + { + if (UNLIKELY(hasvmEventJsListeners_)) { + for (auto it: vmEventJsListeners_) { + it->VmStart(); + } + } + } + + + void VmDeathEvent() const + { + if (UNLIKELY(hasvmEventJsListeners_)) { + for (auto it: vmEventJsListeners_) { + it->VmDeath(); + } + } + } + + std::function methodEntryEventCallback = + [](JSThread *thread, Method *method, JSTaggedValue env) const{ + if (UNLIKELY(hasMethodEventJsListeners_)) { + JSHandle methodHandle(thread, method); + JSHandle envHandle(thread, env); + for (auto it: methodEventJsListeners_) { + it->MethodEntry(methodHandle, envHandle); + } + } + } + + void MethodEntryEvent(ManagedThread *thread, Method *method) + { + if (UNLIKELY(hasMethodListeners_)) { + for (auto *listener : methodListeners_) { + if (listener != nullptr) { + listener->MethodEntry(thread, method); + } + } + } + } + + std::function methodExitEventCallback = + [this](JSThread *thread, Method *method) const{ + if (UNLIKELY(hasMethodEventJsListeners_)) { + JSHandle methodHandle(thread, method); + for (auto it: methodEventJsListeners_) { + it->MethodExit(methodHandle); + } + } + } + + MethodExitEventCallback2([this](ManagedThread *thread, Method *method) + { + if (UNLIKELY(hasMethodListeners_)) { + for (auto *listener : methodListeners_) { + if (listener != nullptr) { + listener->MethodExit(thread, method); + } + } + } + }) + + void ClassLoadEvent(Class *klass) + { + if (UNLIKELY(hasClassListeners_)) { + for (auto *listener : classListeners_) { + if (listener != nullptr) { + listener->ClassLoad(klass); + } + } + } + } + + void ClassPrepareEvent(Class *klass) + { + if (UNLIKELY(hasClassListeners_)) { + for (auto *listener : classListeners_) { + if (listener != nullptr) { + listener->ClassPrepare(klass); + } + } + } + } + + void MonitorWaitEvent(ObjectHeader *object, int64_t timeout) + { + if (UNLIKELY(hasMonitorListeners_)) { + // If we need to support multiple monitor listeners, + // the object must be wrapped to ObjectHandle to protect from GC move + ASSERT(monitorListeners_.size() == 1); + auto *listener = monitorListeners_.front(); + if (listener != nullptr) { + listener->MonitorWait(object, timeout); + } + } + } + + void MonitorWaitedEvent(ObjectHeader *object, bool timedOut) + { + if (UNLIKELY(hasMonitorListeners_)) { + // If we need to support multiple monitor listeners, + // the object must be wrapped to ObjectHandle to protect from GC move + ASSERT(monitorListeners_.size() == 1); + auto *listener = monitorListeners_.front(); + if (listener != nullptr) { + monitorListeners_.front()->MonitorWaited(object, timedOut); + } + } + } + + void MonitorContendedEnterEvent(ObjectHeader *object) + { + if (UNLIKELY(hasMonitorListeners_)) { + // If we need to support multiple monitor listeners, + // the object must be wrapped to ObjectHandle to protect from GC move + ASSERT(monitorListeners_.size() == 1); + auto *listener = monitorListeners_.front(); + if (listener != nullptr) { + monitorListeners_.front()->MonitorContendedEnter(object); + } + } + } + + void MonitorContendedEnteredEvent(ObjectHeader *object) + { + if (UNLIKELY(hasMonitorListeners_)) { + // If we need to support multiple monitor listeners, + // the object must be wrapped to ObjectHandle to protect from GC move + ASSERT(monitorListeners_.size() == 1); + auto *listener = monitorListeners_.front(); + if (listener != nullptr) { + monitorListeners_.front()->MonitorContendedEntered(object); + } + } + } + + bool HasAllocationListeners() const + { + return hasAllocationListeners_; + } + + void ObjectAllocEvent(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size) const + { + if (UNLIKELY(hasAllocationListeners_)) { + // If we need to support multiple allocation listeners, + // the object must be wrapped to ObjectHandle to protect from GC move + ASSERT(allocationListeners_.size() == 1); + auto *listener = allocationListeners_.front(); + if (listener != nullptr) { + allocationListeners_.front()->ObjectAlloc(klass, object, thread, size); + } + } + } + + void ConsoleCallEvent(ManagedThread *thread, ConsoleCallType type, uint64_t timestamp, + const PandaVector &arguments) const + { + if (UNLIKELY(hasConsoleListeners_)) { + for (auto listener : consoleListeners_) { + if (listener != nullptr) { + listener->ConsoleCall(thread, type, timestamp, arguments); + } + } + } + } + + void StartDebugger() + { + os::memory::ReadLockHolder holder(debuggerLock_); + for (auto *listener : debuggerListeners_) { + listener->StartDebugger(); + } + } + + void StopDebugger() + { + os::memory::ReadLockHolder holder(debuggerLock_); + for (auto *listener : debuggerListeners_) { + listener->StopDebugger(); + } + } + + bool IsDebuggerConfigured() + { + os::memory::ReadLockHolder holder(debuggerLock_); + for (auto *listener : debuggerListeners_) { + if (!listener->IsDebuggerConfigured()) { + return false; + } + } + return true; + } + + void SetRendezvous(Rendezvous *rendezvous) + { + rendezvous_ = rendezvous; + } + + void AddDebuggerListener(DebuggerListener *listener) + { + os::memory::WriteLockHolder holder(debuggerLock_); + debuggerListeners_.push_back(listener); + } + + void RemoveDebuggerListener(DebuggerListener *listener) + { + os::memory::WriteLockHolder holder(debuggerLock_); + RemoveListener(debuggerListeners_, listener); + } + +private: + template + static void AddListenerIfMatches(RuntimeListener *listener, uint32_t eventMask, + PandaList *listenerGroup, Event eventModifier, + FlagType *eventFlag) + { + if ((eventMask & eventModifier) != 0) { + // If a free group item presents, use it, otherwise push back a new item + auto it = std::find(listenerGroup->begin(), listenerGroup->end(), nullptr); + if (it != listenerGroup->end()) { + *it = listener; + } else { + listenerGroup->push_back(listener); + } + *eventFlag = true; + } + } + + template + ALWAYS_INLINE void RemoveListener(Container &c, T &listener) + { + c.erase(std::remove_if(c.begin(), c.end(), [&listener](const T &elem) { return listener == elem; })); + } + + template + static void RemoveListenerIfMatches(RuntimeListener *listener, uint32_t eventMask, + PandaList *listenerGroup, Event eventModifier, + FlagType *eventFlag) + { + if ((eventMask & eventModifier) != 0) { + auto it = std::find(listenerGroup->begin(), listenerGroup->end(), listener); + if (it == listenerGroup->end()) { + return; + } + // Removing a listener is not safe, because the iteration can not be completed in another thread. + // We just null the item in the group + *it = nullptr; + + // Check if any listener presents and update the flag if not + if (std::find_if(listenerGroup->begin(), listenerGroup->end(), + [](RuntimeListener *item) { return item != nullptr; }) == listenerGroup->end()) { + *eventFlag = false; + } + } + } + + PandaList bytecodePcListeners_; + PandaList loadModuleListeners_; + PandaList threadEventsListeners_; + PandaList garbageCollectorListeners_; + PandaList exceptionListeners_; + PandaList vmEventsListeners_; + PandaList methodListeners_; + PandaList classListeners_; + PandaList monitorListeners_; + PandaList allocationListeners_; + PandaList consoleListeners_; + PandaList loadModulejsListeners_; + PandaList bytecodePcJsListeners_; + PandaList handleDebuggerStmtListeners_; + PandaList nativeEventJsListeners_; + PandaList methodEventJsListeners_; + PandaList vmEventJsListeners_; + + bool hasBytecodePcListeners_ = false; + bool hasLoadModuleListeners_ = false; + bool hasThreadEventsListeners_ = false; + std::atomic hasGarbageCollectorListeners_ = false; + bool hasExceptionListeners_ = false; + bool hasVmEventsListeners_ = false; + bool hasMethodListeners_ = false; + bool hasClassListeners_ = false; + bool hasMonitorListeners_ = false; + bool hasAllocationListeners_ = false; + bool hasConsoleListeners_ = false; + bool hasLoadModulejsListeners_; + bool hasBytecodePcJsListeners_; + bool hasHandleDebuggerStmtListeners_; + bool hasNativeEventJsListeners_; + bool hasMethodEventJsListeners_; + bool hasVmEventJsListeners_; + Rendezvous *rendezvous_ {nullptr}; + + os::memory::RWLock debuggerLock_; + os::memory::RWLock gcEventLock_; + PandaList debuggerListeners_; +}; + +} // namespace ark + +#endif // PANDA_RUNTIME_RUNTIME_NOTIFICATION_H_ diff --git a/tooling/BUILD.gn b/tooling/BUILD.gn index e345de67..6bc5a681 100644 --- a/tooling/BUILD.gn +++ b/tooling/BUILD.gn @@ -162,3 +162,39 @@ ohos_static_library("libark_ecma_debugger_test") { } subsystem_name = "test" } + +ohos_source_set("common_debugger_set") { + sources = [ + "base/pt_json.cpp", + ] + + public_configs = [ ":ark_ecma_debugger_config" ] + + defines = [] + deps = [] + external_deps = [ "cJSON:cjson" ] + external_deps += [ + "icu:shared_icuuc", + "libuv:uv", + "bounds_checking_function:libsec_shared", + ] + subsystem_name = "arkcompiler" + part_name = "toolchain" +} + +ohos_shared_library("libark_common_debugger") { + stack_protector_ret = true + deps = [ ":common_debugger_set" ] + external_deps = [ "libuv:uv" ] + + public_configs = [ ":ark_ecma_debugger_config" ] + install_enable = true + external_deps += [ + "hilog:libhilog", + "bounds_checking_function:libsec_shared" + ] + output_extension = "so" + relative_install_dir = "ark" + subsystem_name = "arkcompiler" + part_name = "toolchain" +} diff --git a/tooling/base/pt_json.cpp b/tooling/base/pt_json.cpp index 71fee1a1..6c184b06 100644 --- a/tooling/base/pt_json.cpp +++ b/tooling/base/pt_json.cpp @@ -15,6 +15,8 @@ #include "tooling/base/pt_json.h" +#include + namespace panda::ecmascript::tooling { std::unique_ptr PtJson::CreateObject() { @@ -131,6 +133,11 @@ bool PtJson::Add(const char *key, const char *value) const return true; } +bool PtJson::Add(const char *key, const std::string& value) const +{ + return Add(key, value.c_str()); +} + bool PtJson::Add(const char *key, const std::unique_ptr &value) const { if (key == nullptr || Contains(key)) { @@ -500,5 +507,5 @@ Result PtJson::GetAny(const char *key, std::unique_ptr *value) const *value = std::make_unique(item); return Result::SUCCESS; -} } // namespace panda::ecmascript +} \ No newline at end of file diff --git a/tooling/base/pt_json.h b/tooling/base/pt_json.h index dcaa0b8b..a34b36b6 100644 --- a/tooling/base/pt_json.h +++ b/tooling/base/pt_json.h @@ -19,101 +19,115 @@ #include #include #include - +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "cJSON.h" #include "common/macros.h" -namespace panda::ecmascript::tooling { -enum class Result : uint8_t { - SUCCESS, - NOT_EXIST, - TYPE_ERROR, -}; - -class TOOLCHAIN_EXPORT PtJson { -public: - PtJson() = default; - explicit PtJson(cJSON *object) : object_(object) {} - ~PtJson() = default; - - // Create empty json object - static std::unique_ptr CreateObject(); - static std::unique_ptr CreateArray(); - - // Release cJSON object memory - void ReleaseRoot(); - - // String parse to json - static std::unique_ptr Parse(const std::string &data); - - // To string - std::string Stringify() const; - - // Add Json child - bool Add(const char *key, bool value) const; - bool Add(const char *key, int32_t value) const; - bool Add(const char *key, int64_t value) const; - bool Add(const char *key, uint32_t value) const; - bool Add(const char *key, double value) const; - bool Add(const char *key, const char *value) const; - bool Add(const char *key, const std::unique_ptr &value) const; - - // Push back to array - bool Push(bool value) const; - bool Push(int32_t value) const; - bool Push(int64_t value) const; - bool Push(uint32_t value) const; - bool Push(double value) const; - bool Push(const char *value) const; - bool Push(const std::unique_ptr &value) const; - - // Remove Json child - bool Remove(const char *key) const; - - bool Contains(const char *key) const; - - std::string GetKey() const; - - std::vector GetKeysArray() const; - - cJSON *GetJson() const; - - // Type check - bool IsBool() const; - bool IsNumber() const; - bool IsString() const; - bool IsObject() const; - bool IsArray() const; - bool IsNull() const; - - // Object accessor - bool GetBool(bool defaultValue = false) const; - int32_t GetInt(int32_t defaultValue = 0) const; - int64_t GetInt64(int64_t defaultValue = 0) const; - uint32_t GetUInt(uint32_t defaultValue = 0) const; - uint64_t GetUInt64(uint64_t defaultValue = 0) const; - double GetDouble(double defaultValue = 0.0) const; - std::string GetString() const; - - // Array accessor - int32_t GetSize() const; - std::unique_ptr Get(int32_t index) const; - - // Child item accessor - Result GetBool(const char *key, bool *value) const; - Result GetInt(const char *key, int32_t *value) const; - Result GetInt64(const char *key, int64_t *value) const; - Result GetUInt(const char *key, uint32_t *value) const; - Result GetUInt64(const char *key, uint64_t *value) const; - Result GetDouble(const char *key, double *value) const; - Result GetString(const char *key, std::string *value) const; - Result GetObject(const char *key, std::unique_ptr *value) const; - Result GetArray(const char *key, std::unique_ptr *value) const; - Result GetAny(const char *key, std::unique_ptr *value) const; - -private: - cJSON *object_ = nullptr; -}; -} // namespace panda::ecmascript - -#endif // ECMASCRIPT_TOOLING_BASE_PT_JSON_H +namespace panda::ecmascript::tooling +{ + // 前向声明 + class PtJson; + + enum class Result : uint8_t + { + SUCCESS, + NOT_EXIST, + TYPE_ERROR, + }; + + class TOOLCHAIN_EXPORT PtJson + { + public: + PtJson() = default; + explicit PtJson(cJSON *object) : object_(object) {} + ~PtJson() = default; + + // Create empty json object or array + static std::unique_ptr CreateObject(); + static std::unique_ptr CreateArray(); + + // Release cJSON object memory + void ReleaseRoot(); + + // String parse to json + static std::unique_ptr Parse(const std::string &data); + + // To string + std::string Stringify() const; + + // Add Json child + bool Add(const char *key, bool value) const; + bool Add(const char *key, int32_t value) const; + bool Add(const char *key, int64_t value) const; + bool Add(const char *key, uint32_t value) const; + bool Add(const char *key, double value) const; + bool Add(const char *key, const char *value) const; + bool Add(const char *key, const std::string& value) const; + bool Add(const char *key, const std::unique_ptr &value) const; + + // Push back to array + bool Push(bool value) const; + bool Push(int32_t value) const; + bool Push(int64_t value) const; + bool Push(uint32_t value) const; + bool Push(double value) const; + bool Push(const char *value) const; + bool Push(const std::unique_ptr &value) const; + + // Remove Json child + bool Remove(const char *key) const; + + bool Contains(const char *key) const; + + std::string GetKey() const; + + std::vector GetKeysArray() const; + + cJSON *GetJson() const; + + // Type check + bool IsBool() const; + bool IsNumber() const; + bool IsString() const; + bool IsObject() const; + bool IsArray() const; + bool IsNull() const; + + // Object accessor + bool GetBool(bool defaultValue = false) const; + int32_t GetInt(int32_t defaultValue = 0) const; + int64_t GetInt64(int64_t defaultValue = 0) const; + uint32_t GetUInt(uint32_t defaultValue = 0) const; + uint64_t GetUInt64(uint64_t defaultValue = 0) const; + double GetDouble(double defaultValue = 0.0) const; + std::string GetString() const; + + // Array accessor + int32_t GetSize() const; + std::unique_ptr Get(int32_t index) const; + + // Child item accessor + Result GetBool(const char *key, bool *value) const; + Result GetInt(const char *key, int32_t *value) const; + Result GetInt64(const char *key, int64_t *value) const; + Result GetUInt(const char *key, uint32_t *value) const; + Result GetUInt64(const char *key, uint64_t *value) const; + Result GetDouble(const char *key, double *value) const; + Result GetString(const char *key, std::string *value) const; + Result GetObject(const char *key, std::unique_ptr *value) const; + Result GetArray(const char *key, std::unique_ptr *value) const; + Result GetAny(const char *key, std::unique_ptr *value) const; + private: + cJSON *object_ = nullptr; + }; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_TOOLING_BASE_PT_JSON_H diff --git a/tooling/debugger_service.cpp b/tooling/debugger_service.cpp index 86afa0a4..b73d3607 100644 --- a/tooling/debugger_service.cpp +++ b/tooling/debugger_service.cpp @@ -18,6 +18,8 @@ #include "protocol_handler.h" #include "ecmascript/debugger/js_debugger_manager.h" +#include "debugger/runtime_notification.h" +#include "runtime/include/runtime.h" namespace panda::ecmascript::tooling { void InitializeDebugger(::panda::ecmascript::EcmaVM *vm, @@ -32,7 +34,22 @@ void InitializeDebugger(::panda::ecmascript::EcmaVM *vm, LOG_DEBUGGER(ERROR) << "JS debugger was initialized"; return; } - vm->GetJsDebuggerManager()->SetDebuggerHandler(new ProtocolHandler(onResponse, vm)); + + JsDebuggerManager *jsDebuggerManager = vm->GetJsDebuggerManager(); + jsDebuggerManager->SetDebuggerHandler(new ProtocolHandler(onResponse, vm)); + mem::InternalAllocatorPtr internalAllocator = + RuntimeInternalAllocator::Create(options.UseMallocForInternalAllocations()); + jsDebuggerManager->SetNotificationManager(new NotificationManager(internalAllocator)); + NotificationManager *notificationManager = jsDebuggerManager->GetNotificationManager(); + jsDebuggerManager->RegisterLoadModuleEvent(notificationManager.moduleEventLoadCallback); + jsDebuggerManager->RegisterAddListenerEvent(notificationManager.listenerAddCallback); + jsDebuggerManager->RegisterRemoveListenerEvent(notificationManager.listenerRemoveCallback); + jsDebuggerManager->RegisterBytecodePcChangedEvent(notificationManager.bytecodePcChangedEvent); + jsDebuggerManager->RegisterDebuggerStmtEvent(notificationManager.debuggerStmtEventCallback); + jsDebuggerManager->RegisterMethodEntryEvent(notificationManager.methodEntryEventCallback); + jsDebuggerManager->RegisterMethodEntryEvent(notificationManager.methodExitEventCallback); + jsDebuggerManager->RegisterNativeCallingEvent(notificationManager.nativeCallingEventCallback); + jsDebuggerManager->RegisterNativeReturnEvent(notificationManager.nativeReturnEventCallback); } void UninitializeDebugger(::panda::ecmascript::EcmaVM *vm) -- Gitee