8242237: Improve JVM TI HiddenClasses tests
Improve JVM TI HiddenClasses test: cleanup + more comments Reviewed-by: lmesnik, amenkov
This commit is contained in:
parent
a740f83da3
commit
223ca800cc
test/hotspot/jtreg/serviceability/jvmti/HiddenClass
@ -38,19 +38,21 @@ import java.io.FileInputStream;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.compiler.InMemoryJavaCompiler;
|
||||
|
||||
|
||||
interface Test<T> {
|
||||
String test(T t);
|
||||
interface HCInterf<T> {
|
||||
String hcMethod(T t);
|
||||
}
|
||||
|
||||
class HiddenClassSig<T> implements Test<T> {
|
||||
class HiddenClassSig<T> implements HCInterf<T> {
|
||||
private String realTest() { return "HiddenClassSig: "; }
|
||||
|
||||
public String test(T t) {
|
||||
public String hcMethod(T t) {
|
||||
String str = realTest();
|
||||
return str + t.toString();
|
||||
}
|
||||
@ -60,12 +62,12 @@ public class HiddenClassSigTest {
|
||||
private static void log(String str) { System.out.println(str); }
|
||||
|
||||
private static final String HCName = "P/Q/HiddenClassSig.class";
|
||||
private static final String DIR = Utils.TEST_CLASSES;
|
||||
private static final Path CLASSES_DIR = Paths.get(Utils.TEST_CLASSES);
|
||||
private static final String LOG_PREFIX = "HiddenClassSigTest: ";
|
||||
|
||||
static native void checkHiddenClass(Class klass, String sig);
|
||||
static native void checkHiddenClassArray(Class array, String sig);
|
||||
static native boolean checkFailed();
|
||||
static native boolean checkFailed(); // get native agent failing status
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -78,26 +80,14 @@ public class HiddenClassSigTest {
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] readClassFile(String classFileName) throws Exception {
|
||||
File classFile = new File(classFileName);
|
||||
try (FileInputStream in = new FileInputStream(classFile);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream())
|
||||
{
|
||||
int b;
|
||||
while ((b = in.read()) != -1) {
|
||||
out.write(b);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
static Class<?> defineHiddenClass(String classFileName) throws Exception {
|
||||
Lookup lookup = MethodHandles.lookup();
|
||||
byte[] bytes = readClassFile(DIR + File.separator + classFileName);
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(classFileName));
|
||||
Class<?> hc = lookup.defineHiddenClass(bytes, false).lookupClass();
|
||||
return hc;
|
||||
}
|
||||
|
||||
// print all name variations
|
||||
static void logClassInfo(Class<?> klass) {
|
||||
log("\n### Testing class: " + klass);
|
||||
log(LOG_PREFIX + "isHidden: " + klass.isHidden());
|
||||
@ -110,23 +100,29 @@ public class HiddenClassSigTest {
|
||||
|
||||
private static final String HC_NAME = "P.Q.HiddenClassSig";
|
||||
private static final String HC_SUFFIX_REGEX = "0x[0-9a-f]+";
|
||||
|
||||
static boolean checkName(Class<?> klass, String name, String toString) {
|
||||
boolean failed = false;
|
||||
String regex = "";
|
||||
Class<?> c = klass;
|
||||
|
||||
// for an array add the prefix "[" for each dimension
|
||||
while (c.isArray()) {
|
||||
regex = "\\[" + regex;
|
||||
c = c.componentType();
|
||||
}
|
||||
// construct the expected name
|
||||
if (klass.isArray()) {
|
||||
regex += "L" + HC_NAME + "/" + HC_SUFFIX_REGEX + ";";
|
||||
} else {
|
||||
regex = HC_NAME + "/" + HC_SUFFIX_REGEX;
|
||||
}
|
||||
// check the name matches the expected
|
||||
if (!name.matches(regex)) {
|
||||
log("Test FAIL: result of Class::getName" + " \"" + name + "\" does not match " + regex);
|
||||
failed = true;
|
||||
}
|
||||
// check the string name matches the expected
|
||||
if (!toString.matches("class " + regex)) {
|
||||
log("Test FAIL: result of Class::toString" + " \"" + name + "\" does not match " + regex);
|
||||
failed = true;
|
||||
@ -136,12 +132,16 @@ public class HiddenClassSigTest {
|
||||
|
||||
static boolean checkTypeName(Class<?> klass, String name) {
|
||||
boolean failed = false;
|
||||
// construct the expected type name
|
||||
String regex = HC_NAME + "/" + HC_SUFFIX_REGEX;
|
||||
Class<?> c = klass;
|
||||
|
||||
// for an array add the suffix "[]" for each dimension
|
||||
while (c.isArray()) {
|
||||
c = c.componentType();
|
||||
regex = regex + "\\[\\]";
|
||||
}
|
||||
// check the type name matches the expected
|
||||
if (!name.matches(regex)) {
|
||||
log("Test FAIL: result of Class::getTypeName" + " \"" + name + "\" does not match " + regex);
|
||||
failed = true;
|
||||
@ -152,14 +152,19 @@ public class HiddenClassSigTest {
|
||||
static boolean checkGenericString(Class<?> klass, String name) {
|
||||
boolean failed = false;
|
||||
Class<?> c = klass;
|
||||
// construct the expected generic string
|
||||
String regex = HC_NAME + "/" + HC_SUFFIX_REGEX + "<T>";
|
||||
|
||||
// add the expected name prefix for a non-array class
|
||||
if (!klass.isArray()) {
|
||||
regex = "class " + regex;
|
||||
}
|
||||
// for an array get the bottom component class
|
||||
while (c.isArray()) {
|
||||
c = c.componentType();
|
||||
regex = regex + "\\[\\]";
|
||||
}
|
||||
// check the generic string matches the expected
|
||||
if (!name.matches(regex)) {
|
||||
log("Test FAIL: result of Class::toGenericString" + " \"" + name + "\" does not match " + regex);
|
||||
failed = true;
|
||||
@ -169,12 +174,16 @@ public class HiddenClassSigTest {
|
||||
|
||||
static boolean checkDescriptorString(Class<?> klass, String name) {
|
||||
boolean failed = false;
|
||||
// construct the expected descriptor string
|
||||
String regex = "L" + HC_NAME.replace('.', '/') + "." + HC_SUFFIX_REGEX + ";";
|
||||
Class<?> c = klass;
|
||||
|
||||
// for array get the bottom component class
|
||||
while (c.isArray()) {
|
||||
regex = "\\[" + regex;
|
||||
c = c.componentType();
|
||||
}
|
||||
// check the descriptor string matches the expected
|
||||
if (!name.matches(regex)) {
|
||||
log("Test FAIL: result of Class::descriptorString" + " \"" + name + "\" does not match " + regex);
|
||||
failed = true;
|
||||
@ -186,15 +195,19 @@ public class HiddenClassSigTest {
|
||||
boolean failed = false;
|
||||
logClassInfo(klass);
|
||||
|
||||
// verify all name variations
|
||||
failed |= checkName(klass, klass.getName(), klass.toString());
|
||||
failed |= checkTypeName(klass, klass.getTypeName());
|
||||
failed |= checkGenericString(klass, klass.toGenericString());
|
||||
failed |= checkDescriptorString(klass, klass.descriptorString());
|
||||
|
||||
// an array class is never hidden
|
||||
if (klass.isArray() && klass.isHidden()) {
|
||||
log("Test FAIL: an array class is never hidden");
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// verify hidden class array or class by the native agent
|
||||
if (klass.isArray()) {
|
||||
checkHiddenClassArray(klass, klass.descriptorString());
|
||||
} else {
|
||||
@ -205,25 +218,31 @@ public class HiddenClassSigTest {
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
log(LOG_PREFIX + "started");
|
||||
|
||||
// define a hidden class to test
|
||||
Class<?> hc = defineHiddenClass(HCName);
|
||||
String baseName = ("" + hc).substring("class ".length());
|
||||
|
||||
Test<String> t = (Test<String>)hc.newInstance();
|
||||
String str = t.test("Test generic hidden class");
|
||||
log(LOG_PREFIX + "hc.test() returned string: " + str);
|
||||
// allocate a hidden class instance to test
|
||||
HCInterf<String> testObj = (HCInterf<String>)hc.newInstance();
|
||||
|
||||
String str = testObj.hcMethod("Test generic hidden class");
|
||||
log(LOG_PREFIX + "hc.hcMethod() returned string: " + str);
|
||||
|
||||
// test all hidden class name/signature variations
|
||||
boolean failed = testClass(hc);
|
||||
|
||||
// test all hidden class array name/signature variations
|
||||
Class<?> hcArr = hc.arrayType();
|
||||
failed |= testClass(hcArr);
|
||||
|
||||
// test all hidden class double array name/signature variations
|
||||
Class<?> hcArrArr = hcArr.arrayType();
|
||||
failed |= testClass(hcArrArr);
|
||||
|
||||
if (failed) {
|
||||
if (failed) { // check the java part failing status
|
||||
throw new RuntimeException("FAIL: failed status from java part");
|
||||
}
|
||||
if (checkFailed()) {
|
||||
if (checkFailed()) { // check the native agent failing status
|
||||
throw new RuntimeException("FAIL: failed status from native agent");
|
||||
}
|
||||
log(LOG_PREFIX + "finished");
|
||||
|
@ -26,13 +26,14 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
static const char* EXP_INTERF_SIG = "LP/Q/Test;";
|
||||
static const char* EXP_INTERF_SIG = "LP/Q/HCInterf;";
|
||||
static const char* SIG_START = "LP/Q/HiddenClassSig";
|
||||
static const size_t SIG_START_LEN = strlen(SIG_START);
|
||||
static const int ACC_INTERFACE = 0x0200; // Interface class modifiers bit
|
||||
|
||||
static jvmtiEnv *jvmti = NULL;
|
||||
static jint class_load_count = 0;
|
||||
static jint class_prep_count = 0;
|
||||
static bool failed = false;
|
||||
|
||||
#define LOG0(str) { printf(str); fflush(stdout); }
|
||||
@ -86,6 +87,7 @@ check_class_signature(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, bool is_hidden
|
||||
char* gsig = NULL;
|
||||
jvmtiError err;
|
||||
|
||||
// get class signature
|
||||
err = jvmti->GetClassSignature(klass, &sig, &gsig);
|
||||
CHECK_JVMTI_ERROR(jni, err, "check_hidden_class: Error in JVMTI GetClassSignature");
|
||||
|
||||
@ -160,9 +162,11 @@ check_hidden_class_loader(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) {
|
||||
char* sig = NULL;
|
||||
jclass kls = loader_classes[idx];
|
||||
|
||||
// GetClassLoaderClasses should not return any hidden classes
|
||||
if (!is_hidden(jni, kls)) {
|
||||
continue;
|
||||
}
|
||||
// get class signature
|
||||
err = jvmti->GetClassSignature(kls, &sig, NULL);
|
||||
CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_loader: Error in JVMTI GetClassSignature");
|
||||
|
||||
@ -189,11 +193,11 @@ check_hidden_class_impl_interf(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) {
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// check the interface signature is matching the expected
|
||||
// get interface signature
|
||||
err = jvmti->GetClassSignature(interfaces[0], &sig, NULL);
|
||||
CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_impl_interf: Error in JVMTI GetClassSignature for implemented interface");
|
||||
|
||||
// check the interface signature is matching the expected
|
||||
if (strcmp(sig, EXP_INTERF_SIG) != 0) {
|
||||
LOG2("check_hidden_class_impl_interf: FAIL: implemented interface signature: %s, expected to be: %s\n",
|
||||
sig, EXP_INTERF_SIG);
|
||||
@ -236,6 +240,43 @@ check_hidden_class_array(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass_array, const
|
||||
LOG0("### Native agent: check_hidden_class_array finished\n");
|
||||
}
|
||||
|
||||
/* Process a CLASS_LOAD or aClassPrepare event. */
|
||||
static void process_class_event(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass,
|
||||
int* event_count_ptr, const char* event_name) {
|
||||
char* sig = NULL;
|
||||
char* gsig = NULL;
|
||||
jvmtiError err;
|
||||
|
||||
// get class signature
|
||||
err = jvmti->GetClassSignature(klass, &sig, &gsig);
|
||||
CHECK_JVMTI_ERROR(jni, err, "ClassLoad event: Error in JVMTI GetClassSignature");
|
||||
|
||||
// check if this is an expected class event for hidden class
|
||||
if (strlen(sig) > strlen(SIG_START) &&
|
||||
strncmp(sig, SIG_START, SIG_START_LEN) == 0 &&
|
||||
is_hidden(jni, klass)) {
|
||||
(*event_count_ptr)++;
|
||||
if (gsig == NULL) {
|
||||
LOG1("%s event: FAIL: GetClassSignature returned NULL generic signature for hidden class\n", event_name);
|
||||
failed = true;
|
||||
}
|
||||
LOG2("%s event: hidden class with sig: %s\n", event_name, sig);
|
||||
LOG2("%s event: hidden class with gsig: %s\n", event_name, gsig);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check CLASS_LOAD event is generated for the given hidden class. */
|
||||
static void JNICALL
|
||||
ClassLoad(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass) {
|
||||
process_class_event(jvmti, jni, klass, &class_load_count, "ClassLoad");
|
||||
}
|
||||
|
||||
/* Check CLASS_PREPARE event is generated for the given hidden class. */
|
||||
static void JNICALL
|
||||
ClassPrepare(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass) {
|
||||
process_class_event(jvmti, jni, klass, &class_prep_count, "ClassPrepare");
|
||||
}
|
||||
|
||||
/* Enable CLASS_LOAD event notification mode. */
|
||||
static void JNICALL
|
||||
VMInit(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
|
||||
@ -244,38 +285,18 @@ VMInit(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
|
||||
printf("VMInit event: SIG_START: %s, SIG_START_LEN: %d\n", SIG_START, (int)SIG_START_LEN);
|
||||
fflush(stdout);
|
||||
|
||||
// enable ClassLoad event notification mode
|
||||
err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
|
||||
CHECK_JVMTI_ERROR(jni, err, "VMInit event: Error in enabling ClassLoad events notification");
|
||||
}
|
||||
|
||||
/* Check CLASS_LOAD event is generated for the given hidden class. */
|
||||
static void JNICALL
|
||||
ClassLoad(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass) {
|
||||
char* sig = NULL;
|
||||
char* gsig = NULL;
|
||||
char* src_name = NULL;
|
||||
jvmtiError err;
|
||||
|
||||
err = jvmti->GetClassSignature(klass, &sig, &gsig);
|
||||
CHECK_JVMTI_ERROR(jni, err, "ClassLoad event: Error in JVMTI GetClassSignature");
|
||||
|
||||
if (strlen(sig) > strlen(SIG_START) &&
|
||||
strncmp(sig, SIG_START, SIG_START_LEN) == 0 &&
|
||||
is_hidden(jni, klass)) {
|
||||
class_load_count++;
|
||||
if (gsig == NULL) {
|
||||
LOG0("ClassLoad event: FAIL: GetClassSignature returned NULL generic signature for hidden class\n");
|
||||
failed = true;
|
||||
}
|
||||
LOG1("ClassLoad event: hidden class with sig: %s\n", sig);
|
||||
LOG1("ClassLoad event: hidden class with gsig: %s\n", gsig);
|
||||
}
|
||||
// enable ClassPrepare event notification mode
|
||||
err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL);
|
||||
CHECK_JVMTI_ERROR(jni, err, "VMInit event: Error in enabling ClassPrepare events notification");
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
|
||||
jvmtiEventCallbacks callbacks;
|
||||
jvmtiCapabilities caps;
|
||||
jvmtiError err;
|
||||
|
||||
LOG0("Agent_OnLoad: started\n");
|
||||
@ -288,6 +309,7 @@ Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
|
||||
// set required event callbacks
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.ClassLoad = &ClassLoad;
|
||||
callbacks.ClassPrepare = &ClassPrepare;
|
||||
callbacks.VMInit = &VMInit;
|
||||
|
||||
err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
|
||||
@ -297,16 +319,6 @@ Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
// add required capabilities
|
||||
memset(&caps, 0, sizeof(caps));
|
||||
caps.can_get_source_file_name = 1;
|
||||
err = jvmti->AddCapabilities(&caps);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
LOG1("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err);
|
||||
failed = true;
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
// enable VM_INIT event notification mode
|
||||
err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
@ -351,7 +363,13 @@ Java_P_Q_HiddenClassSigTest_checkHiddenClassArray(JNIEnv *jni, jclass klass, jcl
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_P_Q_HiddenClassSigTest_checkFailed(JNIEnv *jni, jclass klass) {
|
||||
if (class_load_count == 0) {
|
||||
LOG0("Native Agent: missed ClassLoad event for hidden class\n");
|
||||
// expected ClassLoad event was not generated for hidden class
|
||||
LOG0("Native Agent: FAIL: missed ClassLoad event for hidden class\n");
|
||||
failed = true;
|
||||
}
|
||||
if (class_prep_count == 0) {
|
||||
// expected ClassPrepare event was not generated for hidden class
|
||||
LOG0("Native Agent: FAIL: missed ClassPrepare event for hidden class\n");
|
||||
failed = true;
|
||||
}
|
||||
return failed;
|
||||
|
Loading…
x
Reference in New Issue
Block a user