Add obsolete object event

Add an extension event to notify agents that an object is becoming
obsolete. This is meant to be used by agents performing allocation
tracking using the VMObjectAlloc event to let them know that an object
replacement is occurring. This event is only triggered by calls to
JVMTI functions that create obsolete objects. Normal GC actions
(including a moving compaction) will not cause this event to trigger.

Test: ./test.py --host
Bug: 134162467

Change-Id: If48b880814a751ba6c24c18d0ad116db4f8fdf64
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
index 3c2b82e..0c06ae1 100644
--- a/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -30,6 +30,7 @@
 #include "class_root.h"
 #include "deopt_manager.h"
 #include "dex/primitive.h"
+#include "events-inl.h"
 #include "gc/collector_type.h"
 #include "gc/gc_cause.h"
 #include "gc/heap-visit-objects-inl.h"
@@ -67,6 +68,8 @@
 
 namespace openjdkjvmti {
 
+EventHandler* HeapExtensions::gEventHandler = nullptr;
+
 namespace {
 
 struct IndexCache {
@@ -1736,10 +1739,49 @@
   }
 }
 
-static void ReplaceWeakRoots(ArrayPtr old_arr_ptr, ArrayPtr new_arr_ptr)
+static void ReplaceWeakRoots(art::Thread* self,
+                             EventHandler* event_handler,
+                             ArrayPtr old_arr_ptr,
+                             ArrayPtr new_arr_ptr)
     REQUIRES(art::Locks::mutator_lock_,
              art::Locks::user_code_suspension_lock_,
              art::Roles::uninterruptible_) {
+  // Handle tags. We want to do this seprately from other weak-refs (handled below) because we need
+  // to send additional events and handle cases where the agent might have tagged the new
+  // replacement object during the VMObjectAlloc. We do this by removing all tags associated with
+  // both the obsolete and the new arrays. Then we send the ObsoleteObjectCreated event and cache
+  // the new tag values. We next update all the other weak-references (the tags have been removed)
+  // and finally update the tag table with the new values. Doing things in this way (1) keeps all
+  // code relating to updating weak-references together and (2) ensures we don't end up in strange
+  // situations where the order of weak-ref visiting affects the final tagging state. Since we have
+  // the mutator_lock_ and gc-paused throughout this whole process no threads should be able to see
+  // the interval where the objects are not tagged.
+  std::unordered_map<ArtJvmTiEnv*, jlong> obsolete_tags;
+  std::unordered_map<ArtJvmTiEnv*, jlong> non_obsolete_tags;
+  event_handler->ForEachEnv(self, [&](ArtJvmTiEnv* env) {
+    // Cannot have REQUIRES(art::Locks::mutator_lock_) since ForEachEnv doesn't require it.
+    art::Locks::mutator_lock_->AssertExclusiveHeld(self);
+    env->object_tag_table->Lock();
+    // Get the tags and clear them (so we don't need to special-case the normal weak-ref visitor)
+    jlong new_tag = 0;
+    jlong obsolete_tag = 0;
+    bool had_new_tag = env->object_tag_table->RemoveLocked(new_arr_ptr, &new_tag);
+    bool had_obsolete_tag = env->object_tag_table->RemoveLocked(old_arr_ptr, &obsolete_tag);
+    // Dispatch event.
+    if (had_obsolete_tag || had_new_tag) {
+      event_handler->DispatchEventOnEnv<ArtJvmtiEvent::kObsoleteObjectCreated>(env,
+                                                                               self,
+                                                                               &obsolete_tag,
+                                                                               &new_tag);
+      obsolete_tags[env] = obsolete_tag;
+      non_obsolete_tags[env] = new_tag;
+    }
+    // After weak-ref update we need to go back and re-add obsoletes. We wait to avoid having to
+    // deal with the visit-weaks overwriting the initial new_arr_ptr tag and generally making things
+    // difficult.
+    env->object_tag_table->Unlock();
+  });
+  // Handle weak-refs.
   struct ReplaceWeaksVisitor : public art::IsMarkedVisitor {
    public:
     ReplaceWeaksVisitor(ArrayPtr old_arr, ArrayPtr new_arr)
@@ -1760,9 +1802,23 @@
   };
   ReplaceWeaksVisitor rwv(old_arr_ptr, new_arr_ptr);
   art::Runtime::Current()->SweepSystemWeaks(&rwv);
+  // Re-add the object tags. At this point all weak-references to the old_arr_ptr are gone.
+  event_handler->ForEachEnv(self, [&](ArtJvmTiEnv* env) {
+    // Cannot have REQUIRES(art::Locks::mutator_lock_) since ForEachEnv doesn't require it.
+    art::Locks::mutator_lock_->AssertExclusiveHeld(self);
+    env->object_tag_table->Lock();
+    if (obsolete_tags.find(env) != obsolete_tags.end()) {
+      env->object_tag_table->SetLocked(old_arr_ptr, obsolete_tags[env]);
+    }
+    if (non_obsolete_tags.find(env) != non_obsolete_tags.end()) {
+      env->object_tag_table->SetLocked(new_arr_ptr, non_obsolete_tags[env]);
+    }
+    env->object_tag_table->Unlock();
+  });
 }
 
 static void PerformArrayReferenceReplacement(art::Thread* self,
+                                             EventHandler* event_handler,
                                              ArrayPtr old_arr_ptr,
                                              ArrayPtr new_arr_ptr)
     REQUIRES(art::Locks::mutator_lock_,
@@ -1770,7 +1826,7 @@
              art::Roles::uninterruptible_) {
   ReplaceObjectReferences(old_arr_ptr, new_arr_ptr);
   ReplaceStrongRoots(self, old_arr_ptr, new_arr_ptr);
-  ReplaceWeakRoots(old_arr_ptr, new_arr_ptr);
+  ReplaceWeakRoots(self, event_handler, old_arr_ptr, new_arr_ptr);
 }
 
 }  // namespace
@@ -1863,8 +1919,12 @@
       UNREACHABLE();
   }
   // Actually replace all the pointers.
-  PerformArrayReferenceReplacement(self, old_arr.Get(), new_arr.Get());
+  PerformArrayReferenceReplacement(self, gEventHandler, old_arr.Get(), new_arr.Get());
   return OK;
 }
 
+void HeapExtensions::Register(EventHandler* eh) {
+  gEventHandler = eh;
+}
+
 }  // namespace openjdkjvmti