ART: Add ClassLoadCallback

Add callback for class-load and class-prepare events. Move Dbg
over. Add tests.

Bug: 31684920
Test: m test-art-host-gtest-runtime_callbacks_test
Test: art/tools/run-jdwp-tests.sh --mode=host
Change-Id: I871f6b3c54448fd6ece8d9a7571b2042be50d525
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 5bdfbc7..f708e26 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -107,6 +107,7 @@
 ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
 ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex
 ART_GTEST_profile_compilation_info_test_DEX_DEPS := ProfileTestMultiDex
+ART_GTEST_runtime_callbacks_test_DEX_DEPS := XandY
 ART_GTEST_stub_test_DEX_DEPS := AllFields
 ART_GTEST_transaction_test_DEX_DEPS := Transaction
 ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f0a64f1..92d1554 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2678,6 +2678,11 @@
     return nullptr;
   }
   CHECK(klass->IsLoaded());
+
+  // At this point the class is loaded. Publish a ClassLoad even.
+  // Note: this may be a temporary class. It is a listener's responsibility to handle this.
+  Runtime::Current()->GetRuntimeCallbacks().ClassLoad(klass);
+
   // Link the class (if necessary)
   CHECK(!klass->IsResolved());
   // TODO: Use fast jobjects?
@@ -2718,7 +2723,7 @@
    * The class has been prepared and resolved but possibly not yet verified
    * at this point.
    */
-  Dbg::PostClassPrepare(h_new_class.Get());
+  Runtime::Current()->GetRuntimeCallbacks().ClassPrepare(klass, h_new_class);
 
   // Notify native debugger of the new class and its layout.
   jit::Jit::NewTypeLoadedIfUsingJit(h_new_class.Get());
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 9b98671..8da979b 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -34,6 +34,7 @@
 #include "dex_file.h"
 #include "dex_file_types.h"
 #include "gc_root.h"
+#include "handle.h"
 #include "jni.h"
 #include "mirror/class.h"
 #include "object_callbacks.h"
@@ -1194,6 +1195,21 @@
   DISALLOW_COPY_AND_ASSIGN(ClassLinker);
 };
 
+class ClassLoadCallback {
+ public:
+  virtual ~ClassLoadCallback() {}
+
+  // A class has been loaded.
+  // Note: the class may be temporary, in which case a following ClassPrepare event will be a
+  //       different object. It is the listener's responsibility to handle this.
+  virtual void ClassLoad(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+  // A class has been prepared, i.e., resolved. As the ClassLoad event might have been for a
+  // temporary class, provide both the former and the current class.
+  virtual void ClassPrepare(Handle<mirror::Class> temp_klass,
+                            Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_CLASS_LINKER_H_
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 6da7e3a..22a3163 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -321,6 +321,7 @@
 uint32_t Dbg::instrumentation_events_ = 0;
 
 Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_;
+Dbg::DbgClassLoadCallback Dbg::class_load_callback_;
 
 // Breakpoints.
 static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
@@ -5145,4 +5146,12 @@
   Dbg::PostThreadDeath(self);
 }
 
+void Dbg::DbgClassLoadCallback::ClassLoad(Handle<mirror::Class> klass ATTRIBUTE_UNUSED) {
+  // Ignore ClassLoad;
+}
+void Dbg::DbgClassLoadCallback::ClassPrepare(Handle<mirror::Class> temp_klass ATTRIBUTE_UNUSED,
+                                             Handle<mirror::Class> klass) {
+  Dbg::PostClassPrepare(klass.Get());
+}
+
 }  // namespace art
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 0135990..a7fd160 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -28,6 +28,8 @@
 #include <vector>
 
 #include "gc_root.h"
+#include "class_linker.h"
+#include "handle.h"
 #include "jdwp/jdwp.h"
 #include "jni.h"
 #include "jvalue.h"
@@ -502,8 +504,6 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
   static void PostException(mirror::Throwable* exception)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  static void PostClassPrepare(mirror::Class* c)
-      REQUIRES_SHARED(Locks::mutator_lock_);
 
   static void UpdateDebugger(Thread* thread, mirror::Object* this_object,
                              ArtMethod* method, uint32_t new_dex_pc,
@@ -706,6 +706,9 @@
   static ThreadLifecycleCallback* GetThreadLifecycleCallback() {
     return &thread_lifecycle_callback_;
   }
+  static ClassLoadCallback* GetClassLoadCallback() {
+    return &class_load_callback_;
+  }
 
  private:
   static void ExecuteMethodWithoutPendingException(ScopedObjectAccess& soa, DebugInvokeReq* pReq)
@@ -733,6 +736,9 @@
   static void PostThreadStartOrStop(Thread*, uint32_t)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  static void PostClassPrepare(mirror::Class* c)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   static void PostLocationEvent(ArtMethod* method, int pcOffset,
                                 mirror::Object* thisPtr, int eventFlags,
                                 const JValue* return_value)
@@ -800,7 +806,15 @@
     void ThreadDeath(Thread* self) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
   };
 
+  class DbgClassLoadCallback : public ClassLoadCallback {
+   public:
+    void ClassLoad(Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+    void ClassPrepare(Handle<mirror::Class> temp_klass,
+                      Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+  };
+
   static DbgThreadLifecycleCallback thread_lifecycle_callback_;
+  static DbgClassLoadCallback class_load_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(Dbg);
 };
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 6ef5f26..a2b462e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1101,6 +1101,7 @@
     Dbg::ConfigureJdwp(runtime_options.GetOrDefault(Opt::JdwpOptions));
   }
   callbacks_.AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback());
