8242237: Improve JVM TI HiddenClasses tests

Improve JVM TI HiddenClasses test: cleanup + more comments

Reviewed-by: lmesnik, amenkov
This commit is contained in:
Serguei Spitsyn 2020-04-28 02:37:15 +00:00
parent a740f83da3
commit 223ca800cc
2 changed files with 102 additions and 65 deletions
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;