diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 4e16e328ab4..13e1ea30a34 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -77,8 +77,6 @@ compiler/interpreter/Test6833129.java 8335266 generic-i586 compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 -serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java 8369150 generic-all - ############################################################################# # :hotspot_gc diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java index 6c465b357d7..10888dce1b4 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java @@ -23,23 +23,30 @@ /* * @test - * * @bug 8316694 * @summary Verify that nmethod relocation posts the correct JVMTI events - * @requires vm.jvmti - * @requires vm.gc == "null" | vm.gc == "Serial" + * @requires vm.jvmti & + * vm.gc != "Epsilon" & + * vm.flavor == "server" & + * !vm.emulatedClient & + * (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) * @library /test/lib /test/hotspot/jtreg * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm/native NMethodRelocationTest + * @run main/othervm/native -agentlib:NMethodRelocationTest + * --enable-native-access=ALL-UNNAMED + * -Xbootclasspath/a:. + * -Xbatch + * -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * -XX:+SegmentedCodeCache + * -XX:-TieredCompilation + * -XX:+UnlockExperimentalVMOptions + * -XX:+NMethodRelocation + * NMethodRelocationTest */ -import static compiler.whitebox.CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION; - import java.lang.reflect.Executable; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import jdk.test.lib.Asserts; import jdk.test.lib.process.OutputAnalyzer; @@ -48,52 +55,9 @@ import jdk.test.whitebox.WhiteBox; import jdk.test.whitebox.code.BlobType; import jdk.test.whitebox.code.NMethod; +import static compiler.whitebox.CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION; public class NMethodRelocationTest { - public static void main(String[] args) throws Exception { - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-agentlib:NMethodRelocationTest", - "--enable-native-access=ALL-UNNAMED", - "-Xbootclasspath/a:.", - "-XX:+UseSerialGC", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+WhiteBoxAPI", - "-XX:+SegmentedCodeCache", - "-XX:-TieredCompilation", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+NMethodRelocation", - "DoWork"); - - OutputAnalyzer oa = new OutputAnalyzer(pb.start()); - String output = oa.getOutput(); - if (oa.getExitValue() != 0) { - System.err.println(oa.getOutput()); - throw new RuntimeException("Non-zero exit code returned from the test"); - } - Asserts.assertTrue(oa.getExitValue() == 0); - - Pattern pattern = Pattern.compile("(?m)^Relocated nmethod from (0x[0-9a-f]{16}) to (0x[0-9a-f]{16})$"); - Matcher matcher = pattern.matcher(output); - - if (matcher.find()) { - String fromAddr = matcher.group(1); - String toAddr = matcher.group(2); - - // Confirm events sent for both original and relocated nmethod - oa.shouldContain(": name: compiledMethod, code: " + fromAddr); - oa.shouldContain(": name: compiledMethod, code: " + toAddr); - oa.shouldContain(": name: compiledMethod, code: " + fromAddr); - oa.shouldContain(": name: compiledMethod, code: " + toAddr); - } else { - System.err.println(oa.getOutput()); - throw new RuntimeException("Unable to find relocation information"); - } - } -} - -class DoWork { - - protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); /** Load native library if required. */ static { @@ -107,43 +71,18 @@ class DoWork { } } - /** - * Returns value of VM option. - * - * @param name option's name - * @return value of option or {@code null}, if option doesn't exist - * @throws NullPointerException if name is null - */ - protected static String getVMOption(String name) { - Objects.requireNonNull(name); - return Objects.toString(WHITE_BOX.getVMFlag(name), null); - } + protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); - /** - * Returns value of VM option or default value. - * - * @param name option's name - * @param defaultValue default value - * @return value of option or {@code defaultValue}, if option doesn't exist - * @throws NullPointerException if name is null - * @see #getVMOption(String) - */ - protected static String getVMOption(String name, String defaultValue) { - String result = getVMOption(name); - return result == null ? defaultValue : result; - } + native static boolean shouldExit(); - public static void main(String argv[]) throws Exception { - run(); - } - - public static void run() throws Exception { - Executable method = DoWork.class.getDeclaredMethod("compiledMethod"); + public static void main(String[] argv) throws Exception { + Executable method = NMethodRelocationTest.class.getDeclaredMethod("compiledMethod"); WHITE_BOX.testSetDontInlineMethod(method, true); WHITE_BOX.enqueueMethodForCompilation(method, COMP_LEVEL_FULL_OPTIMIZATION); - while (WHITE_BOX.isMethodQueuedForCompilation(method)) { - Thread.onSpinWait(); + + if (!WHITE_BOX.isMethodCompiled(method)) { + throw new AssertionError("Method not compiled"); } NMethod originalNMethod = NMethod.get(method, false); @@ -164,13 +103,9 @@ class DoWork { WHITE_BOX.deoptimizeAll(); - WHITE_BOX.fullGC(); - WHITE_BOX.fullGC(); - - WHITE_BOX.lockCompilation(); - - System.out.printf("Relocated nmethod from 0x%016x to 0x%016x%n", originalNMethod.code_begin, relocatedNMethod.code_begin); - System.out.flush(); + while (!shouldExit()) { + WHITE_BOX.fullGC(); + } } public static long compiledMethod() { diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp index 41ba6b10608..7f8ab7b78bc 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp @@ -21,10 +21,20 @@ * questions. */ +#include #include -#include -#include -#include +#include "jvmti_common.hpp" + +extern "C" { + +// Track nmethod addresses for LOAD and UNLOAD events +static const void* first_load_addr = nullptr; +static const void* second_load_addr = nullptr; +static const void* first_unload_addr = nullptr; +static const void* second_unload_addr = nullptr; + +// Keep track of test completion +static std::atomic should_exit{false}; /** * Callback for COMPILED_METHOD_LOAD event. @@ -34,18 +44,27 @@ callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method, jint code_size, const void* code_addr, jint map_length, const jvmtiAddrLocationMap* map, const void* compile_info) { - char* name = nullptr; - char* sig = nullptr; - if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) { - printf(" [Could not retrieve method name]\n"); - fflush(stdout); + // Only track events for "compiledMethod" + char* name = get_method_name(jvmti, method); + if (strcmp(name, "compiledMethod") != 0) { return; } - printf(": name: %s, code: 0x%016" PRIxPTR "\n", - name, (uintptr_t)code_addr); - fflush(stdout); + LOG(": name: %s, code: 0x%016" PRIxPTR "\n", name, (uintptr_t)code_addr); + + if (first_load_addr == nullptr) { + first_load_addr = code_addr; + } else if (second_load_addr == nullptr) { + second_load_addr = code_addr; + + // Verify that the addresses are different + if (first_load_addr == second_load_addr) { + fatal("Load events for 'compiledMethod' are expected to use different addresses"); + } + } else { + fatal("Received too many load events for 'compiledMethod'"); + } } /** @@ -54,25 +73,50 @@ callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method, JNIEXPORT void JNICALL callbackCompiledMethodUnload(jvmtiEnv* jvmti, jmethodID method, const void* code_addr) { - char* name = nullptr; - char* sig = nullptr; - if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) { - printf(" [Could not retrieve method name]\n"); - fflush(stdout); + // Only track events for "compiledMethod" + char* name = get_method_name(jvmti, method); + if (strcmp(name, "compiledMethod") != 0) { return; } - printf(": name: %s, code: 0x%016" PRIxPTR "\n", - name, (uintptr_t)code_addr); - fflush(stdout); + + LOG(": name: %s, code: 0x%016" PRIxPTR "\n", name, (uintptr_t)code_addr); + + // Validate both loads have occurred + if (first_load_addr == nullptr || second_load_addr == nullptr) { + fatal("UNLOAD event for 'compiledMethod' occurred before both LOAD events"); + } + + if (first_unload_addr == nullptr) { + first_unload_addr = code_addr; + } else if (second_unload_addr == nullptr) { + second_unload_addr = code_addr; + + // Verify that the addresses are different + if (first_unload_addr == second_unload_addr) { + fatal("Unload events for 'compiledMethod' are expected to use different addresses"); + } + + // LOAD and UNLOAD events should report the same two addresses, but the order of + // the UNLOADs is not guaranteed, since the GC may unload either nmethod first. + if ((first_load_addr == first_unload_addr && second_load_addr == second_unload_addr) || + (first_load_addr == second_unload_addr && second_load_addr == first_unload_addr)) { + + // Update should_exit to signal test completion + should_exit.store(true); + } else { + fatal("Address mismatch for 'compiledMethod' events"); + } + } else { + fatal("Received too many unload events for 'compiledMethod'"); + } } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { jvmtiEnv* jvmti = nullptr; - jvmtiError error; if (jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_0) != JNI_OK) { - printf("Unable to access JVMTI!\n"); + LOG("Unable to access JVMTI!\n"); return JNI_ERR; } @@ -80,11 +124,8 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) jvmtiCapabilities caps; memset(&caps, 0, sizeof(caps)); caps.can_generate_compiled_method_load_events = 1; - error = jvmti->AddCapabilities(&caps); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to add capabilities, error=%d\n", error); - return JNI_ERR; - } + jvmtiError error = jvmti->AddCapabilities(&caps); + check_jvmti_error(error, "Unable to add capabilities"); // Set event callbacks jvmtiEventCallbacks eventCallbacks; @@ -92,23 +133,21 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) eventCallbacks.CompiledMethodLoad = callbackCompiledMethodLoad; eventCallbacks.CompiledMethodUnload = callbackCompiledMethodUnload; error = jvmti->SetEventCallbacks(&eventCallbacks, sizeof(eventCallbacks)); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to set event callbacks, error=%d\n", error); - return JNI_ERR; - } + check_jvmti_error(error, "Unable to set event callbacks"); // Enable events error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, nullptr); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to enable COMPILED_METHOD_LOAD event, error=%d\n", error); - return JNI_ERR; - } + check_jvmti_error(error, "Unable to enable COMPILED_METHOD_LOAD event"); error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, nullptr); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to enable COMPILED_METHOD_UNLOAD event, error=%d\n", error); - return JNI_ERR; - } + check_jvmti_error(error, "Unable to enable COMPILED_METHOD_UNLOAD event"); return JNI_OK; } + +JNIEXPORT jboolean JNICALL +Java_NMethodRelocationTest_shouldExit(JNIEnv *env, jclass cls) { + return should_exit.load(); +} + +} diff --git a/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp b/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp index 11600bd524b..3832d934b1e 100644 --- a/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp +++ b/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp @@ -123,6 +123,12 @@ char* julong_to_string(julong value, char *string) { return string; } +static void +fatal(const char* msg) { + LOG("FATAL ERROR: %s\n", msg); + abort(); +} + static void fatal(JNIEnv* jni, const char* msg) { jni->FatalError(msg); @@ -313,6 +319,17 @@ get_thread_name(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread) { return tname; } +static char* +get_method_name(jvmtiEnv *jvmti, jmethodID method) { + char* mname = nullptr; + jvmtiError err; + + err = jvmti->GetMethodName(method, &mname, nullptr, nullptr); + check_jvmti_error(err, "get_method_name: error in JVMTI GetMethodName call"); + + return mname; +} + static char* get_method_name(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method) { char* mname = nullptr;