Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
loicottet committed Aug 16, 2021
1 parent 7e6c2bb commit 35ac872
Show file tree
Hide file tree
Showing 24 changed files with 765 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,8 @@ public interface ReflectionRegistry {

void register(boolean finalIsWritable, Field... fields);

@SuppressWarnings("unused")
default void registerAsQueried(Executable... methods) {
// TODO unimplemented/shouldNotReachHere?
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ final class BreakpointInterceptor {
/** Enables experimental support for class definitions via {@code ClassLoader.defineClass}. */
private static boolean experimentalClassDefineSupport = false;

/** Enables tracking of reflection queries for fine-tuned configuration. */
private static boolean trackReflectionMetadata = false;

/**
* Locations in methods where explicit calls to {@code ClassLoader.loadClass} have been found.
*/
Expand Down Expand Up @@ -367,6 +370,31 @@ private static boolean getEnclosingMethod(JNIEnvironment jni, Breakpoint bp, Int
return true;
}

private static boolean invokeMethod(JNIEnvironment jni, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIObjectHandle self = getObjectArgument(0);

JNIObjectHandle declaring = Support.callObjectMethod(jni, self, agent.handles().javaLangReflectMemberGetDeclaringClass);
if (clearException(jni)) {
declaring = nullHandle();
}

JNIObjectHandle nameHandle = Support.callObjectMethod(jni, self, agent.handles().javaLangReflectMemberGetName);
if (clearException(jni)) {
nameHandle = nullHandle();
}
String name = fromJniString(jni, nameHandle);

JNIObjectHandle paramTypesHandle = Support.callObjectMethod(jni, self, agent.handles().javaLangReflectMethodGetParameterTypes);
if (clearException(jni)) {
paramTypesHandle = nullHandle();
}
Object paramTypes = getClassArrayNames(jni, paramTypesHandle);

traceBreakpoint(jni, declaring, declaring, callerClass, bp.specification.methodName, self.notEqual(nullHandle()), state.getFullStackTraceOrNull(), name, paramTypes);
return true;
}

private static boolean newInstance(JNIEnvironment jni, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIMethodId result = nullPointer();
Expand Down Expand Up @@ -1068,12 +1096,13 @@ private static void onClassFileLoadHook(@SuppressWarnings("unused") JvmtiEnv jvm

public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, Tracer writer, NativeImageAgent nativeImageTracingAgent,
Supplier<InterceptedState> currentThreadJavaStackAccessSupplier,
boolean exptlClassLoaderSupport, boolean exptlClassDefineSupport) {
boolean exptlClassLoaderSupport, boolean exptlClassDefineSupport, boolean trackReflectionData) {
BreakpointInterceptor.tracer = writer;
BreakpointInterceptor.agent = nativeImageTracingAgent;
BreakpointInterceptor.interceptedStateSupplier = currentThreadJavaStackAccessSupplier;
BreakpointInterceptor.experimentalClassLoaderSupport = exptlClassLoaderSupport;
BreakpointInterceptor.experimentalClassDefineSupport = exptlClassDefineSupport;
BreakpointInterceptor.trackReflectionMetadata = trackReflectionData;

JvmtiCapabilities capabilities = UnmanagedMemory.calloc(SizeOf.get(JvmtiCapabilities.class));
check(jvmti.getFunctions().GetCapabilities().invoke(jvmti, capabilities));
Expand Down Expand Up @@ -1122,7 +1151,13 @@ public static void onVMInit(JvmtiEnv jvmti, JNIEnvironment jni) {

JNIObjectHandle lastClass = nullHandle();
String lastClassName = null;
for (BreakpointSpecification br : BREAKPOINT_SPECIFICATIONS) {
BreakpointSpecification[] breakpointSpecifications = BREAKPOINT_SPECIFICATIONS;
if (trackReflectionMetadata) {
breakpointSpecifications = new BreakpointSpecification[BREAKPOINT_SPECIFICATIONS.length + REFLECTION_QUERIES_BREAKPOINT_SPECIFICATIONS.length];
System.arraycopy(BREAKPOINT_SPECIFICATIONS, 0, breakpointSpecifications, 0, BREAKPOINT_SPECIFICATIONS.length);
System.arraycopy(REFLECTION_QUERIES_BREAKPOINT_SPECIFICATIONS, 0, breakpointSpecifications, BREAKPOINT_SPECIFICATIONS.length, REFLECTION_QUERIES_BREAKPOINT_SPECIFICATIONS.length);
}
for (BreakpointSpecification br : breakpointSpecifications) {
JNIObjectHandle clazz = nullHandle();
if (lastClassName != null && lastClassName.equals(br.className)) {
clazz = lastClass;
Expand Down Expand Up @@ -1245,24 +1280,17 @@ private interface BreakpointHandler {
brk("java/lang/Class", "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;", BreakpointInterceptor::forName),

brk("java/lang/Class", "getFields", "()[Ljava/lang/reflect/Field;", BreakpointInterceptor::getFields),
brk("java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;", BreakpointInterceptor::getMethods),
brk("java/lang/Class", "getConstructors", "()[Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getConstructors),
brk("java/lang/Class", "getClasses", "()[Ljava/lang/Class;", BreakpointInterceptor::getClasses),
brk("java/lang/Class", "getDeclaredFields", "()[Ljava/lang/reflect/Field;", BreakpointInterceptor::getDeclaredFields),
brk("java/lang/Class", "getDeclaredMethods", "()[Ljava/lang/reflect/Method;", BreakpointInterceptor::getDeclaredMethods),
brk("java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getDeclaredConstructors),
brk("java/lang/Class", "getDeclaredClasses", "()[Ljava/lang/Class;", BreakpointInterceptor::getDeclaredClasses),

brk("java/lang/Class", "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;", BreakpointInterceptor::getField),
brk("java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;", BreakpointInterceptor::getDeclaredField),
brk("java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", BreakpointInterceptor::getMethod),
brk("java/lang/Class", "getConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getConstructor),
brk("java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", BreakpointInterceptor::getDeclaredMethod),
brk("java/lang/Class", "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getConstructor),

brk("java/lang/Class", "getEnclosingMethod", "()Ljava/lang/reflect/Method;", BreakpointInterceptor::getEnclosingMethod),
brk("java/lang/Class", "getEnclosingConstructor", "()Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getEnclosingMethod),

brk("java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", BreakpointInterceptor::invokeMethod),
brk("java/lang/Class", "newInstance", "()Ljava/lang/Object;", BreakpointInterceptor::newInstance),
brk("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;", BreakpointInterceptor::newArrayInstance),
brk("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;[I)Ljava/lang/Object;", BreakpointInterceptor::newArrayInstanceMulti),
Expand Down Expand Up @@ -1353,6 +1381,18 @@ private interface BreakpointHandler {
private static final BreakpointSpecification CLASSLOADER_LOAD_CLASS_BREAKPOINT_SPECIFICATION = optionalBrk("java/lang/ClassLoader", "loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;", BreakpointInterceptor::loadClass);

private static final BreakpointSpecification[] REFLECTION_QUERIES_BREAKPOINT_SPECIFICATIONS = {
brk("java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;", BreakpointInterceptor::getMethods),
brk("java/lang/Class", "getConstructors", "()[Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getConstructors),
brk("java/lang/Class", "getDeclaredMethods", "()[Ljava/lang/reflect/Method;", BreakpointInterceptor::getDeclaredMethods),
brk("java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getDeclaredConstructors),

brk("java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", BreakpointInterceptor::getMethod),
brk("java/lang/Class", "getConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getConstructor),
brk("java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", BreakpointInterceptor::getDeclaredMethod),
brk("java/lang/Class", "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getConstructor),
};

private static BreakpointSpecification brk(String className, String methodName, String signature, BreakpointHandler handler) {
return new BreakpointSpecification(className, methodName, signature, handler, false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
boolean configurationWithOrigins = false;
int configWritePeriod = -1; // in seconds
int configWritePeriodInitialDelay = 1; // in seconds
boolean trackReflectionMetadata = false;

String[] tokens = !options.isEmpty() ? options.split(",") : new String[0];
for (String token : tokens) {
Expand Down Expand Up @@ -189,6 +190,8 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
build = Boolean.parseBoolean(getTokenValue(token));
} else if (token.equals("experimental-configuration-with-origins")) {
configurationWithOrigins = true;
} else if (token.equals("track-reflection-metadata")) {
trackReflectionMetadata = true;
} else {
return usage(1, "unknown option: '" + token + "'.");
}
Expand Down Expand Up @@ -306,7 +309,8 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
}

try {
BreakpointInterceptor.onLoad(jvmti, callbacks, tracer, this, interceptedStateSupplier, experimentalClassLoaderSupport, experimentalClassDefineSupport);
BreakpointInterceptor.onLoad(jvmti, callbacks, tracer, this, interceptedStateSupplier,
experimentalClassLoaderSupport, experimentalClassDefineSupport, trackReflectionMetadata);
} catch (Throwable t) {
return error(3, t.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet {

final JNIMethodId javaLangReflectMemberGetName;
final JNIMethodId javaLangReflectMemberGetDeclaringClass;
final JNIMethodId javaLangReflectMethodGetParameterTypes;

final JNIMethodId javaUtilEnumerationHasMoreElements;

Expand Down Expand Up @@ -76,8 +77,10 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet {
javaLangClassGetName = getMethodId(env, javaLangClass, "getName", "()Ljava/lang/String;", false);

JNIObjectHandle javaLangReflectMember = findClass(env, "java/lang/reflect/Member");
JNIObjectHandle javaLangReflectMethod = findClass(env, "java/lang/reflect/Method");
javaLangReflectMemberGetName = getMethodId(env, javaLangReflectMember, "getName", "()Ljava/lang/String;", false);
javaLangReflectMemberGetDeclaringClass = getMethodId(env, javaLangReflectMember, "getDeclaringClass", "()Ljava/lang/Class;", false);
javaLangReflectMethodGetParameterTypes = getMethodId(env, javaLangReflectMethod, "getParameterTypes", "()[Ljava/lang/Class;", false);

JNIObjectHandle javaUtilEnumeration = findClass(env, "java/util/Enumeration");
javaUtilEnumerationHasMoreElements = getMethodId(env, javaUtilEnumeration, "hasMoreElements", "()Z", false);
Expand Down
Loading

0 comments on commit 35ac872

Please sign in to comment.