+  callbacks_.AddClassLoadCallback(Dbg::GetClassLoadCallback());
 
   jit_options_.reset(jit::JitOptions::CreateFromRuntimeArguments(runtime_options));
   if (IsAotCompiler()) {
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index a523ddf..ee9edda 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 
+#include "class_linker.h"
 #include "thread.h"
 
 namespace art {
@@ -45,4 +46,27 @@
   }
 }
 
+void RuntimeCallbacks::AddClassLoadCallback(ClassLoadCallback* cb) {
+  class_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveClassLoadCallback(ClassLoadCallback* cb) {
+  auto it = std::find(class_callbacks_.begin(), class_callbacks_.end(), cb);
+  if (it != class_callbacks_.end()) {
+    class_callbacks_.erase(it);
+  }
+}
+
+void RuntimeCallbacks::ClassLoad(Handle<mirror::Class> klass) {
+  for (ClassLoadCallback* cb : class_callbacks_) {
+    cb->ClassLoad(klass);
+  }
+}
+
+void RuntimeCallbacks::ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass) {
+  for (ClassLoadCallback* cb : class_callbacks_) {
+    cb->ClassPrepare(temp_klass, klass);
+  }
+}
+
 }  // namespace art
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 1060d85..5bdb44a 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -21,9 +21,15 @@
 
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "handle.h"
 
 namespace art {
 
+namespace mirror {
+class Class;
+}  // namespace mirror
+
+class ClassLoadCallback;
 class Thread;
 class ThreadLifecycleCallback;
 
@@ -44,19 +50,24 @@
 
 class RuntimeCallbacks {
  public:
-  void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb)
-      REQUIRES(Locks::mutator_lock_);
-  void RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb)
-      REQUIRES(Locks::mutator_lock_);
+  void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
+  void RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
 
-  void ThreadStart(Thread* self)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  void ThreadDeath(Thread* self)
+  void ThreadStart(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_);
+  void ThreadDeath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  void AddClassLoadCallback(ClassLoadCallback* cb) REQUIRES(Locks::mutator_lock_);
+  void RemoveClassLoadCallback(ClassLoadCallback* cb) REQUIRES(Locks::mutator_lock_);
+
+  void ClassLoad(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
+  void ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
   std::vector<ThreadLifecycleCallback*> thread_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
+  std::vector<ClassLoadCallback*> class_callbacks_
+      GUARDED_BY(Locks::mutator_lock_);
 };
 
 }  // namespace art
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 62729ca..8cd39a0 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -17,14 +17,20 @@
 #include "runtime_callbacks.h"
 
 #include "jni.h"
