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_PT_HOOKS_WRAPPER_H_ 17 #define PANDA_RUNTIME_TOOLING_PT_HOOKS_WRAPPER_H_ 18 19 #include "runtime/include/tooling/debug_interface.h" 20 #include "os/mutex.h" 21 #include "runtime/include/managed_thread.h" 22 #include "pt_thread_info.h" 23 #include "pt_hook_type_info.h" 24 25 // NOLINTNEXTLINE 26 #define ASSERT_PT_HOOK_NATIVE_CONTEXT() \ 27 ASSERT(MTManagedThread::GetCurrent() != nullptr && MTManagedThread::GetCurrent()->IsInNativeCode()) 28 29 namespace panda::tooling { 30 class ScopedNativePtHook { 31 public: ScopedNativePtHook()32 ScopedNativePtHook() 33 { 34 ManagedThread *managed_thread = ManagedThread::GetCurrent(); 35 ASSERT(managed_thread != nullptr); 36 thread_type_ = managed_thread->GetThreadType(); 37 if (thread_type_ == Thread::ThreadType::THREAD_TYPE_MANAGED) { 38 return; 39 } 40 41 ASSERT(thread_type_ == Thread::ThreadType::THREAD_TYPE_MT_MANAGED); 42 MTManagedThread *mt_managed_thread = MTManagedThread::CastFromThread(managed_thread); 43 if (!mt_managed_thread->IsInNativeCode()) { 44 mt_managed_thread_ = mt_managed_thread; 45 mt_managed_thread_->NativeCodeBegin(); 46 } 47 48 PtPushLocalFrameFromNative(); 49 } 50 ~ScopedNativePtHook()51 ~ScopedNativePtHook() 52 { 53 if (thread_type_ == Thread::ThreadType::THREAD_TYPE_MANAGED) { 54 return; 55 } 56 ASSERT(thread_type_ == Thread::ThreadType::THREAD_TYPE_MT_MANAGED); 57 58 PtPopLocalFrameFromNative(); 59 if (mt_managed_thread_ != nullptr) { 60 mt_managed_thread_->NativeCodeEnd(); 61 } 62 } 63 64 NO_COPY_SEMANTIC(ScopedNativePtHook); 65 NO_MOVE_SEMANTIC(ScopedNativePtHook); 66 67 private: 68 MTManagedThread *mt_managed_thread_ = nullptr; 69 Thread::ThreadType thread_type_ = Thread::ThreadType::THREAD_TYPE_NONE; 70 }; 71 72 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) 73 class PtHooksWrapper : public PtHooks { 74 public: SetHooks(PtHooks * hooks)75 void SetHooks(PtHooks *hooks) 76 { 77 os::memory::WriteLockHolder wholder(hooks_rwlock_); 78 hooks_ = hooks; 79 } 80 EnableGlobalHook(PtHookType hookType)81 void EnableGlobalHook(PtHookType hookType) 82 { 83 global_hook_type_info_.Enable(hookType); 84 } 85 DisableGlobalHook(PtHookType hookType)86 void DisableGlobalHook(PtHookType hookType) 87 { 88 global_hook_type_info_.Disable(hookType); 89 } 90 EnableAllGlobalHook()91 void EnableAllGlobalHook() 92 { 93 global_hook_type_info_.EnableAll(); 94 } 95 DisableAllGlobalHook()96 void DisableAllGlobalHook() 97 { 98 global_hook_type_info_.DisableAll(); 99 } 100 101 // Wrappers for hooks Breakpoint(PtThread thread,const PtLocation & location)102 void Breakpoint(PtThread thread, const PtLocation &location) override 103 { 104 os::memory::ReadLockHolder rholder(hooks_rwlock_); 105 ASSERT(vmdeath_did_not_happen_); 106 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_BREAKPOINT)) { 107 return; 108 } 109 ScopedNativePtHook nativeScope; 110 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 111 hooks_->Breakpoint(thread, location); 112 } 113 LoadModule(std::string_view pandaFile)114 void LoadModule(std::string_view pandaFile) override 115 { 116 os::memory::ReadLockHolder rholder(hooks_rwlock_); 117 ASSERT(vmdeath_did_not_happen_); 118 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_LOAD_MODULE)) { 119 return; 120 } 121 ScopedNativePtHook nativeScope; 122 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 123 hooks_->LoadModule(pandaFile); 124 } 125 Paused(PauseReason reason)126 void Paused(PauseReason reason) override 127 { 128 os::memory::ReadLockHolder rholder(hooks_rwlock_); 129 ASSERT(vmdeath_did_not_happen_); 130 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_PAUSED)) { 131 return; 132 } 133 ScopedNativePtHook nativeScope; 134 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 135 hooks_->Paused(reason); 136 } 137 Exception(PtThread thread,const PtLocation & location,PtObject exceptionObject,const PtLocation & catchLocation)138 void Exception(PtThread thread, const PtLocation &location, PtObject exceptionObject, 139 const PtLocation &catchLocation) override 140 { 141 os::memory::ReadLockHolder rholder(hooks_rwlock_); 142 ASSERT(vmdeath_did_not_happen_); 143 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_EXCEPTION)) { 144 return; 145 } 146 ScopedNativePtHook nativeScope; 147 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 148 hooks_->Exception(thread, location, exceptionObject, catchLocation); 149 } 150 ExceptionCatch(PtThread thread,const PtLocation & location,PtObject exceptionObject)151 void ExceptionCatch(PtThread thread, const PtLocation &location, PtObject exceptionObject) override 152 { 153 os::memory::ReadLockHolder rholder(hooks_rwlock_); 154 ASSERT(vmdeath_did_not_happen_); 155 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_EXCEPTION_CATCH)) { 156 return; 157 } 158 ScopedNativePtHook nativeScope; 159 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 160 hooks_->ExceptionCatch(thread, location, exceptionObject); 161 } 162 PropertyAccess(PtThread thread,const PtLocation & location,PtObject object,PtProperty property)163 void PropertyAccess(PtThread thread, const PtLocation &location, PtObject object, PtProperty property) override 164 { 165 os::memory::ReadLockHolder rholder(hooks_rwlock_); 166 ASSERT(vmdeath_did_not_happen_); 167 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_PROPERTY_ACCESS)) { 168 return; 169 } 170 ScopedNativePtHook nativeScope; 171 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 172 hooks_->PropertyAccess(thread, location, object, property); 173 } 174 PropertyModification(PtThread thread,const PtLocation & location,PtObject object,PtProperty property,PtValue newValue)175 void PropertyModification(PtThread thread, const PtLocation &location, PtObject object, PtProperty property, 176 PtValue newValue) override 177 { 178 os::memory::ReadLockHolder rholder(hooks_rwlock_); 179 ASSERT(vmdeath_did_not_happen_); 180 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_PROPERTY_MODIFICATION)) { 181 return; 182 } 183 ScopedNativePtHook nativeScope; 184 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 185 hooks_->PropertyModification(thread, location, object, property, newValue); 186 } 187 FramePop(PtThread thread,PtMethod method,bool wasPoppedByException)188 void FramePop(PtThread thread, PtMethod method, bool wasPoppedByException) override 189 { 190 os::memory::ReadLockHolder rholder(hooks_rwlock_); 191 ASSERT(vmdeath_did_not_happen_); 192 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_FRAME_POP)) { 193 return; 194 } 195 ScopedNativePtHook nativeScope; 196 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 197 hooks_->FramePop(thread, method, wasPoppedByException); 198 } 199 GarbageCollectionFinish()200 void GarbageCollectionFinish() override 201 { 202 // ASSERT(ManagedThread::GetCurrent() == nullptr) 203 os::memory::ReadLockHolder rholder(hooks_rwlock_); 204 ASSERT(vmdeath_did_not_happen_); 205 if (hooks_ == nullptr || !GlobalHookIsEnabled(PtHookType::PT_HOOK_TYPE_GARBAGE_COLLECTION_FINISH)) { 206 return; 207 } 208 // Called in an unmanaged thread 209 hooks_->GarbageCollectionFinish(); 210 } 211 GarbageCollectionStart()212 void GarbageCollectionStart() override 213 { 214 // ASSERT(ManagedThread::GetCurrent() == nullptr) 215 os::memory::ReadLockHolder rholder(hooks_rwlock_); 216 ASSERT(vmdeath_did_not_happen_); 217 if (hooks_ == nullptr || !GlobalHookIsEnabled(PtHookType::PT_HOOK_TYPE_GARBAGE_COLLECTION_START)) { 218 return; 219 } 220 // Called in an unmanaged thread 221 hooks_->GarbageCollectionStart(); 222 } 223 MethodEntry(PtThread thread,PtMethod method)224 void MethodEntry(PtThread thread, PtMethod method) override 225 { 226 os::memory::ReadLockHolder rholder(hooks_rwlock_); 227 ASSERT(vmdeath_did_not_happen_); 228 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_METHOD_ENTRY)) { 229 return; 230 } 231 ScopedNativePtHook nativeScope; 232 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 233 hooks_->MethodEntry(thread, method); 234 } 235 MethodExit(PtThread thread,PtMethod method,bool wasPoppedByException,PtValue returnValue)236 void MethodExit(PtThread thread, PtMethod method, bool wasPoppedByException, PtValue returnValue) override 237 { 238 os::memory::ReadLockHolder rholder(hooks_rwlock_); 239 ASSERT(vmdeath_did_not_happen_); 240 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_METHOD_EXIT)) { 241 return; 242 } 243 ScopedNativePtHook nativeScope; 244 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 245 hooks_->MethodExit(thread, method, wasPoppedByException, returnValue); 246 } 247 SingleStep(PtThread thread,const PtLocation & location)248 void SingleStep(PtThread thread, const PtLocation &location) override 249 { 250 os::memory::ReadLockHolder rholder(hooks_rwlock_); 251 ASSERT(vmdeath_did_not_happen_); 252 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_SINGLE_STEP)) { 253 return; 254 } 255 ScopedNativePtHook nativeScope; 256 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 257 hooks_->SingleStep(thread, location); 258 } 259 ThreadStart(PtThread thread)260 void ThreadStart(PtThread thread) override 261 { 262 os::memory::ReadLockHolder rholder(hooks_rwlock_); 263 ASSERT(vmdeath_did_not_happen_); 264 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_THREAD_START)) { 265 return; 266 } 267 ScopedNativePtHook nativeScope; 268 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 269 hooks_->ThreadStart(thread); 270 } 271 ThreadEnd(PtThread thread)272 void ThreadEnd(PtThread thread) override 273 { 274 os::memory::ReadLockHolder rholder(hooks_rwlock_); 275 ASSERT(vmdeath_did_not_happen_); 276 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_THREAD_END)) { 277 return; 278 } 279 ScopedNativePtHook nativeScope; 280 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 281 hooks_->ThreadEnd(thread); 282 } 283 VmStart()284 void VmStart() override 285 { 286 os::memory::ReadLockHolder rholder(hooks_rwlock_); 287 ASSERT(vmdeath_did_not_happen_); 288 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_VM_START)) { 289 return; 290 } 291 ScopedNativePtHook nativeScope; 292 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 293 hooks_->VmStart(); 294 } 295 VmInitialization(PtThread thread)296 void VmInitialization(PtThread thread) override 297 { 298 os::memory::ReadLockHolder rholder(hooks_rwlock_); 299 ASSERT(vmdeath_did_not_happen_); 300 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_VM_INITIALIZATION)) { 301 return; 302 } 303 ScopedNativePtHook nativeScope; 304 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 305 hooks_->VmInitialization(thread); 306 } 307 VmDeath()308 void VmDeath() override 309 { 310 os::memory::ReadLockHolder rholder(hooks_rwlock_); 311 ASSERT(vmdeath_did_not_happen_); 312 #ifndef NDEBUG 313 vmdeath_did_not_happen_ = false; 314 #endif 315 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_VM_DEATH)) { 316 return; 317 } 318 ManagedThread *thread = ManagedThread::GetCurrent(); 319 LOG_IF(thread->IsThreadAlive(), FATAL, RUNTIME) << "Main Thread should have been destroyed"; 320 hooks_->VmDeath(); 321 } 322 ExceptionRevoked(ExceptionWrapper reason,ExceptionID exceptionId)323 void ExceptionRevoked(ExceptionWrapper reason, ExceptionID exceptionId) override 324 { 325 os::memory::ReadLockHolder rholder(hooks_rwlock_); 326 ASSERT(vmdeath_did_not_happen_); 327 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_EXCEPTION_REVOKED)) { 328 return; 329 } 330 ScopedNativePtHook nativeScope; 331 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 332 hooks_->ExceptionRevoked(reason, exceptionId); 333 } 334 ExecutionContextCreated(ExecutionContextWrapper context)335 void ExecutionContextCreated(ExecutionContextWrapper context) override 336 { 337 os::memory::ReadLockHolder rholder(hooks_rwlock_); 338 ASSERT(vmdeath_did_not_happen_); 339 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_EXECUTION_CONTEXT_CREATEED)) { 340 return; 341 } 342 ScopedNativePtHook nativeScope; 343 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 344 hooks_->ExecutionContextCreated(context); 345 } 346 ExecutionContextDestroyed(ExecutionContextWrapper context)347 void ExecutionContextDestroyed(ExecutionContextWrapper context) override 348 { 349 os::memory::ReadLockHolder rholder(hooks_rwlock_); 350 ASSERT(vmdeath_did_not_happen_); 351 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_EXECUTION_CONTEXT_DESTROYED)) { 352 return; 353 } 354 ScopedNativePtHook nativeScope; 355 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 356 hooks_->ExecutionContextDestroyed(context); 357 } 358 ExecutionContextsCleared()359 void ExecutionContextsCleared() override 360 { 361 os::memory::ReadLockHolder rholder(hooks_rwlock_); 362 ASSERT(vmdeath_did_not_happen_); 363 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_EXECUTION_CONTEXTS_CLEARED)) { 364 return; 365 } 366 ScopedNativePtHook nativeScope; 367 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 368 hooks_->ExecutionContextsCleared(); 369 } 370 InspectRequested(PtObject object,PtObject hints)371 void InspectRequested(PtObject object, PtObject hints) override 372 { 373 os::memory::ReadLockHolder rholder(hooks_rwlock_); 374 ASSERT(vmdeath_did_not_happen_); 375 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_INSPECT_REQUESTED)) { 376 return; 377 } 378 ScopedNativePtHook nativeScope; 379 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 380 hooks_->InspectRequested(object, hints); 381 } 382 ClassLoad(PtThread thread,PtClass klass)383 void ClassLoad(PtThread thread, PtClass klass) override 384 { 385 os::memory::ReadLockHolder rholder(hooks_rwlock_); 386 ASSERT(vmdeath_did_not_happen_); 387 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_CLASS_LOAD)) { 388 return; 389 } 390 ScopedNativePtHook nativeScope; 391 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 392 hooks_->ClassLoad(thread, klass); 393 } 394 ClassPrepare(PtThread thread,PtClass klass)395 void ClassPrepare(PtThread thread, PtClass klass) override 396 { 397 os::memory::ReadLockHolder rholder(hooks_rwlock_); 398 ASSERT(vmdeath_did_not_happen_); 399 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_CLASS_PREPARE)) { 400 return; 401 } 402 ScopedNativePtHook nativeScope; 403 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 404 hooks_->ClassPrepare(thread, klass); 405 } 406 MonitorWait(PtThread thread,PtObject object,int64_t timeout)407 void MonitorWait(PtThread thread, PtObject object, int64_t timeout) override 408 { 409 os::memory::ReadLockHolder rholder(hooks_rwlock_); 410 ASSERT(vmdeath_did_not_happen_); 411 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_MONITOR_WAIT)) { 412 return; 413 } 414 ScopedNativePtHook nativeScope; 415 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 416 hooks_->MonitorWait(thread, object, timeout); 417 } 418 MonitorWaited(PtThread thread,PtObject object,bool timedOut)419 void MonitorWaited(PtThread thread, PtObject object, bool timedOut) override 420 { 421 os::memory::ReadLockHolder rholder(hooks_rwlock_); 422 ASSERT(vmdeath_did_not_happen_); 423 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_MONITOR_WAITED)) { 424 return; 425 } 426 ScopedNativePtHook nativeScope; 427 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 428 hooks_->MonitorWaited(thread, object, timedOut); 429 } 430 MonitorContendedEnter(PtThread thread,PtObject object)431 void MonitorContendedEnter(PtThread thread, PtObject object) override 432 { 433 os::memory::ReadLockHolder rholder(hooks_rwlock_); 434 ASSERT(vmdeath_did_not_happen_); 435 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_MONITOR_CONTENDED_ENTER)) { 436 return; 437 } 438 ScopedNativePtHook nativeScope; 439 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 440 hooks_->MonitorContendedEnter(thread, object); 441 } 442 MonitorContendedEntered(PtThread thread,PtObject object)443 void MonitorContendedEntered(PtThread thread, PtObject object) override 444 { 445 os::memory::ReadLockHolder rholder(hooks_rwlock_); 446 ASSERT(vmdeath_did_not_happen_); 447 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_MONITOR_CONTENDED_ENTERED)) { 448 return; 449 } 450 ScopedNativePtHook nativeScope; 451 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 452 hooks_->MonitorContendedEntered(thread, object); 453 } 454 ObjectAlloc(PtClass klass,PtObject object,PtThread thread,size_t size)455 void ObjectAlloc(PtClass klass, PtObject object, PtThread thread, size_t size) override 456 { 457 os::memory::ReadLockHolder rholder(hooks_rwlock_); 458 ASSERT(vmdeath_did_not_happen_); 459 if (hooks_ == nullptr || !HookIsEnabled(PtHookType::PT_HOOK_TYPE_OBJECT_ALLOC)) { 460 return; 461 } 462 ScopedNativePtHook nativeScope; 463 ASSERT_PT_HOOK_NATIVE_CONTEXT(); 464 hooks_->ObjectAlloc(klass, object, thread, size); 465 } 466 467 private: GlobalHookIsEnabled(PtHookType type)468 bool GlobalHookIsEnabled(PtHookType type) const 469 { 470 return global_hook_type_info_.IsEnabled(type); 471 } 472 HookIsEnabled(PtHookType type)473 bool HookIsEnabled(PtHookType type) const 474 { 475 if (GlobalHookIsEnabled(type)) { 476 return true; 477 } 478 479 MTManagedThread *mt_managed_thread = MTManagedThread::GetCurrent(); 480 ASSERT(mt_managed_thread != nullptr); 481 482 // Check local value 483 return mt_managed_thread->GetPtThreadInfo()->GetHookTypeInfo().IsEnabled(type); 484 } 485 486 PtHooks *hooks_ = nullptr; 487 mutable os::memory::RWLock hooks_rwlock_; 488 489 PtHookTypeInfo global_hook_type_info_ {true}; 490 491 #ifndef NDEBUG 492 bool vmdeath_did_not_happen_ = true; 493 #endif 494 }; 495 496 } // namespace panda::tooling 497 498 #endif // PANDA_RUNTIME_TOOLING_PT_HOOKS_WRAPPER_H_ 499