diff options
-rw-r--r-- | test/912-classes/classes.cc | 185 | ||||
-rw-r--r-- | test/912-classes/src/Main.java | 55 | ||||
-rw-r--r-- | test/Android.run-test.mk | 1 |
3 files changed, 171 insertions, 70 deletions
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index d13436ebf6..e659ea3bfb 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -17,9 +17,14 @@ #include <stdio.h> #include "base/macros.h" +#include "class_linker.h" #include "jni.h" +#include "mirror/class_loader.h" #include "openjdkjvmti/jvmti.h" +#include "runtime.h" #include "ScopedLocalRef.h" +#include "ScopedUtfChars.h" +#include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "ti-agent/common_helper.h" @@ -278,69 +283,11 @@ static std::string GetClassName(jvmtiEnv* jenv, JNIEnv* jni_env, jclass klass) { return tmp; } -static std::string GetThreadName(jvmtiEnv* jenv, JNIEnv* jni_env, jthread thread) { - jvmtiThreadInfo info; - jvmtiError result = jenv->GetThreadInfo(thread, &info); - if (result != JVMTI_ERROR_NONE) { - if (jni_env != nullptr) { - JvmtiErrorToException(jni_env, result); - } else { - printf("Failed to get thread name.\n"); - } - return ""; - } - - std::string tmp(info.name); - jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name)); - jni_env->DeleteLocalRef(info.context_class_loader); - jni_env->DeleteLocalRef(info.thread_group); - - return tmp; -} - -static std::string GetThreadName(Thread* thread) { - std::string tmp; - thread->GetThreadName(tmp); - return tmp; -} - -static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv, - JNIEnv* jni_env, - jthread thread, - jclass klass) { - std::string name = GetClassName(jenv, jni_env, klass); - if (name == "") { - return; - } - std::string thread_name = GetThreadName(jenv, jni_env, thread); - if (thread_name == "") { - return; - } - std::string cur_thread_name = GetThreadName(Thread::Current()); - printf("Prepare: %s on %s (cur=%s)\n", - name.c_str(), - thread_name.c_str(), - cur_thread_name.c_str()); -} - -static void JNICALL ClassLoadCallback(jvmtiEnv* jenv, - JNIEnv* jni_env, - jthread thread, - jclass klass) { - std::string name = GetClassName(jenv, jni_env, klass); - if (name == "") { - return; - } - std::string thread_name = GetThreadName(jenv, jni_env, thread); - if (thread_name == "") { - return; - } - printf("Load: %s on %s\n", name.c_str(), thread_name.c_str()); -} - -extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadEvents( - JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) { - if (b == JNI_FALSE) { +static void EnableEvents(JNIEnv* env, + jboolean enable, + decltype(jvmtiEventCallbacks().ClassLoad) class_load, + decltype(jvmtiEventCallbacks().ClassPrepare) class_prepare) { + if (enable == JNI_FALSE) { jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_CLASS_LOAD, nullptr); @@ -356,8 +303,8 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadEvents( jvmtiEventCallbacks callbacks; memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); - callbacks.ClassLoad = ClassLoadCallback; - callbacks.ClassPrepare = ClassPrepareCallback; + callbacks.ClassLoad = class_load; + callbacks.ClassPrepare = class_prepare; jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); if (JvmtiErrorToException(env, ret)) { return; @@ -375,5 +322,113 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadEvents( JvmtiErrorToException(env, ret); } +class ClassLoadPreparePrinter { + public: + static void JNICALL ClassLoadCallback(jvmtiEnv* jenv, + JNIEnv* jni_env, + jthread thread, + jclass klass) { + std::string name = GetClassName(jenv, jni_env, klass); + if (name == "") { + return; + } + std::string thread_name = GetThreadName(jenv, jni_env, thread); + if (thread_name == "") { + return; + } + printf("Load: %s on %s\n", name.c_str(), thread_name.c_str()); + } + + static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv, + JNIEnv* jni_env, + jthread thread, + jclass klass) { + std::string name = GetClassName(jenv, jni_env, klass); + if (name == "") { + return; + } + std::string thread_name = GetThreadName(jenv, jni_env, thread); + if (thread_name == "") { + return; + } + std::string cur_thread_name = GetThreadName(Thread::Current()); + printf("Prepare: %s on %s (cur=%s)\n", + name.c_str(), + thread_name.c_str(), + cur_thread_name.c_str()); + } + + private: + static std::string GetThreadName(jvmtiEnv* jenv, JNIEnv* jni_env, jthread thread) { + jvmtiThreadInfo info; + jvmtiError result = jenv->GetThreadInfo(thread, &info); + if (result != JVMTI_ERROR_NONE) { + if (jni_env != nullptr) { + JvmtiErrorToException(jni_env, result); + } else { + printf("Failed to get thread name.\n"); + } + return ""; + } + + std::string tmp(info.name); + jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name)); + jni_env->DeleteLocalRef(info.context_class_loader); + jni_env->DeleteLocalRef(info.thread_group); + + return tmp; + } + + static std::string GetThreadName(Thread* thread) { + std::string tmp; + thread->GetThreadName(tmp); + return tmp; + } +}; + +extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadPreparePrintEvents( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean enable) { + EnableEvents(env, + enable, + ClassLoadPreparePrinter::ClassLoadCallback, + ClassLoadPreparePrinter::ClassPrepareCallback); +} + +struct ClassLoadSeen { + static void JNICALL ClassLoadSeenCallback(jvmtiEnv* jenv ATTRIBUTE_UNUSED, + JNIEnv* jni_env ATTRIBUTE_UNUSED, + jthread thread ATTRIBUTE_UNUSED, + jclass klass ATTRIBUTE_UNUSED) { + saw_event = true; + } + + static bool saw_event; +}; +bool ClassLoadSeen::saw_event = false; + +extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadSeenEvents( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) { + EnableEvents(env, b, ClassLoadSeen::ClassLoadSeenCallback, nullptr); +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hadLoadEvent( + JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED) { + return ClassLoadSeen::saw_event ? JNI_TRUE : JNI_FALSE; +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_isLoadedClass( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring class_name) { + ScopedUtfChars name(env, class_name); + ScopedObjectAccess soa(Thread::Current()); + Runtime* current = Runtime::Current(); + ClassLinker* class_linker = current->GetClassLinker(); + bool found = + class_linker->LookupClass( + soa.Self(), + name.c_str(), + soa.Decode<mirror::ClassLoader>(current->GetSystemClassLoader())) != nullptr; + return found ? JNI_TRUE : JNI_FALSE; +} + } // namespace Test912Classes } // namespace art diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java index 6ad23a4869..cec69194fe 100644 --- a/test/912-classes/src/Main.java +++ b/test/912-classes/src/Main.java @@ -219,6 +219,14 @@ public class Main { } final ClassLoader boot = cl; + // The JIT may deeply inline and load some classes. Preload these for test determinism. + final String PRELOAD_FOR_JIT[] = { + "java.nio.charset.CoderMalfunctionError" + }; + for (String s : PRELOAD_FOR_JIT) { + Class.forName(s); + } + Runnable r = new Runnable() { @Override public void run() { @@ -238,7 +246,7 @@ public class Main { ensureJitCompiled(Main.class, "testClassEvents"); - enableClassLoadEvents(true); + enableClassLoadPreparePrintEvents(true); ClassLoader cl1 = create(boot, DEX1, DEX2); System.out.println("B, false"); @@ -270,7 +278,37 @@ public class Main { t.start(); t.join(); - enableClassLoadEvents(false); + enableClassLoadPreparePrintEvents(false); + + // Note: the JIT part of this test is about the JIT pulling in a class not yet touched by + // anything else in the system. This could be the verifier or the interpreter. We + // block the interpreter by calling ensureJitCompiled. The verifier, however, must + // run in configurations where dex2oat didn't verify the class itself. So explicitly + // check whether the class has been already loaded, and skip then. + // TODO: Add multiple configurations to the run script once that becomes easier to do. + if (hasJit() && !isLoadedClass("Main$ClassD")) { + testClassEventsJit(); + } + } + + private static void testClassEventsJit() throws Exception { + enableClassLoadSeenEvents(true); + + testClassEventsJitImpl(); + + enableClassLoadSeenEvents(false); + + if (!hadLoadEvent()) { + throw new RuntimeException("Did not get expected load event."); + } + } + + private static void testClassEventsJitImpl() throws Exception { + ensureJitCompiled(Main.class, "testClassEventsJitImpl"); + + if (ClassD.x != 1) { + throw new RuntimeException("Unexpected value"); + } } private static void printClassLoaderClasses(ClassLoader cl) { @@ -335,9 +373,14 @@ public class Main { private static native int[] getClassVersion(Class<?> c); - private static native void enableClassLoadEvents(boolean b); + private static native void enableClassLoadPreparePrintEvents(boolean b); + + private static native void ensureJitCompiled(Class<?> c, String name); - private static native void ensureJitCompiled(Class c, String name); + private static native boolean hasJit(); + private static native boolean isLoadedClass(String name); + private static native void enableClassLoadSeenEvents(boolean b); + private static native boolean hadLoadEvent(); private static class TestForNonInit { public static double dummy = Math.random(); // So it can't be compile-time initialized. @@ -361,6 +404,10 @@ public class Main { public abstract static class ClassC implements InfA, InfC { } + public static class ClassD { + static int x = 1; + } + private static final String DEX1 = System.getenv("DEX_LOCATION") + "/912-classes.jar"; private static final String DEX2 = System.getenv("DEX_LOCATION") + "/912-classes-ex.jar"; diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 742353da46..946d7e4b96 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -547,7 +547,6 @@ TEST_ART_BROKEN_JIT_RUN_TESTS := \ 902-hello-transformation \ 904-object-allocation \ 906-iterate-heap \ - 912-classes \ 914-hello-obsolescence \ 915-obsolete-2 \ 917-fields-transformation \ |