• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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