diff options
-rw-r--r-- | build/Android.gtest.mk | 1 | ||||
-rw-r--r-- | runtime/class_linker.cc | 7 | ||||
-rw-r--r-- | runtime/class_linker.h | 16 | ||||
-rw-r--r-- | runtime/debugger.cc | 9 | ||||
-rw-r--r-- | runtime/debugger.h | 18 | ||||
-rw-r--r-- | runtime/runtime.cc | 1 | ||||
-rw-r--r-- | runtime/runtime_callbacks.cc | 24 | ||||
-rw-r--r-- | runtime/runtime_callbacks.h | 25 | ||||
-rw-r--r-- | runtime/runtime_callbacks_test.cc | 87 | ||||
-rw-r--r-- | test/XandY/Y.java | 6 |
10 files changed, 181 insertions, 13 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 5bdfbc74eb..f708e26efd 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -107,6 +107,7 @@ ART_GTEST_proxy_test_DEX_DEPS := Interfaces 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 f0a64f175a..92d15546c1 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2678,6 +2678,11 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, 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 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, * 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 9b98671cb4..8da979b36f 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 @@ class ClassLinker { 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 6da7e3a94e..22a31635a6 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -321,6 +321,7 @@ size_t Dbg::exception_catch_event_ref_count_ = 0; 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 @@ void Dbg::DbgThreadLifecycleCallback::ThreadDeath(Thread* self) { 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 01359907d9..a7fd1605df 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 @@ class Dbg { 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 @@ class Dbg { 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 @@ class Dbg { 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 @@ class Dbg { 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 6ef5f26ce5..a2b462e1e4 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1101,6 +1101,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { 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 a523ddfa44..ee9eddab96 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::ThreadDeath(Thread* self) { } } +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 1060d853d8..5bdb44a44a 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 ThreadLifecycleCallback; 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 62729cafe0..8cd39a0378 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 @@ class ThreadLifecycleCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest Callback cb_; }; - TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackJava) { Thread* self = Thread::Current(); @@ -207,4 +212,82 @@ TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackAttac 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 ecead6e35f..2a1f03698c 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 { + } +} |