/* * Copyright (c) 2021 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_TOOLING_DEBUGGER_H_ #define PANDA_RUNTIME_TOOLING_DEBUGGER_H_ #include #include #include #include #include "include/method.h" #include "include/runtime.h" #include "pt_hooks_wrapper.h" #include "include/mem/panda_smart_pointers.h" #include "include/mem/panda_containers.h" #include "include/runtime_notification.h" #include "include/tooling/debug_interface.h" #include "libpandabase/utils/span.h" #include "runtime/include/mem/panda_containers.h" #include "runtime/include/method.h" #include "runtime/include/runtime.h" #include "runtime/include/panda_vm.h" #include "runtime/include/tooling/debug_interface.h" #include "runtime/thread_manager.h" #include "pt_hooks_wrapper.h" namespace panda::tooling { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) class Breakpoint { public: // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) Breakpoint(Method *method, uint32_t bcOffset) : method_(method), bc_offset_(bcOffset) {} ~Breakpoint() = default; Method *GetMethod() const { return method_; } uint32_t GetBytecodeOffset() const { return bc_offset_; } bool operator==(const Breakpoint &bpoint) const { return GetMethod() == bpoint.GetMethod() && GetBytecodeOffset() == bpoint.GetBytecodeOffset(); } DEFAULT_COPY_SEMANTIC(Breakpoint); DEFAULT_MOVE_SEMANTIC(Breakpoint); private: Method *method_; uint32_t bc_offset_; }; class HashBreakpoint { public: size_t operator()(const Breakpoint &bpoint) const { return (std::hash()(bpoint.GetMethod())) ^ (std::hash()(bpoint.GetBytecodeOffset())); } }; class PropertyWatch { public: enum class Type { ACCESS, MODIFY }; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) PropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId, Type type) : class_id_(classId), field_id_(fieldId), type_(type) { } ~PropertyWatch() = default; panda_file::File::EntityId GetClassId() const { return class_id_; } panda_file::File::EntityId GetFieldId() const { return field_id_; } Type GetType() const { return type_; } private: NO_COPY_SEMANTIC(PropertyWatch); NO_MOVE_SEMANTIC(PropertyWatch); panda_file::File::EntityId class_id_; panda_file::File::EntityId field_id_; Type type_; }; class Debugger : public DebugInterface, RuntimeListener { public: // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) explicit Debugger(const Runtime *runtime) : runtime_(runtime), breakpoints_(GetInternalAllocatorAdapter(runtime)), property_watches_(GetInternalAllocatorAdapter(runtime)), vm_started_(runtime->IsInitialized()) { runtime_->GetNotificationManager()->AddListener(this, DEBUG_EVENT_MASK); } ~Debugger() override { runtime_->GetNotificationManager()->RemoveListener(this, DEBUG_EVENT_MASK); } PtLangExt *GetLangExtension() const override { return runtime_->GetPtLangExt(); } Expected GetPtMethod(const PtLocation &location) const override; std::optional RegisterHooks(PtHooks *hooks) override { hooks_.SetHooks(hooks); return {}; } std::optional UnregisterHooks() override { hooks_.SetHooks(nullptr); return {}; } std::optional EnableAllGlobalHook() override { hooks_.EnableAllGlobalHook(); return {}; } std::optional DisableAllGlobalHook() override { hooks_.DisableAllGlobalHook(); return {}; } std::optional SetNotification(PtThread thread, bool enable, PtHookType hookType) override; std::optional SetBreakpoint(const PtLocation &location) override; std::optional RemoveBreakpoint(const PtLocation &location) override; Expected, Error> GetCurrentFrame(PtThread thread) const override; std::optional EnumerateFrames(PtThread thread, std::function callback) const override; // RuntimeListener methods void LoadModule(std::string_view filename) override { hooks_.LoadModule(filename); } void ThreadStart(ManagedThread::ThreadId threadId) override { hooks_.ThreadStart(PtThread(threadId)); } void ThreadEnd(ManagedThread::ThreadId threadId) override { hooks_.ThreadEnd(PtThread(threadId)); } void BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset) override; void VmStart() override { vm_started_ = true; hooks_.VmStart(); } void VmInitialization(ManagedThread::ThreadId threadId) override { hooks_.VmInitialization(PtThread(threadId)); } void VmDeath() override { hooks_.VmDeath(); } void GarbageCollectorStart() override { hooks_.GarbageCollectionStart(); } void GarbageCollectorFinish() override { hooks_.GarbageCollectionFinish(); } void ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size) override; void ExceptionCatch(const ManagedThread *thread, const Method *method, uint32_t bcOffset) override; void MethodEntry(ManagedThread *thread, Method *method) override; void MethodExit(ManagedThread *thread, Method *method) override; void ClassLoad(Class *klass) override; void ClassPrepare(Class *klass) override; void MonitorWait(ObjectHeader *object, int64_t timeout) override; void MonitorWaited(ObjectHeader *object, bool timedOut) override; void MonitorContendedEnter(ObjectHeader *object) override; void MonitorContendedEntered(ObjectHeader *object) override; /* * Mock API for debug interphase starts: * * API's function should be revorked and input parameters should be added */ std::optional GetThreadList(PandaVector *threadList) const override { runtime_->GetPandaVM()->GetThreadManager()->EnumerateThreads( [threadList](MTManagedThread *mt_managed_thread) { ASSERT(mt_managed_thread && "thread is null"); threadList->push_back(PtThread(mt_managed_thread->GetId())); return true; }, static_cast(panda::EnumerationFlag::ALL), static_cast(panda::EnumerationFlag::VM_THREAD)); return {}; } std::optional GetThreadInfo(PtThread thread, ThreadInfo *infoPtr) const override { MTManagedThread *mt_managed_thread = GetManagedThreadByPtThread(thread); if (mt_managed_thread == nullptr) { return Error(Error::Type::THREAD_NOT_FOUND, std::string("Thread ") + std::to_string(thread.GetId()) + " not found"); } infoPtr->is_daemon = mt_managed_thread->IsDaemon(); infoPtr->priority = mt_managed_thread->GetThreadPriority(); /* fields that didn't still implemented (we don't support it): * infoPtr->thread_group * infoPtr->context_class_loader */ return {}; } std::optional SuspendThread(PtThread thread) const override; std::optional ResumeThread(PtThread thread) const override; std::optional SetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber, const PtValue &value) const override; std::optional GetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber, PtValue *result) const override; std::optional GetProperty([[maybe_unused]] PtObject object, [[maybe_unused]] PtProperty property, PtValue *value) const override { std::cout << "GetProperty called " << std::endl; const int64_t anydata = 0x123456789; value->SetValue(anydata); return {}; } std::optional SetProperty([[maybe_unused]] PtObject object, [[maybe_unused]] PtProperty property, [[maybe_unused]] const PtValue &value) const override { std::cout << "SetProperty called " << std::endl; return {}; } std::optional EvaluateExpression([[maybe_unused]] PtThread thread, [[maybe_unused]] uint32_t frameNumber, ExpressionWrapper expr, PtValue *result) const override { std::cout << "EvaluateExpression called " << std::endl; if (expr.empty()) { return Error(Error::Type::INVALID_EXPRESSION, "invalid expression"); } const int64_t anydata = 0x123456789; result->SetValue(anydata); return {}; } std::optional RetransformClasses([[maybe_unused]] int classCount, [[maybe_unused]] const PtClass *classes) const override { std::cout << "RetransformClasses called " << std::endl; return {}; } std::optional RedefineClasses([[maybe_unused]] int classCount, [[maybe_unused]] const PandaClassDefinition *classes) const override { std::cout << "RedefineClasses called " << std::endl; return {}; } std::optional RestartFrame([[maybe_unused]] PtThread thread, [[maybe_unused]] uint32_t frameNumber) const override; std::optional SetAsyncCallStackDepth([[maybe_unused]] uint32_t maxDepth) const override { std::cout << "SetAsyncCallStackDepth called " << std::endl; return {}; } std::optional AwaitPromise([[maybe_unused]] PtObject promiseObject, PtValue *result) const override { const uint32_t anyobj = 123456789; result->SetValue(anyobj); std::cout << "AwaitPromise called " << std::endl; return {}; } std::optional CallFunctionOn([[maybe_unused]] PtObject object, [[maybe_unused]] PtMethod method, [[maybe_unused]] const PandaVector &arguments, PtValue *returnValue) const override { const int64_t anydata = 0x123456789; returnValue->SetValue(anydata); std::cout << "CallFunctionOn called " << std::endl; return {}; } std::optional GetProperties(uint32_t *countPtr, [[maybe_unused]] char ***propertyPtr) const override { *countPtr = 0; std::cout << "GetProperties called " << std::endl; return {}; } std::optional NotifyFramePop(PtThread thread, uint32_t depth) const override; std::optional SetPropertyAccessWatch(PtClass klass, PtProperty property) override; std::optional ClearPropertyAccessWatch(PtClass klass, PtProperty property) override; std::optional SetPropertyModificationWatch(PtClass klass, PtProperty property) override; std::optional ClearPropertyModificationWatch(PtClass klass, PtProperty property) override; std::optional GetThisVariableByFrame(PtThread thread, uint32_t frameDepth, PtValue *result) override; private: Expected GetVRegByPtThread(PtThread thread, uint32_t frameDepth, int32_t regNumber) const; const tooling::Breakpoint *FindBreakpoint(const Method *method, uint32_t bcOffset) const; bool RemoveBreakpoint(Method *method, uint32_t bcOffset); MTManagedThread *GetManagedThreadByPtThread(PtThread thread) const; bool IsPropertyWatchActive() const { return !property_watches_.empty(); } const tooling::PropertyWatch *FindPropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId, tooling::PropertyWatch::Type type) const; bool RemovePropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId, tooling::PropertyWatch::Type type); bool HandleBreakpoint(const ManagedThread *thread, const Method *method, uint32_t bcOffset); void HandleNotifyFramePop(ManagedThread *thread, Method *method, bool wasPoppedByException); void HandleExceptionThrowEvent(ManagedThread *thread, Method *method, uint32_t bcOffset); bool HandleStep(const ManagedThread *thread, const Method *method, uint32_t bcOffset); bool HandlePropertyAccess(const ManagedThread *thread, const Method *method, uint32_t bcOffset); bool HandlePropertyModify(const ManagedThread *thread, const Method *method, uint32_t bcOffset); static constexpr uint32_t DEBUG_EVENT_MASK = RuntimeNotificationManager::Event::LOAD_MODULE | RuntimeNotificationManager::Event::THREAD_EVENTS | RuntimeNotificationManager::Event::BYTECODE_PC_CHANGED | RuntimeNotificationManager::Event::EXCEPTION_EVENTS | RuntimeNotificationManager::Event::VM_EVENTS | RuntimeNotificationManager::Event::GARBAGE_COLLECTOR_EVENTS | RuntimeNotificationManager::Event::METHOD_EVENTS | RuntimeNotificationManager::Event::CLASS_EVENTS | RuntimeNotificationManager::Event::MONITOR_EVENTS | RuntimeNotificationManager::Event::ALLOCATION_EVENTS; const Runtime *runtime_; PtHooksWrapper hooks_; PandaUnorderedSet breakpoints_; PandaList property_watches_; bool vm_started_ {false}; NO_COPY_SEMANTIC(Debugger); NO_MOVE_SEMANTIC(Debugger); }; class PtDebugFrame : public PtFrame { public: explicit PtDebugFrame(Method *method, const Frame *interpreterFrame); ~PtDebugFrame() override = default; bool IsInterpreterFrame() const override { return is_interpreter_frame_; } PtMethod GetPtMethod() const override { return method_; } uint64_t GetVReg(size_t i) const override { if (!is_interpreter_frame_) { return 0; } return vregs_[i]; } size_t GetVRegNum() const override { return vregs_.size(); } uint64_t GetArgument(size_t i) const override { if (!is_interpreter_frame_) { return 0; } return args_[i]; } size_t GetArgumentNum() const override { return args_.size(); } uint64_t GetAccumulator() const override { return acc_; } panda_file::File::EntityId GetMethodId() const override { return method_id_; } uint32_t GetBytecodeOffset() const override { return bc_offset_; } std::string GetPandaFile() const override { return panda_file_; } // mock API uint32_t GetFrameId() const override { return 0; } private: NO_COPY_SEMANTIC(PtDebugFrame); NO_MOVE_SEMANTIC(PtDebugFrame); bool is_interpreter_frame_; PtMethod method_; uint64_t acc_ {0}; PandaVector vregs_; PandaVector args_; panda_file::File::EntityId method_id_; uint32_t bc_offset_ {0}; std::string panda_file_; }; } // namespace panda::tooling #endif // PANDA_RUNTIME_TOOLING_DEBUGGER_H_