1 /* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef PANDA_RUNTIME_TOOLING_DEBUGGER_H_ 17 #define PANDA_RUNTIME_TOOLING_DEBUGGER_H_ 18 19 #include <atomic> 20 #include <functional> 21 #include <memory> 22 #include <string_view> 23 24 #include "include/method.h" 25 #include "include/runtime.h" 26 #include "pt_hooks_wrapper.h" 27 #include "include/mem/panda_smart_pointers.h" 28 #include "include/mem/panda_containers.h" 29 #include "include/runtime_notification.h" 30 #include "include/tooling/debug_interface.h" 31 #include "libpandabase/utils/span.h" 32 #include "runtime/include/mem/panda_containers.h" 33 #include "runtime/include/method.h" 34 #include "runtime/include/runtime.h" 35 #include "runtime/include/panda_vm.h" 36 #include "runtime/include/tooling/debug_interface.h" 37 #include "runtime/thread_manager.h" 38 #include "pt_hooks_wrapper.h" 39 40 namespace panda::tooling { 41 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) 42 class Breakpoint { 43 public: 44 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) Breakpoint(Method * method,uint32_t bcOffset)45 Breakpoint(Method *method, uint32_t bcOffset) : method_(method), bc_offset_(bcOffset) {} 46 ~Breakpoint() = default; 47 GetMethod()48 Method *GetMethod() const 49 { 50 return method_; 51 } 52 GetBytecodeOffset()53 uint32_t GetBytecodeOffset() const 54 { 55 return bc_offset_; 56 } 57 58 bool operator==(const Breakpoint &bpoint) const 59 { 60 return GetMethod() == bpoint.GetMethod() && GetBytecodeOffset() == bpoint.GetBytecodeOffset(); 61 } 62 63 DEFAULT_COPY_SEMANTIC(Breakpoint); 64 DEFAULT_MOVE_SEMANTIC(Breakpoint); 65 66 private: 67 Method *method_; 68 uint32_t bc_offset_; 69 }; 70 71 class HashBreakpoint { 72 public: operator()73 size_t operator()(const Breakpoint &bpoint) const 74 { 75 return (std::hash<Method *>()(bpoint.GetMethod())) ^ (std::hash<uint32_t>()(bpoint.GetBytecodeOffset())); 76 } 77 }; 78 79 class PropertyWatch { 80 public: 81 enum class Type { ACCESS, MODIFY }; 82 83 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) PropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,Type type)84 PropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId, Type type) 85 : class_id_(classId), field_id_(fieldId), type_(type) 86 { 87 } 88 89 ~PropertyWatch() = default; 90 GetClassId()91 panda_file::File::EntityId GetClassId() const 92 { 93 return class_id_; 94 } 95 GetFieldId()96 panda_file::File::EntityId GetFieldId() const 97 { 98 return field_id_; 99 } 100 GetType()101 Type GetType() const 102 { 103 return type_; 104 } 105 106 private: 107 NO_COPY_SEMANTIC(PropertyWatch); 108 NO_MOVE_SEMANTIC(PropertyWatch); 109 110 panda_file::File::EntityId class_id_; 111 panda_file::File::EntityId field_id_; 112 Type type_; 113 }; 114 115 class Debugger : public DebugInterface, RuntimeListener { 116 public: 117 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) Debugger(const Runtime * runtime)118 explicit Debugger(const Runtime *runtime) 119 : runtime_(runtime), 120 breakpoints_(GetInternalAllocatorAdapter(runtime)), 121 property_watches_(GetInternalAllocatorAdapter(runtime)), 122 vm_started_(runtime->IsInitialized()) 123 { 124 runtime_->GetNotificationManager()->AddListener(this, DEBUG_EVENT_MASK); 125 } 126 ~Debugger()127 ~Debugger() override 128 { 129 runtime_->GetNotificationManager()->RemoveListener(this, DEBUG_EVENT_MASK); 130 } 131 GetLangExtension()132 PtLangExt *GetLangExtension() const override 133 { 134 return runtime_->GetPtLangExt(); 135 } 136 137 Expected<PtMethod, Error> GetPtMethod(const PtLocation &location) const override; 138 RegisterHooks(PtHooks * hooks)139 std::optional<Error> RegisterHooks(PtHooks *hooks) override 140 { 141 hooks_.SetHooks(hooks); 142 return {}; 143 } 144 UnregisterHooks()145 std::optional<Error> UnregisterHooks() override 146 { 147 hooks_.SetHooks(nullptr); 148 return {}; 149 } 150 EnableAllGlobalHook()151 std::optional<Error> EnableAllGlobalHook() override 152 { 153 hooks_.EnableAllGlobalHook(); 154 return {}; 155 } 156 DisableAllGlobalHook()157 std::optional<Error> DisableAllGlobalHook() override 158 { 159 hooks_.DisableAllGlobalHook(); 160 return {}; 161 } 162 163 std::optional<Error> SetNotification(PtThread thread, bool enable, PtHookType hookType) override; 164 std::optional<Error> SetBreakpoint(const PtLocation &location) override; 165 166 std::optional<Error> RemoveBreakpoint(const PtLocation &location) override; 167 168 Expected<std::unique_ptr<PtFrame>, Error> GetCurrentFrame(PtThread thread) const override; 169 170 std::optional<Error> EnumerateFrames(PtThread thread, std::function<bool(const PtFrame &)> callback) const override; 171 172 // RuntimeListener methods 173 LoadModule(std::string_view filename)174 void LoadModule(std::string_view filename) override 175 { 176 hooks_.LoadModule(filename); 177 } 178 ThreadStart(ManagedThread::ThreadId threadId)179 void ThreadStart(ManagedThread::ThreadId threadId) override 180 { 181 hooks_.ThreadStart(PtThread(threadId)); 182 } 183 ThreadEnd(ManagedThread::ThreadId threadId)184 void ThreadEnd(ManagedThread::ThreadId threadId) override 185 { 186 hooks_.ThreadEnd(PtThread(threadId)); 187 } 188 189 void BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset) override; 190 VmStart()191 void VmStart() override 192 { 193 vm_started_ = true; 194 hooks_.VmStart(); 195 } 196 VmInitialization(ManagedThread::ThreadId threadId)197 void VmInitialization(ManagedThread::ThreadId threadId) override 198 { 199 hooks_.VmInitialization(PtThread(threadId)); 200 } 201 VmDeath()202 void VmDeath() override 203 { 204 hooks_.VmDeath(); 205 } 206 GarbageCollectorStart()207 void GarbageCollectorStart() override 208 { 209 hooks_.GarbageCollectionStart(); 210 } 211 GarbageCollectorFinish()212 void GarbageCollectorFinish() override 213 { 214 hooks_.GarbageCollectionFinish(); 215 } 216 217 void ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size) override; 218 219 void ExceptionCatch(const ManagedThread *thread, const Method *method, uint32_t bcOffset) override; 220 221 void MethodEntry(ManagedThread *thread, Method *method) override; 222 void MethodExit(ManagedThread *thread, Method *method) override; 223 224 void ClassLoad(Class *klass) override; 225 void ClassPrepare(Class *klass) override; 226 227 void MonitorWait(ObjectHeader *object, int64_t timeout) override; 228 void MonitorWaited(ObjectHeader *object, bool timedOut) override; 229 void MonitorContendedEnter(ObjectHeader *object) override; 230 void MonitorContendedEntered(ObjectHeader *object) override; 231 232 /* 233 * Mock API for debug interphase starts: 234 * 235 * API's function should be revorked and input parameters should be added 236 */ GetThreadList(PandaVector<PtThread> * threadList)237 std::optional<Error> GetThreadList(PandaVector<PtThread> *threadList) const override 238 { 239 runtime_->GetPandaVM()->GetThreadManager()->EnumerateThreads( 240 [threadList](MTManagedThread *mt_managed_thread) { 241 ASSERT(mt_managed_thread && "thread is null"); 242 threadList->push_back(PtThread(mt_managed_thread->GetId())); 243 return true; 244 }, 245 static_cast<unsigned int>(panda::EnumerationFlag::ALL), 246 static_cast<unsigned int>(panda::EnumerationFlag::VM_THREAD)); 247 248 return {}; 249 } 250 GetThreadInfo(PtThread thread,ThreadInfo * infoPtr)251 std::optional<Error> GetThreadInfo(PtThread thread, ThreadInfo *infoPtr) const override 252 { 253 MTManagedThread *mt_managed_thread = GetManagedThreadByPtThread(thread); 254 255 if (mt_managed_thread == nullptr) { 256 return Error(Error::Type::THREAD_NOT_FOUND, 257 std::string("Thread ") + std::to_string(thread.GetId()) + " not found"); 258 } 259 260 infoPtr->is_daemon = mt_managed_thread->IsDaemon(); 261 infoPtr->priority = mt_managed_thread->GetThreadPriority(); 262 /* fields that didn't still implemented (we don't support it): 263 * infoPtr->thread_group 264 * infoPtr->context_class_loader 265 */ 266 return {}; 267 } 268 269 std::optional<Error> SuspendThread(PtThread thread) const override; 270 271 std::optional<Error> ResumeThread(PtThread thread) const override; 272 273 std::optional<Error> SetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber, 274 const PtValue &value) const override; 275 276 std::optional<Error> GetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber, 277 PtValue *result) const override; 278 GetProperty(PtObject object,PtProperty property,PtValue * value)279 std::optional<Error> GetProperty([[maybe_unused]] PtObject object, [[maybe_unused]] PtProperty property, 280 PtValue *value) const override 281 { 282 std::cout << "GetProperty called " << std::endl; 283 const int64_t anydata = 0x123456789; 284 value->SetValue(anydata); 285 return {}; 286 } 287 SetProperty(PtObject object,PtProperty property,const PtValue & value)288 std::optional<Error> SetProperty([[maybe_unused]] PtObject object, [[maybe_unused]] PtProperty property, 289 [[maybe_unused]] const PtValue &value) const override 290 { 291 std::cout << "SetProperty called " << std::endl; 292 return {}; 293 } 294 EvaluateExpression(PtThread thread,uint32_t frameNumber,ExpressionWrapper expr,PtValue * result)295 std::optional<Error> EvaluateExpression([[maybe_unused]] PtThread thread, [[maybe_unused]] uint32_t frameNumber, 296 ExpressionWrapper expr, PtValue *result) const override 297 { 298 std::cout << "EvaluateExpression called " << std::endl; 299 if (expr.empty()) { 300 return Error(Error::Type::INVALID_EXPRESSION, "invalid expression"); 301 } 302 const int64_t anydata = 0x123456789; 303 result->SetValue(anydata); 304 return {}; 305 } 306 RetransformClasses(int classCount,const PtClass * classes)307 std::optional<Error> RetransformClasses([[maybe_unused]] int classCount, 308 [[maybe_unused]] const PtClass *classes) const override 309 { 310 std::cout << "RetransformClasses called " << std::endl; 311 return {}; 312 } 313 RedefineClasses(int classCount,const PandaClassDefinition * classes)314 std::optional<Error> RedefineClasses([[maybe_unused]] int classCount, 315 [[maybe_unused]] const PandaClassDefinition *classes) const override 316 { 317 std::cout << "RedefineClasses called " << std::endl; 318 return {}; 319 } 320 321 std::optional<Error> RestartFrame([[maybe_unused]] PtThread thread, 322 [[maybe_unused]] uint32_t frameNumber) const override; 323 SetAsyncCallStackDepth(uint32_t maxDepth)324 std::optional<Error> SetAsyncCallStackDepth([[maybe_unused]] uint32_t maxDepth) const override 325 { 326 std::cout << "SetAsyncCallStackDepth called " << std::endl; 327 return {}; 328 } 329 AwaitPromise(PtObject promiseObject,PtValue * result)330 std::optional<Error> AwaitPromise([[maybe_unused]] PtObject promiseObject, PtValue *result) const override 331 { 332 const uint32_t anyobj = 123456789; 333 result->SetValue(anyobj); 334 335 std::cout << "AwaitPromise called " << std::endl; 336 return {}; 337 } 338 CallFunctionOn(PtObject object,PtMethod method,const PandaVector<PtValue> & arguments,PtValue * returnValue)339 std::optional<Error> CallFunctionOn([[maybe_unused]] PtObject object, [[maybe_unused]] PtMethod method, 340 [[maybe_unused]] const PandaVector<PtValue> &arguments, 341 PtValue *returnValue) const override 342 { 343 const int64_t anydata = 0x123456789; 344 returnValue->SetValue(anydata); 345 std::cout << "CallFunctionOn called " << std::endl; 346 return {}; 347 } 348 GetProperties(uint32_t * countPtr,char *** propertyPtr)349 std::optional<Error> GetProperties(uint32_t *countPtr, [[maybe_unused]] char ***propertyPtr) const override 350 { 351 *countPtr = 0; 352 std::cout << "GetProperties called " << std::endl; 353 return {}; 354 } 355 356 std::optional<Error> NotifyFramePop(PtThread thread, uint32_t depth) const override; 357 358 std::optional<Error> SetPropertyAccessWatch(PtClass klass, PtProperty property) override; 359 360 std::optional<Error> ClearPropertyAccessWatch(PtClass klass, PtProperty property) override; 361 362 std::optional<Error> SetPropertyModificationWatch(PtClass klass, PtProperty property) override; 363 364 std::optional<Error> ClearPropertyModificationWatch(PtClass klass, PtProperty property) override; 365 366 std::optional<Error> GetThisVariableByFrame(PtThread thread, uint32_t frameDepth, PtValue *result) override; 367 368 private: 369 Expected<panda::Frame::VRegister *, Error> GetVRegByPtThread(PtThread thread, uint32_t frameDepth, 370 int32_t regNumber) const; 371 const tooling::Breakpoint *FindBreakpoint(const Method *method, uint32_t bcOffset) const; 372 bool RemoveBreakpoint(Method *method, uint32_t bcOffset); 373 374 MTManagedThread *GetManagedThreadByPtThread(PtThread thread) const; 375 IsPropertyWatchActive()376 bool IsPropertyWatchActive() const 377 { 378 return !property_watches_.empty(); 379 } 380 const tooling::PropertyWatch *FindPropertyWatch(panda_file::File::EntityId classId, 381 panda_file::File::EntityId fieldId, 382 tooling::PropertyWatch::Type type) const; 383 bool RemovePropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId, 384 tooling::PropertyWatch::Type type); 385 386 bool HandleBreakpoint(const ManagedThread *thread, const Method *method, uint32_t bcOffset); 387 void HandleNotifyFramePop(ManagedThread *thread, Method *method, bool wasPoppedByException); 388 void HandleExceptionThrowEvent(ManagedThread *thread, Method *method, uint32_t bcOffset); 389 bool HandleStep(const ManagedThread *thread, const Method *method, uint32_t bcOffset); 390 391 bool HandlePropertyAccess(const ManagedThread *thread, const Method *method, uint32_t bcOffset); 392 bool HandlePropertyModify(const ManagedThread *thread, const Method *method, uint32_t bcOffset); 393 394 static constexpr uint32_t DEBUG_EVENT_MASK = 395 RuntimeNotificationManager::Event::LOAD_MODULE | RuntimeNotificationManager::Event::THREAD_EVENTS | 396 RuntimeNotificationManager::Event::BYTECODE_PC_CHANGED | RuntimeNotificationManager::Event::EXCEPTION_EVENTS | 397 RuntimeNotificationManager::Event::VM_EVENTS | RuntimeNotificationManager::Event::GARBAGE_COLLECTOR_EVENTS | 398 RuntimeNotificationManager::Event::METHOD_EVENTS | RuntimeNotificationManager::Event::CLASS_EVENTS | 399 RuntimeNotificationManager::Event::MONITOR_EVENTS | RuntimeNotificationManager::Event::ALLOCATION_EVENTS; 400 401 const Runtime *runtime_; 402 PtHooksWrapper hooks_; 403 404 PandaUnorderedSet<tooling::Breakpoint, tooling::HashBreakpoint> breakpoints_; 405 PandaList<tooling::PropertyWatch> property_watches_; 406 bool vm_started_ {false}; 407 408 NO_COPY_SEMANTIC(Debugger); 409 NO_MOVE_SEMANTIC(Debugger); 410 }; 411 412 class PtDebugFrame : public PtFrame { 413 public: 414 explicit PtDebugFrame(Method *method, const Frame *interpreterFrame); 415 ~PtDebugFrame() override = default; 416 IsInterpreterFrame()417 bool IsInterpreterFrame() const override 418 { 419 return is_interpreter_frame_; 420 } 421 GetPtMethod()422 PtMethod GetPtMethod() const override 423 { 424 return method_; 425 } 426 GetVReg(size_t i)427 uint64_t GetVReg(size_t i) const override 428 { 429 if (!is_interpreter_frame_) { 430 return 0; 431 } 432 return vregs_[i]; 433 } 434 GetVRegNum()435 size_t GetVRegNum() const override 436 { 437 return vregs_.size(); 438 } 439 GetArgument(size_t i)440 uint64_t GetArgument(size_t i) const override 441 { 442 if (!is_interpreter_frame_) { 443 return 0; 444 } 445 return args_[i]; 446 } 447 GetArgumentNum()448 size_t GetArgumentNum() const override 449 { 450 return args_.size(); 451 } 452 GetAccumulator()453 uint64_t GetAccumulator() const override 454 { 455 return acc_; 456 } 457 GetMethodId()458 panda_file::File::EntityId GetMethodId() const override 459 { 460 return method_id_; 461 } 462 GetBytecodeOffset()463 uint32_t GetBytecodeOffset() const override 464 { 465 return bc_offset_; 466 } 467 GetPandaFile()468 std::string GetPandaFile() const override 469 { 470 return panda_file_; 471 } 472 473 // mock API GetFrameId()474 uint32_t GetFrameId() const override 475 { 476 return 0; 477 } 478 479 private: 480 NO_COPY_SEMANTIC(PtDebugFrame); 481 NO_MOVE_SEMANTIC(PtDebugFrame); 482 483 bool is_interpreter_frame_; 484 PtMethod method_; 485 uint64_t acc_ {0}; 486 PandaVector<uint64_t> vregs_; 487 PandaVector<uint64_t> args_; 488 panda_file::File::EntityId method_id_; 489 uint32_t bc_offset_ {0}; 490 std::string panda_file_; 491 }; 492 493 } // namespace panda::tooling 494 495 #endif // PANDA_RUNTIME_TOOLING_DEBUGGER_H_ 496