ART: Add class events

Add initial support for ClassLoad and ClassPrepare events. Add tests.

The initial implementation does not deal with the difference between
ClassLoad and ClassPrepare classes (the former may be temporary
classes).

Bug: 31684920
Test: m test-art-host-run-test-912-classes
Change-Id: I83535b9c871971f60b6b61f26651958fb32d502f
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index be10378..33550d1 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1310,6 +1310,7 @@
   }
   PhaseUtil::Register(&gEventHandler);
   ThreadUtil::Register(&gEventHandler);
+  ClassUtil::Register(&gEventHandler);
 
   runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
   runtime->AddSystemWeakHolder(&gObjectTagTable);
@@ -1320,6 +1321,7 @@
 extern "C" bool ArtPlugin_Deinitialize() {
   PhaseUtil::Unregister();
   ThreadUtil::Unregister();
+  ClassUtil::Unregister();
 
   return true;
 }
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index abcc849..450f75a 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -31,16 +31,119 @@
 
 #include "ti_class.h"
 
+#include <mutex>
+#include <unordered_set>
+
 #include "art_jvmti.h"
+#include "base/macros.h"
 #include "class_table-inl.h"
 #include "class_linker.h"
+#include "events-inl.h"
+#include "handle.h"
+#include "jni_env_ext-inl.h"
 #include "jni_internal.h"
 #include "runtime.h"
+#include "runtime_callbacks.h"
+#include "ScopedLocalRef.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
+#include "thread_list.h"
 
 namespace openjdkjvmti {
 
+struct ClassCallback : public art::ClassLoadCallback {
+  void ClassLoad(art::Handle<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassLoad)) {
+      art::Thread* thread = art::Thread::Current();
+      ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
+                                    thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
+      ScopedLocalRef<jclass> jthread(
+          thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer()));
+      {
+        art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
+        event_handler->DispatchEvent(thread,
+                                     ArtJvmtiEvent::kClassLoad,
+                                     reinterpret_cast<JNIEnv*>(thread->GetJniEnv()),
+                                     jthread.get(),
+                                     jklass.get());
+      }
+      AddTempClass(thread, jklass.get());
+    }
+  }
+
+  void ClassPrepare(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED,
+                    art::Handle<art::mirror::Class> klass)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassPrepare)) {
+      art::Thread* thread = art::Thread::Current();
+      ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
+                                    thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
+      ScopedLocalRef<jclass> jthread(
+          thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer()));
+      art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
+      event_handler->DispatchEvent(thread,
+                                   ArtJvmtiEvent::kClassPrepare,
+                                   reinterpret_cast<JNIEnv*>(thread->GetJniEnv()),
+                                   jthread.get(),
+                                   jklass.get());
+    }
+  }
+
+  void AddTempClass(art::Thread* self, jclass klass) {
+    std::unique_lock<std::mutex> mu(temp_classes_lock);
+    temp_classes.push_back(reinterpret_cast<jclass>(self->GetJniEnv()->NewGlobalRef(klass)));
+  }
+
+  void HandleTempClass(art::Handle<art::mirror::Class> temp_klass,
+                       art::Handle<art::mirror::Class> klass)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    std::unique_lock<std::mutex> mu(temp_classes_lock);
+    if (temp_classes.empty()) {
+      return;
+    }
+
+    art::Thread* self = art::Thread::Current();
+    for (auto it = temp_classes.begin(); it != temp_classes.end(); ++it) {
+      if (temp_klass.Get() == art::ObjPtr<art::mirror::Class>::DownCast(self->DecodeJObject(*it))) {
+        temp_classes.erase(it);
+        FixupTempClass(temp_klass, klass);
+      }
+    }
+  }
+
+  void FixupTempClass(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED,
+                      art::Handle<art::mirror::Class> klass ATTRIBUTE_UNUSED)
+     REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    // TODO: Implement.
+  }
+
+  // A set of all the temp classes we have handed out. We have to fix up references to these.
+  // For simplicity, we store the temp classes as JNI global references in a vector. Normally a
+  // Prepare event will closely follow, so the vector should be small.
+  std::mutex temp_classes_lock;
+  std::vector<jclass> temp_classes;
+
+  EventHandler* event_handler = nullptr;
+};
+
+ClassCallback gClassCallback;
+
+void ClassUtil::Register(EventHandler* handler) {
+  gClassCallback.event_handler = handler;
+  art::ScopedThreadStateChange stsc(art::Thread::Current(),
+                                    art::ThreadState::kWaitingForDebuggerToAttach);
+  art::ScopedSuspendAll ssa("Add load callback");
+  art::Runtime::Current()->GetRuntimeCallbacks()->AddClassLoadCallback(&gClassCallback);
+}
+
+void ClassUtil::Unregister() {
+  art::ScopedThreadStateChange stsc(art::Thread::Current(),
+                                    art::ThreadState::kWaitingForDebuggerToAttach);
+  art::ScopedSuspendAll ssa("Remove thread callback");
+  art::Runtime* runtime = art::Runtime::Current();
+  runtime->GetRuntimeCallbacks()->RemoveClassLoadCallback(&gClassCallback);
+}
+
 jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env,
                                      jclass jklass,
                                      jint* field_count_ptr,
@@ -200,7 +303,9 @@
   }
 
   // TODO: Support generic signature.
-  *generic_ptr = nullptr;
+  if (generic_ptr != nullptr) {
+    *generic_ptr = nullptr;
+  }
 
   // Everything is fine, release the buffers.
   sig_copy.release();
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
index 9558894..aa2260f 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -37,8 +37,13 @@
 
 namespace openjdkjvmti {
 
+class EventHandler;
+
 class ClassUtil {
  public:
+  static void Register(EventHandler* event_handler);
+  static void Unregister();
+
   static jvmtiError GetClassFields(jvmtiEnv* env,
                                    jclass klass,
                                    jint* field_count_ptr,
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc
index 62c3ebe..154406a 100644
--- a/runtime/openjdkjvmti/ti_phase.cc
+++ b/runtime/openjdkjvmti/ti_phase.cc
@@ -55,19 +55,23 @@
     return soa.AddLocalReference<jthread>(soa.Self()->GetPeer());
   }
 
-  void NextRuntimePhase(RuntimePhase phase) OVERRIDE {
+  void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
     // TODO: Events.
     switch (phase) {
       case RuntimePhase::kInitialAgents:
         PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL;
         break;
       case RuntimePhase::kStart:
-        event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv());
-        PhaseUtil::current_phase_ = JVMTI_PHASE_START;
+        {
+          art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
+          event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv());
+          PhaseUtil::current_phase_ = JVMTI_PHASE_START;
+        }
         break;
       case RuntimePhase::kInit:
         {
           ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread());
+          art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
           event_handler->DispatchEvent(nullptr,
                                        ArtJvmtiEvent::kVmInit,
                                        GetJniEnv(),
@@ -76,8 +80,11 @@
         }
         break;
       case RuntimePhase::kDeath:
-        event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmDeath, GetJniEnv());
-        PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD;
+        {
+          art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
+          event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmDeath, GetJniEnv());
+          PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD;
+        }
         // TODO: Block events now.
         break;
     }
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index bf79570..9f81d6b 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -64,6 +64,7 @@
   void Post(art::Thread* self, ArtJvmtiEvent type) REQUIRES_SHARED(art::Locks::mutator_lock_) {
     DCHECK_EQ(self, art::Thread::Current());
     ScopedLocalRef<jthread> thread(self->GetJniEnv(), GetThreadObject(self));
+    art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
     event_handler->DispatchEvent(self, type, self->GetJniEnv(), thread.get());
   }