+
+#include <initializer_list>
 #include <memory>
 #include <string>
 
 #include "art_method-inl.h"
 #include "base/mutex.h"
-#include "mirror/class-inl.h"
+#include "class_linker.h"
 #include "common_runtime_test.h"
+#include "handle.h"
+#include "handle_scope-inl.h"
 #include "mem_map.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
 #include "obj_ptr.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
@@ -129,7 +135,6 @@
   Callback cb_;
 };
 
-
 TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackJava) {
   Thread* self = Thread::Current();
 
@@ -207,4 +212,82 @@
   EXPECT_TRUE(cb_.state == CallbackState::kStarted) << static_cast<int>(cb_.state);
 }
 
+class ClassLoadCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest {
+ protected:
+  void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+    Runtime::Current()->GetRuntimeCallbacks().AddClassLoadCallback(&cb_);
+  }
+  void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+    Runtime::Current()->GetRuntimeCallbacks().RemoveClassLoadCallback(&cb_);
+  }
+
+  bool Expect(std::initializer_list<const char*> list) {
+    if (cb_.data.size() != list.size()) {
+      PrintError(list);
+      return false;
+    }
+
+    if (!std::equal(cb_.data.begin(), cb_.data.end(), list.begin())) {
+      PrintError(list);
+      return false;
+    }
+
+    return true;
+  }
+
+  void PrintError(std::initializer_list<const char*> list) {
+    LOG(ERROR) << "Expected:";
+    for (const char* expected : list) {
+      LOG(ERROR) << "  " << expected;
+    }
+    LOG(ERROR) << "Found:";
+    for (const auto& s : cb_.data) {
+      LOG(ERROR) << "  " << s;
+    }
+  }
+
+  struct Callback : public ClassLoadCallback {
+    void ClassLoad(Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+      std::string tmp;
+      std::string event = std::string("Load:") + klass->GetDescriptor(&tmp);
+      data.push_back(event);
+    }
+
+    void ClassPrepare(Handle<mirror::Class> temp_klass,
+                      Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+      std::string tmp, tmp2;
+      std::string event = std::string("Prepare:") + klass->GetDescriptor(&tmp)
+          + "[" + temp_klass->GetDescriptor(&tmp2) + "]";
+      data.push_back(event);
+    }
+
+    std::vector<std::string> data;
+  };
+
+  Callback cb_;
+};
+
+TEST_F(ClassLoadCallbackRuntimeCallbacksTest, ClassLoadCallback) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader = LoadDex("XandY");
+  VariableSizedHandleScope hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(jclass_loader)));
+
+  const char* descriptor_y = "LY;";
+  Handle<mirror::Class> h_Y(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader)));
+  ASSERT_TRUE(h_Y.Get() != nullptr);
+
+  bool expect1 = Expect({ "Load:LX;", "Prepare:LX;[LX;]", "Load:LY;", "Prepare:LY;[LY;]" });
+  EXPECT_TRUE(expect1);
+
+  cb_.data.clear();
+
+  ASSERT_TRUE(class_linker_->EnsureInitialized(Thread::Current(), h_Y, true, true));
+
+  bool expect2 = Expect({ "Load:LY$Z;", "Prepare:LY$Z;[LY$Z;]" });
+  EXPECT_TRUE(expect2);
+}
+
 }  // namespace art
diff --git a/test/XandY/Y.java b/test/XandY/Y.java
index ecead6e..2a1f036 100644
--- a/test/XandY/Y.java
+++ b/test/XandY/Y.java
@@ -14,4 +14,8 @@
  * limitations under the License.
  */
 
-class Y extends X {}
+class Y extends X {
+  static Z z = new Z();
+  static class Z {
+  }
+}