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/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