1 /** 2 * Copyright (c) 2021-2022 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 #ifndef PANDA_RUNTIME_RUNTIME_NOTIFICATION_H_ 16 #define PANDA_RUNTIME_RUNTIME_NOTIFICATION_H_ 17 18 #include <optional> 19 #include <string_view> 20 21 #include "libpandabase/os/mutex.h" 22 #include "runtime/include/locks.h" 23 #include "runtime/include/mem/panda_containers.h" 24 #include "runtime/include/mem/panda_string.h" 25 #include "runtime/include/runtime.h" 26 27 namespace panda { 28 29 class Method; 30 class Class; 31 class Rendezvous; 32 33 class RuntimeListener { 34 public: 35 RuntimeListener() = default; 36 virtual ~RuntimeListener() = default; 37 DEFAULT_COPY_SEMANTIC(RuntimeListener); 38 DEFAULT_MOVE_SEMANTIC(RuntimeListener); 39 LoadModule(std::string_view name)40 virtual void LoadModule([[maybe_unused]] std::string_view name) {} 41 ThreadStart(ManagedThread * managed_thread)42 virtual void ThreadStart([[maybe_unused]] ManagedThread *managed_thread) {} ThreadEnd(ManagedThread * managed_thread)43 virtual void ThreadEnd([[maybe_unused]] ManagedThread *managed_thread) {} 44 BytecodePcChanged(ManagedThread * thread,Method * method,uint32_t bc_offset)45 virtual void BytecodePcChanged([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Method *method, 46 [[maybe_unused]] uint32_t bc_offset) 47 { 48 } 49 GarbageCollectorStart()50 virtual void GarbageCollectorStart() {} GarbageCollectorFinish()51 virtual void GarbageCollectorFinish() {} 52 ExceptionThrow(ManagedThread * thread,Method * method,ObjectHeader * exception_object,uint32_t bc_offset)53 virtual void ExceptionThrow([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Method *method, 54 [[maybe_unused]] ObjectHeader *exception_object, [[maybe_unused]] uint32_t bc_offset) 55 { 56 } 57 ExceptionCatch(ManagedThread * thread,Method * method,ObjectHeader * exception_object,uint32_t bc_offset)58 virtual void ExceptionCatch([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Method *method, 59 [[maybe_unused]] ObjectHeader *exception_object, [[maybe_unused]] uint32_t bc_offset) 60 { 61 } 62 VmStart()63 virtual void VmStart() {} VmInitialization(ManagedThread * managed_thread)64 virtual void VmInitialization([[maybe_unused]] ManagedThread *managed_thread) {} VmDeath()65 virtual void VmDeath() {} 66 MethodEntry(ManagedThread * thread,Method * method)67 virtual void MethodEntry([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Method *method) {} MethodExit(ManagedThread * thread,Method * method)68 virtual void MethodExit([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] Method *method) {} 69 ClassLoad(Class * klass)70 virtual void ClassLoad([[maybe_unused]] Class *klass) {} ClassPrepare(Class * klass)71 virtual void ClassPrepare([[maybe_unused]] Class *klass) {} 72 MonitorWait(ObjectHeader * object,int64_t timeout)73 virtual void MonitorWait([[maybe_unused]] ObjectHeader *object, [[maybe_unused]] int64_t timeout) {} MonitorWaited(ObjectHeader * object,bool timed_out)74 virtual void MonitorWaited([[maybe_unused]] ObjectHeader *object, [[maybe_unused]] bool timed_out) {} MonitorContendedEnter(ObjectHeader * object)75 virtual void MonitorContendedEnter([[maybe_unused]] ObjectHeader *object) {} MonitorContendedEntered(ObjectHeader * object)76 virtual void MonitorContendedEntered([[maybe_unused]] ObjectHeader *object) {} 77 ObjectAlloc(BaseClass * klass,ObjectHeader * object,ManagedThread * thread,size_t size)78 virtual void ObjectAlloc([[maybe_unused]] BaseClass *klass, [[maybe_unused]] ObjectHeader *object, 79 [[maybe_unused]] ManagedThread *thread, [[maybe_unused]] size_t size) 80 { 81 } 82 83 // Deprecated events ThreadStart(ManagedThread::ThreadId thread_id)84 virtual void ThreadStart([[maybe_unused]] ManagedThread::ThreadId thread_id) {} ThreadEnd(ManagedThread::ThreadId thread_id)85 virtual void ThreadEnd([[maybe_unused]] ManagedThread::ThreadId thread_id) {} VmInitialization(ManagedThread::ThreadId thread_id)86 virtual void VmInitialization([[maybe_unused]] ManagedThread::ThreadId thread_id) {} ExceptionCatch(const ManagedThread * thread,const Method * method,uint32_t bc_offset)87 virtual void ExceptionCatch([[maybe_unused]] const ManagedThread *thread, [[maybe_unused]] const Method *method, 88 [[maybe_unused]] uint32_t bc_offset) 89 { 90 } 91 }; 92 93 class DebuggerListener { 94 public: 95 DebuggerListener() = default; 96 virtual ~DebuggerListener() = default; 97 DEFAULT_COPY_SEMANTIC(DebuggerListener); 98 DEFAULT_MOVE_SEMANTIC(DebuggerListener); 99 100 virtual void StartDebugger() = 0; 101 virtual void StopDebugger() = 0; 102 virtual bool IsDebuggerConfigured() = 0; 103 }; 104 105 class RuntimeNotificationManager { 106 public: 107 enum Event : uint32_t { 108 BYTECODE_PC_CHANGED = 0x01, 109 LOAD_MODULE = 0x02, 110 THREAD_EVENTS = 0x04, 111 GARBAGE_COLLECTOR_EVENTS = 0x08, 112 EXCEPTION_EVENTS = 0x10, 113 VM_EVENTS = 0x20, 114 METHOD_EVENTS = 0x40, 115 CLASS_EVENTS = 0x80, 116 MONITOR_EVENTS = 0x100, 117 ALLOCATION_EVENTS = 0x200, 118 ALL = 0xFFFFFFFF 119 }; 120 RuntimeNotificationManager(mem::AllocatorPtr<mem::AllocatorPurpose::ALLOCATOR_PURPOSE_INTERNAL> allocator)121 explicit RuntimeNotificationManager(mem::AllocatorPtr<mem::AllocatorPurpose::ALLOCATOR_PURPOSE_INTERNAL> allocator) 122 : bytecode_pc_listeners_(allocator->Adapter()), 123 load_module_listeners_(allocator->Adapter()), 124 thread_events_listeners_(allocator->Adapter()), 125 garbage_collector_listeners_(allocator->Adapter()), 126 exception_listeners_(allocator->Adapter()), 127 vm_events_listeners_(allocator->Adapter()), 128 method_listeners_(allocator->Adapter()), 129 class_listeners_(allocator->Adapter()), 130 monitor_listeners_(allocator->Adapter()) 131 { 132 } 133 AddListener(RuntimeListener * listener,uint32_t event_mask)134 void AddListener(RuntimeListener *listener, uint32_t event_mask) 135 { 136 ScopedSuspendAllThreads ssat(rendezvous_); 137 AddListenerIfMatches(listener, event_mask, &bytecode_pc_listeners_, Event::BYTECODE_PC_CHANGED, 138 &has_bytecode_pc_listeners_); 139 140 AddListenerIfMatches(listener, event_mask, &load_module_listeners_, Event::LOAD_MODULE, 141 &has_load_module_listeners_); 142 143 AddListenerIfMatches(listener, event_mask, &thread_events_listeners_, Event::THREAD_EVENTS, 144 &has_thread_events_listeners_); 145 146 AddListenerIfMatches(listener, event_mask, &exception_listeners_, Event::EXCEPTION_EVENTS, 147 &has_exception_listeners_); 148 149 AddListenerIfMatches(listener, event_mask, &vm_events_listeners_, Event::VM_EVENTS, &has_vm_events_listeners_); 150 151 AddListenerIfMatches(listener, event_mask, &method_listeners_, Event::METHOD_EVENTS, &has_method_listeners_); 152 153 AddListenerIfMatches(listener, event_mask, &class_listeners_, Event::CLASS_EVENTS, &has_class_listeners_); 154 155 AddListenerIfMatches(listener, event_mask, &monitor_listeners_, Event::MONITOR_EVENTS, &has_monitor_listeners_); 156 157 AddListenerIfMatches(listener, event_mask, &allocation_listeners_, Event::ALLOCATION_EVENTS, 158 &has_allocation_listeners_); 159 160 { 161 // We cannot stop GC thread, so holding lock to avoid data race 162 os::memory::WriteLockHolder rwlock(gc_event_lock_); 163 AddListenerIfMatches(listener, event_mask, &garbage_collector_listeners_, Event::GARBAGE_COLLECTOR_EVENTS, 164 &has_garbage_collector_listeners_); 165 } 166 } 167 RemoveListener(RuntimeListener * listener,uint32_t event_mask)168 void RemoveListener(RuntimeListener *listener, uint32_t event_mask) 169 { 170 ScopedSuspendAllThreads ssat(rendezvous_); 171 RemoveListenerIfMatches(listener, event_mask, &bytecode_pc_listeners_, Event::BYTECODE_PC_CHANGED, 172 &has_bytecode_pc_listeners_); 173 174 RemoveListenerIfMatches(listener, event_mask, &load_module_listeners_, Event::LOAD_MODULE, 175 &has_load_module_listeners_); 176 177 RemoveListenerIfMatches(listener, event_mask, &thread_events_listeners_, Event::THREAD_EVENTS, 178 &has_thread_events_listeners_); 179 180 RemoveListenerIfMatches(listener, event_mask, &exception_listeners_, Event::EXCEPTION_EVENTS, 181 &has_exception_listeners_); 182 183 RemoveListenerIfMatches(listener, event_mask, &vm_events_listeners_, Event::VM_EVENTS, 184 &has_vm_events_listeners_); 185 186 RemoveListenerIfMatches(listener, event_mask, &method_listeners_, Event::METHOD_EVENTS, &has_method_listeners_); 187 188 RemoveListenerIfMatches(listener, event_mask, &class_listeners_, Event::CLASS_EVENTS, &has_class_listeners_); 189 190 RemoveListenerIfMatches(listener, event_mask, &monitor_listeners_, Event::MONITOR_EVENTS, 191 &has_monitor_listeners_); 192 193 RemoveListenerIfMatches(listener, event_mask, &allocation_listeners_, Event::ALLOCATION_EVENTS, 194 &has_allocation_listeners_); 195 196 { 197 // We cannot stop GC thread, so holding lock to avoid data race 198 os::memory::WriteLockHolder rwlock(gc_event_lock_); 199 RemoveListenerIfMatches(listener, event_mask, &garbage_collector_listeners_, 200 Event::GARBAGE_COLLECTOR_EVENTS, &has_garbage_collector_listeners_); 201 } 202 } 203 LoadModuleEvent(std::string_view name)204 void LoadModuleEvent(std::string_view name) 205 { 206 if (UNLIKELY(has_load_module_listeners_)) { 207 for (auto *listener : load_module_listeners_) { 208 if (listener != nullptr) { 209 listener->LoadModule(name); 210 } 211 } 212 } 213 } 214 ThreadStartEvent(ManagedThread * managed_thread)215 void ThreadStartEvent(ManagedThread *managed_thread) 216 { 217 if (UNLIKELY(has_thread_events_listeners_)) { 218 for (auto *listener : thread_events_listeners_) { 219 if (listener != nullptr) { 220 listener->ThreadStart(managed_thread); 221 } 222 } 223 } 224 } 225 ThreadEndEvent(ManagedThread * managed_thread)226 void ThreadEndEvent(ManagedThread *managed_thread) 227 { 228 if (UNLIKELY(has_thread_events_listeners_)) { 229 for (auto *listener : thread_events_listeners_) { 230 if (listener != nullptr) { 231 listener->ThreadEnd(managed_thread); 232 } 233 } 234 } 235 } 236 BytecodePcChangedEvent(ManagedThread * thread,Method * method,uint32_t bc_offset)237 void BytecodePcChangedEvent(ManagedThread *thread, Method *method, uint32_t bc_offset) 238 { 239 if (UNLIKELY(has_bytecode_pc_listeners_)) { 240 for (auto *listener : bytecode_pc_listeners_) { 241 if (listener != nullptr) { 242 listener->BytecodePcChanged(thread, method, bc_offset); 243 } 244 } 245 } 246 } 247 GarbageCollectorStartEvent()248 void GarbageCollectorStartEvent() 249 { 250 if (UNLIKELY(has_garbage_collector_listeners_)) { 251 os::memory::ReadLockHolder rwlock(gc_event_lock_); 252 for (auto *listener : garbage_collector_listeners_) { 253 if (listener != nullptr) { 254 listener->GarbageCollectorStart(); 255 } 256 } 257 } 258 } 259 GarbageCollectorFinishEvent()260 void GarbageCollectorFinishEvent() 261 { 262 if (UNLIKELY(has_garbage_collector_listeners_)) { 263 os::memory::ReadLockHolder rwlock(gc_event_lock_); 264 for (auto *listener : garbage_collector_listeners_) { 265 if (listener != nullptr) { 266 listener->GarbageCollectorFinish(); 267 } 268 } 269 } 270 } 271 ExceptionThrowEvent(ManagedThread * thread,Method * method,ObjectHeader * exception_object,uint32_t bc_offset)272 void ExceptionThrowEvent(ManagedThread *thread, Method *method, ObjectHeader *exception_object, uint32_t bc_offset) 273 { 274 if (UNLIKELY(has_exception_listeners_)) { 275 for (auto *listener : exception_listeners_) { 276 if (listener != nullptr) { 277 listener->ExceptionThrow(thread, method, exception_object, bc_offset); 278 } 279 } 280 } 281 } 282 ExceptionCatchEvent(ManagedThread * thread,Method * method,ObjectHeader * exception_object,uint32_t bc_offset)283 void ExceptionCatchEvent(ManagedThread *thread, Method *method, ObjectHeader *exception_object, uint32_t bc_offset) 284 { 285 if (UNLIKELY(has_exception_listeners_)) { 286 for (auto *listener : exception_listeners_) { 287 if (listener != nullptr) { 288 listener->ExceptionCatch(thread, method, exception_object, bc_offset); 289 } 290 } 291 } 292 } 293 VmStartEvent()294 void VmStartEvent() 295 { 296 if (UNLIKELY(has_vm_events_listeners_)) { 297 for (auto *listener : vm_events_listeners_) { 298 if (listener != nullptr) { 299 listener->VmStart(); 300 } 301 } 302 } 303 } 304 VmInitializationEvent(ManagedThread * managed_thread)305 void VmInitializationEvent(ManagedThread *managed_thread) 306 { 307 if (UNLIKELY(has_vm_events_listeners_)) { 308 for (auto *listener : vm_events_listeners_) { 309 if (listener != nullptr) { 310 listener->VmInitialization(managed_thread); 311 } 312 } 313 } 314 } 315 316 // Deprecated API VmInitializationEvent(ManagedThread::ThreadId thread_id)317 void VmInitializationEvent(ManagedThread::ThreadId thread_id) 318 { 319 if (UNLIKELY(has_vm_events_listeners_)) { 320 for (auto *listener : vm_events_listeners_) { 321 if (listener != nullptr) { 322 listener->VmInitialization(thread_id); 323 } 324 } 325 } 326 } 327 VmDeathEvent()328 void VmDeathEvent() 329 { 330 if (UNLIKELY(has_vm_events_listeners_)) { 331 for (auto *listener : vm_events_listeners_) { 332 if (listener != nullptr) { 333 listener->VmDeath(); 334 } 335 } 336 } 337 } 338 MethodEntryEvent(ManagedThread * thread,Method * method)339 void MethodEntryEvent(ManagedThread *thread, Method *method) 340 { 341 if (UNLIKELY(has_method_listeners_)) { 342 for (auto *listener : method_listeners_) { 343 if (listener != nullptr) { 344 listener->MethodEntry(thread, method); 345 } 346 } 347 } 348 } 349 MethodExitEvent(ManagedThread * thread,Method * method)350 void MethodExitEvent(ManagedThread *thread, Method *method) 351 { 352 if (UNLIKELY(has_method_listeners_)) { 353 for (auto *listener : method_listeners_) { 354 if (listener != nullptr) { 355 listener->MethodExit(thread, method); 356 } 357 } 358 } 359 } 360 ClassLoadEvent(Class * klass)361 void ClassLoadEvent(Class *klass) 362 { 363 if (UNLIKELY(has_class_listeners_)) { 364 for (auto *listener : class_listeners_) { 365 if (listener != nullptr) { 366 listener->ClassLoad(klass); 367 } 368 } 369 } 370 } 371 ClassPrepareEvent(Class * klass)372 void ClassPrepareEvent(Class *klass) 373 { 374 if (UNLIKELY(has_class_listeners_)) { 375 for (auto *listener : class_listeners_) { 376 if (listener != nullptr) { 377 listener->ClassPrepare(klass); 378 } 379 } 380 } 381 } 382 MonitorWaitEvent(ObjectHeader * object,int64_t timeout)383 void MonitorWaitEvent(ObjectHeader *object, int64_t timeout) 384 { 385 if (UNLIKELY(has_monitor_listeners_)) { 386 // If we need to support multiple monitor listeners, 387 // the object must be wrapped to ObjectHandle to protect from GC move 388 ASSERT(monitor_listeners_.size() == 1); 389 auto *listener = monitor_listeners_.front(); 390 if (listener != nullptr) { 391 listener->MonitorWait(object, timeout); 392 } 393 } 394 } 395 MonitorWaitedEvent(ObjectHeader * object,bool timed_out)396 void MonitorWaitedEvent(ObjectHeader *object, bool timed_out) 397 { 398 if (UNLIKELY(has_monitor_listeners_)) { 399 // If we need to support multiple monitor listeners, 400 // the object must be wrapped to ObjectHandle to protect from GC move 401 ASSERT(monitor_listeners_.size() == 1); 402 auto *listener = monitor_listeners_.front(); 403 if (listener != nullptr) { 404 monitor_listeners_.front()->MonitorWaited(object, timed_out); 405 } 406 } 407 } 408 MonitorContendedEnterEvent(ObjectHeader * object)409 void MonitorContendedEnterEvent(ObjectHeader *object) 410 { 411 if (UNLIKELY(has_monitor_listeners_)) { 412 // If we need to support multiple monitor listeners, 413 // the object must be wrapped to ObjectHandle to protect from GC move 414 ASSERT(monitor_listeners_.size() == 1); 415 auto *listener = monitor_listeners_.front(); 416 if (listener != nullptr) { 417 monitor_listeners_.front()->MonitorContendedEnter(object); 418 } 419 } 420 } 421 MonitorContendedEnteredEvent(ObjectHeader * object)422 void MonitorContendedEnteredEvent(ObjectHeader *object) 423 { 424 if (UNLIKELY(has_monitor_listeners_)) { 425 // If we need to support multiple monitor listeners, 426 // the object must be wrapped to ObjectHandle to protect from GC move 427 ASSERT(monitor_listeners_.size() == 1); 428 auto *listener = monitor_listeners_.front(); 429 if (listener != nullptr) { 430 monitor_listeners_.front()->MonitorContendedEntered(object); 431 } 432 } 433 } 434 HasAllocationListeners()435 bool HasAllocationListeners() const 436 { 437 return has_allocation_listeners_; 438 } 439 ObjectAllocEvent(BaseClass * klass,ObjectHeader * object,ManagedThread * thread,size_t size)440 void ObjectAllocEvent(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size) const 441 { 442 if (UNLIKELY(has_allocation_listeners_)) { 443 // If we need to support multiple allocation listeners, 444 // the object must be wrapped to ObjectHandle to protect from GC move 445 ASSERT(allocation_listeners_.size() == 1); 446 auto *listener = allocation_listeners_.front(); 447 if (listener != nullptr) { 448 allocation_listeners_.front()->ObjectAlloc(klass, object, thread, size); 449 } 450 } 451 } 452 StartDebugger()453 void StartDebugger() 454 { 455 os::memory::ReadLockHolder holder(debugger_lock_); 456 for (auto *listener : debugger_listeners_) { 457 listener->StartDebugger(); 458 } 459 } 460 StopDebugger()461 void StopDebugger() 462 { 463 os::memory::ReadLockHolder holder(debugger_lock_); 464 for (auto *listener : debugger_listeners_) { 465 listener->StopDebugger(); 466 } 467 } 468 IsDebuggerConfigured()469 bool IsDebuggerConfigured() 470 { 471 os::memory::ReadLockHolder holder(debugger_lock_); 472 for (auto *listener : debugger_listeners_) { 473 if (!listener->IsDebuggerConfigured()) { 474 return false; 475 } 476 } 477 return true; 478 } 479 SetRendezvous(Rendezvous * rendezvous)480 void SetRendezvous(Rendezvous *rendezvous) 481 { 482 rendezvous_ = rendezvous; 483 } 484 AddDebuggerListener(DebuggerListener * listener)485 void AddDebuggerListener(DebuggerListener *listener) 486 { 487 os::memory::WriteLockHolder holder(debugger_lock_); 488 debugger_listeners_.push_back(listener); 489 } 490 RemoveDebuggerListener(DebuggerListener * listener)491 void RemoveDebuggerListener(DebuggerListener *listener) 492 { 493 os::memory::WriteLockHolder holder(debugger_lock_); 494 RemoveListener(debugger_listeners_, listener); 495 } 496 497 private: 498 template <typename FlagType> AddListenerIfMatches(RuntimeListener * listener,uint32_t event_mask,PandaList<RuntimeListener * > * listener_group,Event event_modifier,FlagType * event_flag)499 static void AddListenerIfMatches(RuntimeListener *listener, uint32_t event_mask, 500 PandaList<RuntimeListener *> *listener_group, Event event_modifier, 501 FlagType *event_flag) 502 { 503 if ((event_mask & event_modifier) != 0) { 504 // If a free group item presents, use it, otherwise push back a new item 505 auto it = std::find(listener_group->begin(), listener_group->end(), nullptr); 506 if (it != listener_group->end()) { 507 *it = listener; 508 } else { 509 listener_group->push_back(listener); 510 } 511 *event_flag = true; 512 } 513 } 514 515 template <typename Container, typename T> RemoveListener(Container & c,T & listener)516 ALWAYS_INLINE void RemoveListener(Container &c, T &listener) 517 { 518 c.erase(std::remove_if(c.begin(), c.end(), [&listener](const T &elem) { return listener == elem; })); 519 } 520 521 template <typename FlagType> RemoveListenerIfMatches(RuntimeListener * listener,uint32_t event_mask,PandaList<RuntimeListener * > * listener_group,Event event_modifier,FlagType * event_flag)522 static void RemoveListenerIfMatches(RuntimeListener *listener, uint32_t event_mask, 523 PandaList<RuntimeListener *> *listener_group, Event event_modifier, 524 FlagType *event_flag) 525 { 526 if ((event_mask & event_modifier) != 0) { 527 auto it = std::find(listener_group->begin(), listener_group->end(), listener); 528 if (it == listener_group->end()) { 529 return; 530 } 531 // Removing a listener is not safe, because the iteration can not be completed in another thread. 532 // We just null the item in the group 533 *it = nullptr; 534 535 // Check if any listener presents and update the flag if not 536 if (std::find_if(listener_group->begin(), listener_group->end(), 537 [](RuntimeListener *item) { return item != nullptr; }) == listener_group->end()) { 538 *event_flag = false; 539 } 540 } 541 } 542 543 PandaList<RuntimeListener *> bytecode_pc_listeners_; 544 PandaList<RuntimeListener *> load_module_listeners_; 545 PandaList<RuntimeListener *> thread_events_listeners_; 546 PandaList<RuntimeListener *> garbage_collector_listeners_; 547 PandaList<RuntimeListener *> exception_listeners_; 548 PandaList<RuntimeListener *> vm_events_listeners_; 549 PandaList<RuntimeListener *> method_listeners_; 550 PandaList<RuntimeListener *> class_listeners_; 551 PandaList<RuntimeListener *> monitor_listeners_; 552 PandaList<RuntimeListener *> allocation_listeners_; 553 554 bool has_bytecode_pc_listeners_ = false; 555 bool has_load_module_listeners_ = false; 556 bool has_thread_events_listeners_ = false; 557 std::atomic<bool> has_garbage_collector_listeners_ = false; 558 bool has_exception_listeners_ = false; 559 bool has_vm_events_listeners_ = false; 560 bool has_method_listeners_ = false; 561 bool has_class_listeners_ = false; 562 bool has_monitor_listeners_ = false; 563 bool has_allocation_listeners_ = false; 564 Rendezvous *rendezvous_ {nullptr}; 565 566 os::memory::RWLock debugger_lock_; 567 os::memory::RWLock gc_event_lock_; 568 PandaList<DebuggerListener *> debugger_listeners_; 569 }; 570 571 } // namespace panda 572 573 #endif // PANDA_RUNTIME_RUNTIME_NOTIFICATION_H_ 574