1 /** 2 * Copyright (c) 2021-2024 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/mem/panda_containers.h" 25 #include "include/mem/panda_smart_pointers.h" 26 #include "include/method.h" 27 #include "include/panda_vm.h" 28 #include "include/runtime.h" 29 #include "include/runtime_notification.h" 30 #include "include/tooling/debug_interface.h" 31 #include "libpandabase/os/mutex.h" 32 #include "libpandabase/utils/span.h" 33 #include "pt_hooks_wrapper.h" 34 #include "runtime/thread_manager.h" 35 36 namespace ark::tooling { 37 // Deprecated API 38 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) 39 class Breakpoint { 40 public: 41 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) Breakpoint(Method * method,uint32_t bcOffset)42 Breakpoint(Method *method, uint32_t bcOffset) : method_(method), bcOffset_(bcOffset) {} 43 ~Breakpoint() = default; 44 GetMethod()45 Method *GetMethod() const 46 { 47 return method_; 48 } 49 GetBytecodeOffset()50 uint32_t GetBytecodeOffset() const 51 { 52 return bcOffset_; 53 } 54 55 bool operator==(const Breakpoint &bpoint) const 56 { 57 return GetMethod() == bpoint.GetMethod() && GetBytecodeOffset() == bpoint.GetBytecodeOffset(); 58 } 59 60 DEFAULT_COPY_SEMANTIC(Breakpoint); 61 DEFAULT_MOVE_SEMANTIC(Breakpoint); 62 63 private: 64 Method *method_; 65 uint32_t bcOffset_; 66 }; 67 68 // Deprecated API 69 class HashBreakpoint { 70 public: operator()71 size_t operator()(const Breakpoint &bpoint) const 72 { 73 return (std::hash<Method *>()(bpoint.GetMethod())) ^ (std::hash<uint32_t>()(bpoint.GetBytecodeOffset())); 74 } 75 }; 76 77 class HashLocation { 78 public: operator()79 size_t operator()(const PtLocation &location) const 80 { 81 return std::hash<std::string>()(location.GetPandaFile()) ^ 82 std::hash<uint32_t>()(location.GetMethodId().GetOffset()) ^ // CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK) 83 std::hash<uint32_t>()(location.GetBytecodeOffset()); // CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK) 84 } 85 }; 86 87 class PropertyWatch { 88 public: 89 enum class Type { ACCESS, MODIFY }; 90 91 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) PropertyWatch(panda_file::File::EntityId classId,panda_file::File::EntityId fieldId,Type type)92 PropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId, Type type) 93 : classId_(classId), fieldId_(fieldId), type_(type) 94 { 95 } 96 97 ~PropertyWatch() = default; 98 GetClassId()99 panda_file::File::EntityId GetClassId() const 100 { 101 return classId_; 102 } 103 GetFieldId()104 panda_file::File::EntityId GetFieldId() const 105 { 106 return fieldId_; 107 } 108 GetType()109 Type GetType() const 110 { 111 return type_; 112 } 113 114 private: 115 NO_COPY_SEMANTIC(PropertyWatch); 116 NO_MOVE_SEMANTIC(PropertyWatch); 117 118 panda_file::File::EntityId classId_; 119 panda_file::File::EntityId fieldId_; 120 Type type_; 121 }; 122 123 // NOLINTNEXTLINE(fuchsia-multiple-inheritance) 124 class Debugger : public DebugInterface, RuntimeListener { 125 public: 126 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) Debugger(const Runtime * runtime)127 explicit Debugger(const Runtime *runtime) 128 : runtime_(runtime), 129 breakpoints_(GetInternalAllocatorAdapter(runtime)), 130 propertyWatches_(GetInternalAllocatorAdapter(runtime)), 131 vmStarted_(runtime->IsInitialized()) 132 { 133 runtime_->GetNotificationManager()->AddListener(this, DEBUG_EVENT_MASK); 134 } 135 ~Debugger()136 ~Debugger() override 137 { 138 runtime_->GetNotificationManager()->RemoveListener(this, DEBUG_EVENT_MASK); 139 } 140 RegisterHooks(PtHooks * hooks)141 std::optional<Error> RegisterHooks(PtHooks *hooks) override 142 { 143 hooks_.SetHooks(hooks); 144 return {}; 145 } 146 UnregisterHooks()147 std::optional<Error> UnregisterHooks() override 148 { 149 hooks_.SetHooks(nullptr); 150 return {}; 151 } 152 EnableAllGlobalHook()153 std::optional<Error> EnableAllGlobalHook() override 154 { 155 hooks_.EnableAllGlobalHook(); 156 return {}; 157 } 158 DisableAllGlobalHook()159 std::optional<Error> DisableAllGlobalHook() override 160 { 161 hooks_.DisableAllGlobalHook(); 162 return {}; 163 } 164 165 std::optional<Error> SetNotification(PtThread thread, bool enable, PtHookType hookType) override; 166 std::optional<Error> SetBreakpoint(const PtLocation &location) override; 167 168 std::optional<Error> RemoveBreakpoint(const PtLocation &location) override; 169 170 Expected<std::unique_ptr<PtFrame>, Error> GetCurrentFrame(PtThread thread) const override; 171 172 std::optional<Error> EnumerateFrames(PtThread thread, std::function<bool(const PtFrame &)> callback) const override; 173 174 // RuntimeListener methods 175 LoadModule(std::string_view filename)176 void LoadModule(std::string_view filename) override 177 { 178 hooks_.LoadModule(filename); 179 } 180 ThreadStart(ManagedThread * managedThread)181 void ThreadStart(ManagedThread *managedThread) override 182 { 183 hooks_.ThreadStart(PtThread(managedThread)); 184 } 185 ThreadEnd(ManagedThread * managedThread)186 void ThreadEnd(ManagedThread *managedThread) override 187 { 188 hooks_.ThreadEnd(PtThread(managedThread)); 189 } 190 191 void BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset) override; 192 VmStart()193 void VmStart() override 194 { 195 vmStarted_ = true; 196 hooks_.VmStart(); 197 } 198 VmInitialization(ManagedThread * managedThread)199 void VmInitialization(ManagedThread *managedThread) override 200 { 201 hooks_.VmInitialization(PtThread(managedThread)); 202 } 203 VmDeath()204 void VmDeath() override 205 { 206 hooks_.VmDeath(); 207 } 208 GarbageCollectorStart()209 void GarbageCollectorStart() override 210 { 211 hooks_.GarbageCollectionStart(); 212 } 213 GarbageCollectorFinish()214 void GarbageCollectorFinish() override 215 { 216 ark::MTManagedThread *self = ark::MTManagedThread::GetCurrent(); 217 if (self == nullptr) { 218 return; 219 } 220 hooks_.GarbageCollectionFinish(); 221 } 222 223 void ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size) override; 224 225 void ExceptionThrow(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, 226 uint32_t bcOffset) override; 227 void ExceptionCatch(ManagedThread *thread, Method *method, ObjectHeader *exceptionObject, 228 uint32_t bcOffset) override; 229 ConsoleCall(ManagedThread * thread,ConsoleCallType type,uint64_t timestamp,const PandaVector<TypedValue> & arguments)230 void ConsoleCall(ManagedThread *thread, ConsoleCallType type, uint64_t timestamp, 231 const PandaVector<TypedValue> &arguments) override 232 { 233 hooks_.ConsoleCall(PtThread(thread), type, timestamp, arguments); 234 } 235 236 void MethodEntry(ManagedThread *managedThread, Method *method) override; 237 void MethodExit(ManagedThread *managedThread, Method *method) override; 238 239 void ClassLoad(Class *klass) override; 240 void ClassPrepare(Class *klass) override; 241 242 void MonitorWait(ObjectHeader *object, int64_t timeout) override; 243 void MonitorWaited(ObjectHeader *object, bool timedOut) override; 244 void MonitorContendedEnter(ObjectHeader *object) override; 245 void MonitorContendedEntered(ObjectHeader *object) override; 246 247 /* 248 * Mock API for debug interphase starts: 249 * 250 * API's function should be revorked and input parameters should be added 251 */ GetThreadList(PandaVector<PtThread> * threadList)252 std::optional<Error> GetThreadList(PandaVector<PtThread> *threadList) const override 253 { 254 runtime_->GetPandaVM()->GetThreadManager()->EnumerateThreads( 255 [threadList](ManagedThread *managedThread) { 256 ASSERT(managedThread && "thread is null"); 257 threadList->push_back(PtThread(managedThread)); 258 return true; 259 }, 260 static_cast<unsigned int>(ark::EnumerationFlag::ALL), 261 static_cast<unsigned int>(ark::EnumerationFlag::VM_THREAD)); 262 263 return {}; 264 } 265 GetThreadInfo(PtThread thread,ThreadInfo * infoPtr)266 std::optional<Error> GetThreadInfo([[maybe_unused]] PtThread thread, 267 [[maybe_unused]] ThreadInfo *infoPtr) const override 268 { 269 PT_UNIMPLEMENTED(); 270 return {}; 271 } 272 273 std::optional<Error> SuspendThread(PtThread thread) const override; 274 275 std::optional<Error> ResumeThread(PtThread thread) const override; 276 277 std::optional<Error> SetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber, 278 const VRegValue &value) const override; 279 280 std::optional<Error> GetVariable(PtThread thread, uint32_t frameDepth, int32_t regNumber, 281 VRegValue *result) const override; 282 283 /** 284 * @brief Loads provided bytecode and executes evaluation method. 285 * File must contain public class with equally named static method taking no arguments. 286 * Name of both class and method must equal source code file name, path to which must be included into binary. 287 * @param thread in which to load and execute method. 288 * @param frameNumber call stack depth in which context to evaluate. 289 * @param expr bytecode with expression. 290 * @param method pointer to save the loaded method in. 291 * @param result pointer to save result in. 292 */ 293 std::optional<Error> EvaluateExpression(PtThread thread, uint32_t frameNumber, const ExpressionWrapper &expr, 294 Method **method, VRegValue *result) const override; 295 296 /** 297 * @brief Executes the provided evaluation method. 298 * @param thread in which to execute method. 299 * @param frameNumber call stack depth in which context to evaluate. 300 * @param method pointer to the method. 301 * @param result pointer to save result in. 302 */ 303 std::optional<Error> EvaluateExpression(PtThread thread, uint32_t frameNumber, Method *method, 304 VRegValue *result) const override; 305 306 std::optional<Error> RestartFrame([[maybe_unused]] PtThread thread, 307 [[maybe_unused]] uint32_t frameNumber) const override; 308 SetAsyncCallStackDepth(uint32_t maxDepth)309 std::optional<Error> SetAsyncCallStackDepth([[maybe_unused]] uint32_t maxDepth) const override 310 { 311 PT_UNIMPLEMENTED(); 312 return {}; 313 } 314 GetProperties(uint32_t * countPtr,char *** propertyPtr)315 std::optional<Error> GetProperties([[maybe_unused]] uint32_t *countPtr, 316 [[maybe_unused]] char ***propertyPtr) const override 317 { 318 PT_UNIMPLEMENTED(); 319 return {}; 320 } 321 322 std::optional<Error> NotifyFramePop(PtThread thread, uint32_t depth) const override; 323 324 std::optional<Error> GetThisVariableByFrame(PtThread thread, uint32_t frameDepth, ObjectHeader **thisPtr) override; 325 326 std::optional<Error> SetPropertyAccessWatch(BaseClass *klass, PtProperty property) override; 327 328 std::optional<Error> ClearPropertyAccessWatch(BaseClass *klass, PtProperty property) override; 329 330 std::optional<Error> SetPropertyModificationWatch(BaseClass *klass, PtProperty property) override; 331 332 std::optional<Error> ClearPropertyModificationWatch(BaseClass *klass, PtProperty property) override; 333 334 private: 335 Expected<interpreter::StaticVRegisterRef, Error> GetVRegByPandaFrame(ark::Frame *frame, int32_t regNumber) const; 336 Expected<interpreter::DynamicVRegisterRef, Error> GetVRegByPandaFrameDyn(ark::Frame *frame, 337 int32_t regNumber) const; 338 std::optional<Error> CheckLocation(const PtLocation &location); 339 bool IsBreakpoint(const PtLocation &location) const REQUIRES_SHARED(rwlock_); 340 bool EraseBreakpoint(const PtLocation &location); 341 IsPropertyWatchActive()342 bool IsPropertyWatchActive() const 343 { 344 os::memory::ReadLockHolder rholder(rwlock_); 345 return !propertyWatches_.empty(); 346 } 347 const tooling::PropertyWatch *FindPropertyWatch(panda_file::File::EntityId classId, 348 panda_file::File::EntityId fieldId, 349 tooling::PropertyWatch::Type type) const REQUIRES_SHARED(rwlock_); 350 bool RemovePropertyWatch(panda_file::File::EntityId classId, panda_file::File::EntityId fieldId, 351 tooling::PropertyWatch::Type type); 352 353 bool HandleBreakpoint(ManagedThread *managedThread, Method *method, const PtLocation &location); 354 void HandleNotifyFramePop(ManagedThread *managedThread, Method *method, bool wasPoppedByException); 355 bool HandleStep(ManagedThread *managedThread, Method *method, const PtLocation &location); 356 357 bool HandlePropertyAccess(ManagedThread *thread, Method *method, const PtLocation &location); 358 bool HandlePropertyModify(ManagedThread *thread, Method *method, const PtLocation &location); 359 360 static constexpr uint32_t DEBUG_EVENT_MASK = 361 RuntimeNotificationManager::Event::LOAD_MODULE | RuntimeNotificationManager::Event::THREAD_EVENTS | 362 RuntimeNotificationManager::Event::BYTECODE_PC_CHANGED | RuntimeNotificationManager::Event::EXCEPTION_EVENTS | 363 RuntimeNotificationManager::Event::VM_EVENTS | RuntimeNotificationManager::Event::GARBAGE_COLLECTOR_EVENTS | 364 RuntimeNotificationManager::Event::METHOD_EVENTS | RuntimeNotificationManager::Event::CLASS_EVENTS | 365 RuntimeNotificationManager::Event::MONITOR_EVENTS | RuntimeNotificationManager::Event::ALLOCATION_EVENTS | 366 RuntimeNotificationManager::Event::CONSOLE_EVENTS; 367 368 const Runtime *runtime_; 369 PtHooksWrapper hooks_; 370 371 mutable os::memory::RWLock rwlock_; 372 PandaUnorderedSet<PtLocation, HashLocation> breakpoints_ GUARDED_BY(rwlock_); 373 PandaList<PropertyWatch> propertyWatches_ GUARDED_BY(rwlock_); 374 // NOTE(m.strizhak): research how to rework VM start to avoid atomic 375 std::atomic_bool vmStarted_ {false}; 376 377 NO_COPY_SEMANTIC(Debugger); 378 NO_MOVE_SEMANTIC(Debugger); 379 }; 380 381 class PtDebugFrame : public PtFrame { 382 public: 383 explicit PANDA_PUBLIC_API PtDebugFrame(Method *method, Frame *interpreterFrame); 384 ~PtDebugFrame() override = default; 385 IsInterpreterFrame()386 bool IsInterpreterFrame() const override 387 { 388 return isInterpreterFrame_; 389 } 390 GetMethod()391 Method *GetMethod() const override 392 { 393 return method_; 394 } 395 GetVReg(size_t i)396 uint64_t GetVReg(size_t i) const override 397 { 398 if (!isInterpreterFrame_) { 399 return 0; 400 } 401 return vregs_[i]; 402 } 403 GetVRegKind(size_t i)404 RegisterKind GetVRegKind(size_t i) const override 405 { 406 if (!isInterpreterFrame_) { 407 return PtFrame::RegisterKind::PRIMITIVE; 408 } 409 return vregKinds_[i]; 410 } 411 GetVRegNum()412 size_t GetVRegNum() const override 413 { 414 return vregs_.size(); 415 } 416 GetArgument(size_t i)417 uint64_t GetArgument(size_t i) const override 418 { 419 if (!isInterpreterFrame_) { 420 return 0; 421 } 422 return args_[i]; 423 } 424 GetArgumentKind(size_t i)425 RegisterKind GetArgumentKind(size_t i) const override 426 { 427 if (!isInterpreterFrame_) { 428 return PtFrame::RegisterKind::PRIMITIVE; 429 } 430 return argKinds_[i]; 431 } 432 GetArgumentNum()433 size_t GetArgumentNum() const override 434 { 435 return args_.size(); 436 } 437 GetAccumulator()438 uint64_t GetAccumulator() const override 439 { 440 return acc_; 441 } 442 GetAccumulatorKind()443 RegisterKind GetAccumulatorKind() const override 444 { 445 return accKind_; 446 } 447 GetMethodId()448 panda_file::File::EntityId GetMethodId() const override 449 { 450 return methodId_; 451 } 452 GetBytecodeOffset()453 uint32_t GetBytecodeOffset() const override 454 { 455 return bcOffset_; 456 } 457 GetPandaFile()458 std::string GetPandaFile() const override 459 { 460 return pandaFile_; 461 } 462 463 // mock API GetFrameId()464 uint32_t GetFrameId() const override 465 { 466 return 0; 467 } 468 469 private: 470 NO_COPY_SEMANTIC(PtDebugFrame); 471 NO_MOVE_SEMANTIC(PtDebugFrame); 472 473 bool isInterpreterFrame_; 474 Method *method_; 475 uint64_t acc_ {0}; 476 RegisterKind accKind_ {PtFrame::RegisterKind::PRIMITIVE}; 477 PandaVector<uint64_t> vregs_; 478 PandaVector<RegisterKind> vregKinds_; 479 PandaVector<uint64_t> args_; 480 PandaVector<RegisterKind> argKinds_; 481 panda_file::File::EntityId methodId_; 482 uint32_t bcOffset_ {0}; 483 std::string pandaFile_; 484 }; 485 } // namespace ark::tooling 486 487 #endif // PANDA_RUNTIME_TOOLING_DEBUGGER_H 488