mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8369150: NMethodRelocationTest fails when JVMTI events not published before JVM exit
Reviewed-by: lmesnik, sspitsyn
This commit is contained in:
parent
9a944e5587
commit
0a1fa21921
@ -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
|
||||
|
||||
@ -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("<COMPILED_METHOD_LOAD>: name: compiledMethod, code: " + fromAddr);
|
||||
oa.shouldContain("<COMPILED_METHOD_LOAD>: name: compiledMethod, code: " + toAddr);
|
||||
oa.shouldContain("<COMPILED_METHOD_UNLOAD>: name: compiledMethod, code: " + fromAddr);
|
||||
oa.shouldContain("<COMPILED_METHOD_UNLOAD>: 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() {
|
||||
|
||||
@ -21,10 +21,20 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <inttypes.h>
|
||||
#include <jvmti.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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<bool> 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("<COMPILED_METHOD_LOAD>: name: %s, code: 0x%016" PRIxPTR "\n",
|
||||
name, (uintptr_t)code_addr);
|
||||
fflush(stdout);
|
||||
LOG("<COMPILED_METHOD_LOAD>: 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("<COMPILED_METHOD_UNLOAD>: name: %s, code: 0x%016" PRIxPTR "\n",
|
||||
name, (uintptr_t)code_addr);
|
||||
fflush(stdout);
|
||||
|
||||
LOG("<COMPILED_METHOD_UNLOAD>: 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user