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