Fix broken handling of instrumentation lists.
- We cannot copy before iterating, as entries might be deleted.
- We cannot remove entries in the list, as mutators could be
currently iterating over it.
Solution in this change is to never remove list entries, but
put null when a listener is removed. When adding a listener, we
will either put it where there is a null slot, or at the end
of the list if there is no null slot.
Change-Id: Id94582fd971cd56bcb445caff64270d21987f700
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 4db37e6..e937397 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -387,146 +387,151 @@
return (events & expected) != 0;
}
+static void PotentiallyAddListenerTo(Instrumentation::InstrumentationEvent event,
+ uint32_t events,
+ std::list<InstrumentationListener*>& list,
+ InstrumentationListener* listener,
+ bool* has_listener)
+ REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_) {
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+ if (!HasEvent(event, events)) {
+ return;
+ }
+ // If there is a free slot in the list, we insert the listener in that slot.
+ // Otherwise we add it to the end of the list.
+ auto it = std::find(list.begin(), list.end(), nullptr);
+ if (it != list.end()) {
+ *it = listener;
+ } else {
+ list.push_back(listener);
+ }
+ *has_listener = true;
+}
+
void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t events) {
Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
- if (HasEvent(kMethodEntered, events)) {
- method_entry_listeners_.push_back(listener);
- have_method_entry_listeners_ = true;
- }
- if (HasEvent(kMethodExited, events)) {
- method_exit_listeners_.push_back(listener);
- have_method_exit_listeners_ = true;
- }
- if (HasEvent(kMethodUnwind, events)) {
- method_unwind_listeners_.push_back(listener);
- have_method_unwind_listeners_ = true;
- }
- if (HasEvent(kBackwardBranch, events)) {
- backward_branch_listeners_.push_back(listener);
- have_backward_branch_listeners_ = true;
- }
- if (HasEvent(kInvokeVirtualOrInterface, events)) {
- invoke_virtual_or_interface_listeners_.push_back(listener);
- have_invoke_virtual_or_interface_listeners_ = true;
- }
- if (HasEvent(kDexPcMoved, events)) {
- std::list<InstrumentationListener*>* modified;
- if (have_dex_pc_listeners_) {
- modified = new std::list<InstrumentationListener*>(*dex_pc_listeners_.get());
- } else {
- modified = new std::list<InstrumentationListener*>();
- }
- modified->push_back(listener);
- dex_pc_listeners_.reset(modified);
- have_dex_pc_listeners_ = true;
- }
- if (HasEvent(kFieldRead, events)) {
- std::list<InstrumentationListener*>* modified;
- if (have_field_read_listeners_) {
- modified = new std::list<InstrumentationListener*>(*field_read_listeners_.get());
- } else {
- modified = new std::list<InstrumentationListener*>();
- }
- modified->push_back(listener);
- field_read_listeners_.reset(modified);
- have_field_read_listeners_ = true;
- }
- if (HasEvent(kFieldWritten, events)) {
- std::list<InstrumentationListener*>* modified;
- if (have_field_write_listeners_) {
- modified = new std::list<InstrumentationListener*>(*field_write_listeners_.get());
- } else {
- modified = new std::list<InstrumentationListener*>();
- }
- modified->push_back(listener);
- field_write_listeners_.reset(modified);
- have_field_write_listeners_ = true;
- }
- if (HasEvent(kExceptionCaught, events)) {
- std::list<InstrumentationListener*>* modified;
- if (have_exception_caught_listeners_) {
- modified = new std::list<InstrumentationListener*>(*exception_caught_listeners_.get());
- } else {
- modified = new std::list<InstrumentationListener*>();
- }
- modified->push_back(listener);
- exception_caught_listeners_.reset(modified);
- have_exception_caught_listeners_ = true;
- }
+ PotentiallyAddListenerTo(kMethodEntered,
+ events,
+ method_entry_listeners_,
+ listener,
+ &have_method_entry_listeners_);
+ PotentiallyAddListenerTo(kMethodExited,
+ events,
+ method_exit_listeners_,
+ listener,
+ &have_method_exit_listeners_);
+ PotentiallyAddListenerTo(kMethodUnwind,
+ events,
+ method_unwind_listeners_,
+ listener,
+ &have_method_unwind_listeners_);
+ PotentiallyAddListenerTo(kBackwardBranch,
+ events,
+ backward_branch_listeners_,
+ listener,
+ &have_backward_branch_listeners_);
+ PotentiallyAddListenerTo(kInvokeVirtualOrInterface,
+ events,
+ invoke_virtual_or_interface_listeners_,
+ listener,
+ &have_invoke_virtual_or_interface_listeners_);
+ PotentiallyAddListenerTo(kDexPcMoved,
+ events,
+ dex_pc_listeners_,
+ listener,
+ &have_dex_pc_listeners_);
+ PotentiallyAddListenerTo(kFieldRead,
+ events,
+ field_read_listeners_,
+ listener,
+ &have_field_read_listeners_);
+ PotentiallyAddListenerTo(kFieldWritten,
+ events,
+ field_write_listeners_,
+ listener,
+ &have_field_write_listeners_);
+ PotentiallyAddListenerTo(kExceptionCaught,
+ events,
+ exception_caught_listeners_,
+ listener,
+ &have_exception_caught_listeners_);
UpdateInterpreterHandlerTable();
}
+static void PotentiallyRemoveListenerFrom(Instrumentation::InstrumentationEvent event,
+ uint32_t events,
+ std::list<InstrumentationListener*>& list,
+ InstrumentationListener* listener,
+ bool* has_listener)
+ REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_) {
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+ if (!HasEvent(event, events)) {
+ return;
+ }
+ auto it = std::find(list.begin(), list.end(), listener);
+ if (it != list.end()) {
+ // Just update the entry, do not remove from the list. Removing entries in the list
+ // is unsafe when mutators are iterating over it.
+ *it = nullptr;
+ }
+
+ // Check if the list contains any non-null listener, and update 'has_listener'.
+ for (InstrumentationListener* l : list) {
+ if (l != nullptr) {
+ *has_listener = true;
+ return;
+ }
+ }
+ *has_listener = false;
+}
+
void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) {
Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
-
- if (HasEvent(kMethodEntered, events) && have_method_entry_listeners_) {
- method_entry_listeners_.remove(listener);
- have_method_entry_listeners_ = !method_entry_listeners_.empty();
- }
- if (HasEvent(kMethodExited, events) && have_method_exit_listeners_) {
- method_exit_listeners_.remove(listener);
- have_method_exit_listeners_ = !method_exit_listeners_.empty();
- }
- if (HasEvent(kMethodUnwind, events) && have_method_unwind_listeners_) {
- method_unwind_listeners_.remove(listener);
- have_method_unwind_listeners_ = !method_unwind_listeners_.empty();
- }
- if (HasEvent(kBackwardBranch, events) && have_backward_branch_listeners_) {
- backward_branch_listeners_.remove(listener);
- have_backward_branch_listeners_ = !backward_branch_listeners_.empty();
- }
- if (HasEvent(kInvokeVirtualOrInterface, events) && have_invoke_virtual_or_interface_listeners_) {
- invoke_virtual_or_interface_listeners_.remove(listener);
- have_invoke_virtual_or_interface_listeners_ = !invoke_virtual_or_interface_listeners_.empty();
- }
- if (HasEvent(kDexPcMoved, events) && have_dex_pc_listeners_) {
- std::list<InstrumentationListener*>* modified =
- new std::list<InstrumentationListener*>(*dex_pc_listeners_.get());
- modified->remove(listener);
- have_dex_pc_listeners_ = !modified->empty();
- if (have_dex_pc_listeners_) {
- dex_pc_listeners_.reset(modified);
- } else {
- dex_pc_listeners_.reset();
- delete modified;
- }
- }
- if (HasEvent(kFieldRead, events) && have_field_read_listeners_) {
- std::list<InstrumentationListener*>* modified =
- new std::list<InstrumentationListener*>(*field_read_listeners_.get());
- modified->remove(listener);
- have_field_read_listeners_ = !modified->empty();
- if (have_field_read_listeners_) {
- field_read_listeners_.reset(modified);
- } else {
- field_read_listeners_.reset();
- delete modified;
- }
- }
- if (HasEvent(kFieldWritten, events) && have_field_write_listeners_) {
- std::list<InstrumentationListener*>* modified =
- new std::list<InstrumentationListener*>(*field_write_listeners_.get());
- modified->remove(listener);
- have_field_write_listeners_ = !modified->empty();
- if (have_field_write_listeners_) {
- field_write_listeners_.reset(modified);
- } else {
- field_write_listeners_.reset();
- delete modified;
- }
- }
- if (HasEvent(kExceptionCaught, events) && have_exception_caught_listeners_) {
- std::list<InstrumentationListener*>* modified =
- new std::list<InstrumentationListener*>(*exception_caught_listeners_.get());
- modified->remove(listener);
- have_exception_caught_listeners_ = !modified->empty();
- if (have_exception_caught_listeners_) {
- exception_caught_listeners_.reset(modified);
- } else {
- exception_caught_listeners_.reset();
- delete modified;
- }
- }
+ PotentiallyRemoveListenerFrom(kMethodEntered,
+ events,
+ method_entry_listeners_,
+ listener,
+ &have_method_entry_listeners_);
+ PotentiallyRemoveListenerFrom(kMethodExited,
+ events,
+ method_exit_listeners_,
+ listener,
+ &have_method_exit_listeners_);
+ PotentiallyRemoveListenerFrom(kMethodUnwind,
+ events,
+ method_unwind_listeners_,
+ listener,
+ &have_method_unwind_listeners_);
+ PotentiallyRemoveListenerFrom(kBackwardBranch,
+ events,
+ backward_branch_listeners_,
+ listener,
+ &have_backward_branch_listeners_);
+ PotentiallyRemoveListenerFrom(kInvokeVirtualOrInterface,
+ events,
+ invoke_virtual_or_interface_listeners_,
+ listener,
+ &have_invoke_virtual_or_interface_listeners_);
+ PotentiallyRemoveListenerFrom(kDexPcMoved,
+ events,
+ dex_pc_listeners_,
+ listener,
+ &have_dex_pc_listeners_);
+ PotentiallyRemoveListenerFrom(kFieldRead,
+ events,
+ field_read_listeners_,
+ listener,
+ &have_field_read_listeners_);
+ PotentiallyRemoveListenerFrom(kFieldWritten,
+ events,
+ field_write_listeners_,
+ listener,
+ &have_field_write_listeners_);
+ PotentiallyRemoveListenerFrom(kExceptionCaught,
+ events,
+ exception_caught_listeners_,
+ listener,
+ &have_exception_caught_listeners_);
UpdateInterpreterHandlerTable();
}
@@ -861,28 +866,24 @@
void Instrumentation::MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
ArtMethod* method,
uint32_t dex_pc) const {
- auto it = method_entry_listeners_.begin();
- bool is_end = (it == method_entry_listeners_.end());
- // Implemented this way to prevent problems caused by modification of the list while iterating.
- while (!is_end) {
- InstrumentationListener* cur = *it;
- ++it;
- is_end = (it == method_entry_listeners_.end());
- cur->MethodEntered(thread, this_object, method, dex_pc);
+ if (HasMethodEntryListeners()) {
+ for (InstrumentationListener* listener : method_entry_listeners_) {
+ if (listener != nullptr) {
+ listener->MethodEntered(thread, this_object, method, dex_pc);
+ }
+ }
}
}
void Instrumentation::MethodExitEventImpl(Thread* thread, mirror::Object* this_object,
ArtMethod* method,
uint32_t dex_pc, const JValue& return_value) const {
- auto it = method_exit_listeners_.begin();
- bool is_end = (it == method_exit_listeners_.end());
- // Implemented this way to prevent problems caused by modification of the list while iterating.
- while (!is_end) {
- InstrumentationListener* cur = *it;
- ++it;
- is_end = (it == method_exit_listeners_.end());
- cur->MethodExited(thread, this_object, method, dex_pc, return_value);
+ if (HasMethodExitListeners()) {
+ for (InstrumentationListener* listener : method_exit_listeners_) {
+ if (listener != nullptr) {
+ listener->MethodExited(thread, this_object, method, dex_pc, return_value);
+ }
+ }
}
}
@@ -891,7 +892,9 @@
uint32_t dex_pc) const {
if (HasMethodUnwindListeners()) {
for (InstrumentationListener* listener : method_unwind_listeners_) {
- listener->MethodUnwind(thread, this_object, method, dex_pc);
+ if (listener != nullptr) {
+ listener->MethodUnwind(thread, this_object, method, dex_pc);
+ }
}
}
}
@@ -899,16 +902,19 @@
void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,
ArtMethod* method,
uint32_t dex_pc) const {
- std::shared_ptr<std::list<InstrumentationListener*>> original(dex_pc_listeners_);
- for (InstrumentationListener* listener : *original.get()) {
- listener->DexPcMoved(thread, this_object, method, dex_pc);
+ for (InstrumentationListener* listener : dex_pc_listeners_) {
+ if (listener != nullptr) {
+ listener->DexPcMoved(thread, this_object, method, dex_pc);
+ }
}
}
void Instrumentation::BackwardBranchImpl(Thread* thread, ArtMethod* method,
int32_t offset) const {
for (InstrumentationListener* listener : backward_branch_listeners_) {
- listener->BackwardBranch(thread, method, offset);
+ if (listener != nullptr) {
+ listener->BackwardBranch(thread, method, offset);
+ }
}
}
@@ -918,25 +924,29 @@
uint32_t dex_pc,
ArtMethod* callee) const {
for (InstrumentationListener* listener : invoke_virtual_or_interface_listeners_) {
- listener->InvokeVirtualOrInterface(thread, this_object, caller, dex_pc, callee);
+ if (listener != nullptr) {
+ listener->InvokeVirtualOrInterface(thread, this_object, caller, dex_pc, callee);
+ }
}
}
void Instrumentation::FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
ArtMethod* method, uint32_t dex_pc,
ArtField* field) const {
- std::shared_ptr<std::list<InstrumentationListener*>> original(field_read_listeners_);
- for (InstrumentationListener* listener : *original.get()) {
- listener->FieldRead(thread, this_object, method, dex_pc, field);
+ for (InstrumentationListener* listener : field_read_listeners_) {
+ if (listener != nullptr) {
+ listener->FieldRead(thread, this_object, method, dex_pc, field);
+ }
}
}
void Instrumentation::FieldWriteEventImpl(Thread* thread, mirror::Object* this_object,
ArtMethod* method, uint32_t dex_pc,
ArtField* field, const JValue& field_value) const {
- std::shared_ptr<std::list<InstrumentationListener*>> original(field_write_listeners_);
- for (InstrumentationListener* listener : *original.get()) {
- listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value);
+ for (InstrumentationListener* listener : field_write_listeners_) {
+ if (listener != nullptr) {
+ listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value);
+ }
}
}
@@ -945,9 +955,10 @@
if (HasExceptionCaughtListeners()) {
DCHECK_EQ(thread->GetException(), exception_object);
thread->ClearException();
- std::shared_ptr<std::list<InstrumentationListener*>> original(exception_caught_listeners_);
- for (InstrumentationListener* listener : *original.get()) {
- listener->ExceptionCaught(thread, exception_object);
+ for (InstrumentationListener* listener : exception_caught_listeners_) {
+ if (listener != nullptr) {
+ listener->ExceptionCaught(thread, exception_object);
+ }
}
thread->SetException(exception_object);
}