ART: Add allocation callback

Bug: 31684277
Test: m test-art-host
Change-Id: I959f44e23ca5fe55ed678315708895faf0aadb04
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index b298301..d027201 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -29,6 +29,9 @@
     return nullptr;
   }
 
+  // TODO: Add a type check. Can be done, for example, by an explicitly instantiated template
+  //       function.
+
   switch (event) {
     case JVMTI_EVENT_VM_INIT:
       return reinterpret_cast<FnType*>(env->event_callbacks->VMInit);
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 48f3de4..4d5b7e0 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -29,9 +29,17 @@
  * questions.
  */
 
-#include "events.h"
+#include "events-inl.h"
 
 #include "art_jvmti.h"
+#include "base/logging.h"
+#include "gc/allocation_listener.h"
+#include "instrumentation.h"
+#include "jni_env_ext-inl.h"
+#include "mirror/class.h"
+#include "mirror/object.h"
+#include "runtime.h"
+#include "ScopedLocalRef.h"
 
 namespace openjdkjvmti {
 
@@ -116,8 +124,65 @@
   }
 }
 
+class JvmtiAllocationListener : public art::gc::AllocationListener {
+ public:
+  explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {}
+
+  void ObjectAllocated(art::Thread* self, art::mirror::Object** obj, size_t byte_count)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    DCHECK_EQ(self, art::Thread::Current());
+
+    if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
+      // jvmtiEventVMObjectAlloc parameters:
+      //      jvmtiEnv *jvmti_env,
+      //      JNIEnv* jni_env,
+      //      jthread thread,
+      //      jobject object,
+      //      jclass object_klass,
+      //      jlong size
+      art::JNIEnvExt* jni_env = self->GetJniEnv();
+
+      jthread thread_peer;
+      if (self->IsStillStarting()) {
+        thread_peer = nullptr;
+      } else {
+        thread_peer = jni_env->AddLocalReference<jthread>(self->GetPeer());
+      }
+
+      ScopedLocalRef<jthread> thread(jni_env, thread_peer);
+      ScopedLocalRef<jobject> object(
+          jni_env, jni_env->AddLocalReference<jobject>(*obj));
+      ScopedLocalRef<jclass> klass(
+          jni_env, jni_env->AddLocalReference<jclass>((*obj)->GetClass()));
+
+      handler_->DispatchEvent(self,
+                              JVMTI_EVENT_VM_OBJECT_ALLOC,
+                              jni_env,
+                              thread.get(),
+                              object.get(),
+                              klass.get(),
+                              byte_count);
+    }
+  }
+
+ private:
+  EventHandler* handler_;
+};
+
+static void SetupObjectAllocationTracking(art::gc::AllocationListener* listener, bool enable) {
+  if (enable) {
+    art::Runtime::Current()->GetHeap()->SetAllocationListener(listener);
+  } else {
+    art::Runtime::Current()->GetHeap()->RemoveAllocationListener();
+  }
+}
+
 // Handle special work for the given event type, if necessary.
-static void HandleEventType(jvmtiEvent event ATTRIBUTE_UNUSED, bool enable ATTRIBUTE_UNUSED) {
+void EventHandler::HandleEventType(jvmtiEvent event, bool enable) {
+  if (event == JVMTI_EVENT_VM_OBJECT_ALLOC) {
+    SetupObjectAllocationTracking(alloc_listener_.get(), enable);
+    return;
+  }
 }
 
 jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env,
@@ -172,4 +237,11 @@
   return ERR(NONE);
 }
 
+EventHandler::EventHandler() {
+  alloc_listener_.reset(new JvmtiAllocationListener(this));
+}
+
+EventHandler::~EventHandler() {
+}
+
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 5716d03..3212b12 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -27,6 +27,7 @@
 namespace openjdkjvmti {
 
 struct ArtJvmTiEnv;
+class JvmtiAllocationListener;
 
 struct EventMask {
   static constexpr size_t kEventsSize = JVMTI_MAX_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL + 1;
@@ -71,12 +72,10 @@
 };
 
 // Helper class for event handling.
-struct EventHandler {
-  // List of all JvmTiEnv objects that have been created, in their creation order.
-  std::vector<ArtJvmTiEnv*> envs;
-
-  // A union of all enabled events, anywhere.
-  EventMask global_mask;
+class EventHandler {
+ public:
+  EventHandler();
+  ~EventHandler();
 
   // Register an env. It is assumed that this happens on env creation, that is, no events are
   // enabled, yet.
@@ -93,6 +92,17 @@
 
   template <typename ...Args>
   ALWAYS_INLINE inline void DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args);
+
+ private:
+  void HandleEventType(jvmtiEvent event, bool enable);
+
+  // List of all JvmTiEnv objects that have been created, in their creation order.
+  std::vector<ArtJvmTiEnv*> envs;
+
+  // A union of all enabled events, anywhere.
+  EventMask global_mask;
+
+  std::unique_ptr<JvmtiAllocationListener> alloc_listener_;
 };
 
 }  // namespace openjdkjvmti