|
44 | 44 | import static com.oracle.svm.jvmtiagentbase.Support.jvmtiFunctions;
|
45 | 45 | import static com.oracle.svm.jvmtiagentbase.Support.testException;
|
46 | 46 | import static com.oracle.svm.jvmtiagentbase.Support.toCString;
|
| 47 | +import static com.oracle.svm.jvmtiagentbase.Support.callObjectMethodL; |
47 | 48 | import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_BREAKPOINT;
|
48 | 49 | import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_CLASS_PREPARE;
|
49 | 50 | import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_NATIVE_METHOD_BIND;
|
50 | 51 | import static org.graalvm.word.WordFactory.nullPointer;
|
51 | 52 |
|
| 53 | +import java.io.IOException; |
52 | 54 | import java.nio.ByteBuffer;
|
53 | 55 | import java.nio.ByteOrder;
|
54 | 56 | import java.util.ArrayList;
|
|
61 | 63 | import java.util.concurrent.locks.ReentrantLock;
|
62 | 64 | import java.util.function.Supplier;
|
63 | 65 |
|
| 66 | +import com.oracle.svm.configure.trace.AccessAdvisor; |
64 | 67 | import org.graalvm.compiler.core.common.NumUtil;
|
| 68 | +import org.graalvm.compiler.phases.common.LazyValue; |
65 | 69 | import org.graalvm.nativeimage.StackValue;
|
66 | 70 | import org.graalvm.nativeimage.UnmanagedMemory;
|
67 | 71 | import org.graalvm.nativeimage.c.function.CEntryPoint;
|
|
84 | 88 | import com.oracle.svm.configure.config.ConfigurationMethod;
|
85 | 89 | import com.oracle.svm.core.c.function.CEntryPointOptions;
|
86 | 90 | import com.oracle.svm.core.util.VMError;
|
| 91 | + |
87 | 92 | import com.oracle.svm.jni.nativeapi.JNIEnvironment;
|
88 | 93 | import com.oracle.svm.jni.nativeapi.JNIMethodId;
|
89 | 94 | import com.oracle.svm.jni.nativeapi.JNINativeMethod;
|
@@ -131,6 +136,7 @@ final class BreakpointInterceptor {
|
131 | 136 | private static ResourceAccessVerifier resourceVerifier;
|
132 | 137 | private static NativeImageAgent agent;
|
133 | 138 |
|
| 139 | + private static AccessAdvisor accessAdvisor = new AccessAdvisor(); |
134 | 140 | private static Map<Long, Breakpoint> installedBreakpoints;
|
135 | 141 |
|
136 | 142 | /**
|
@@ -175,6 +181,18 @@ private static void traceBreakpoint(JNIEnvironment env, JNIObjectHandle clazz, J
|
175 | 181 | }
|
176 | 182 | }
|
177 | 183 |
|
| 184 | + static void traceBreakpoint(JNIEnvironment env, JNIObjectHandle clazz, JNIObjectHandle declaringClass, |
| 185 | + JNIObjectHandle callerClass, String function, boolean allowWrite, boolean unsafeAccess, Object result, |
| 186 | + String fieldName) { |
| 187 | + if (traceWriter != null) { |
| 188 | + traceWriter.traceCall("reflect", function, getClassNameOr(env, clazz, null, TraceWriter.UNKNOWN_VALUE), |
| 189 | + getClassNameOr(env, declaringClass, null, TraceWriter.UNKNOWN_VALUE), |
| 190 | + getClassNameOr(env, callerClass, null, TraceWriter.UNKNOWN_VALUE), result, allowWrite, unsafeAccess, |
| 191 | + fieldName); |
| 192 | + guarantee(!testException(env)); |
| 193 | + } |
| 194 | + } |
| 195 | + |
178 | 196 | private static boolean forName(JNIEnvironment jni, Breakpoint bp) {
|
179 | 197 | JNIObjectHandle callerClass = getDirectCallerClass();
|
180 | 198 | JNIObjectHandle name = getObjectArgument(0);
|
@@ -678,6 +696,64 @@ private static boolean handleGetSystemResources(JNIEnvironment jni, Breakpoint b
|
678 | 696 | return allowed;
|
679 | 697 | }
|
680 | 698 |
|
| 699 | + /** |
| 700 | + * java.lang.ClassLoader.postDefineClass is always called in java.lang.ClassLoader.defineClass, |
| 701 | + * so intercepting postDefineClass is equivalent to intercepting defineClass but with extra |
| 702 | + * benefit of being always able to get defined class' name even if defineClass' classname |
| 703 | + * parameter is null. |
| 704 | + */ |
| 705 | + @SuppressWarnings("unused") |
| 706 | + private static boolean postDefineClass(JNIEnvironment jni, Breakpoint bp) throws IOException { |
| 707 | + boolean isDynamicallyGenerated = false; |
| 708 | + // Get class name from the argument "name" of |
| 709 | + // defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) |
| 710 | + // The first argument is implicitly "this", so "name" is the 2nd parameter. |
| 711 | + String nameFromDefineClassParam = fromJniString(jni, getObjectArgument(1, 1)); |
| 712 | + final String definedClassName; |
| 713 | + // 1. Don't have a name for class before defining. |
| 714 | + // The class is dynamically generated. |
| 715 | + if (nameFromDefineClassParam == null) { |
| 716 | + isDynamicallyGenerated = true; |
| 717 | + // Get name from parameter "c" of method postDefineClass(Class<?> c, ProtectionDomain |
| 718 | + // pd) |
| 719 | + definedClassName = getClassNameOrNull(jni, getObjectArgument(1)); |
| 720 | + } else { |
| 721 | + definedClassName = nameFromDefineClassParam; |
| 722 | + // Filter out internal classes which are definitely not dynamically generated |
| 723 | + if (accessAdvisor.shouldIgnore(new LazyValue<>(() -> definedClassName), new LazyValue<>(() -> definedClassName))) { |
| 724 | + return false; |
| 725 | + } |
| 726 | + |
| 727 | + // 2. Class with name starts with $ or contains $$ is usually dynamically generated |
| 728 | + String className = definedClassName.substring(definedClassName.lastIndexOf('.') + 1); |
| 729 | + if (className.startsWith("$") || className.contains("$$")) { |
| 730 | + isDynamicallyGenerated = true; |
| 731 | + } else { |
| 732 | + // 3. A dynamically defined class always return null |
| 733 | + // when call java.lang.ClassLoader.getResource(classname) |
| 734 | + // This is the accurate but slow way. |
| 735 | + JNIObjectHandle self = getObjectArgument(0); |
| 736 | + String asResourceName = definedClassName.replace('.', '/') + ".class"; |
| 737 | + try (CCharPointerHolder resourceNameHolder = toCString(asResourceName);) { |
| 738 | + JNIObjectHandle resourceNameJString = jniFunctions().getNewStringUTF().invoke(jni, resourceNameHolder.get()); |
| 739 | + JNIObjectHandle returnValue = callObjectMethodL(jni, self, agent.handles().javaLangClassLoaderGetResource, resourceNameJString); |
| 740 | + isDynamicallyGenerated = returnValue.equal(nullHandle()); |
| 741 | + } |
| 742 | + } |
| 743 | + } |
| 744 | + if (isDynamicallyGenerated) { |
| 745 | + JNIObjectHandle callerClass = getDirectCallerClass(); |
| 746 | + AbstractDynamicClassGenerationSupport dynamicSupport = AbstractDynamicClassGenerationSupport.getDynamicClassGenerationSupport(jni, callerClass, |
| 747 | + definedClassName, traceWriter, agent); |
| 748 | + if (!dynamicSupport.dumpDefinedClass()) { |
| 749 | + return false; |
| 750 | + } |
| 751 | + return dynamicSupport.traceReflects(); |
| 752 | + } else { |
| 753 | + return true; |
| 754 | + } |
| 755 | + } |
| 756 | + |
681 | 757 | private static boolean newProxyInstance(JNIEnvironment jni, Breakpoint bp) {
|
682 | 758 | JNIObjectHandle callerClass = getDirectCallerClass();
|
683 | 759 | JNIObjectHandle classLoader = getObjectArgument(0);
|
@@ -1177,7 +1253,7 @@ public static void onUnload() {
|
1177 | 1253 | }
|
1178 | 1254 |
|
1179 | 1255 | private interface BreakpointHandler {
|
1180 |
| - boolean dispatch(JNIEnvironment jni, Breakpoint bp); |
| 1256 | + boolean dispatch(JNIEnvironment jni, Breakpoint bp) throws IOException; |
1181 | 1257 | }
|
1182 | 1258 |
|
1183 | 1259 | private static final BreakpointSpecification[] BREAKPOINT_SPECIFICATIONS = {
|
@@ -1217,6 +1293,10 @@ private interface BreakpointHandler {
|
1217 | 1293 | brk("java/lang/reflect/Proxy", "getProxyClass", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;)Ljava/lang/Class;", BreakpointInterceptor::getProxyClass),
|
1218 | 1294 | brk("java/lang/reflect/Proxy", "newProxyInstance",
|
1219 | 1295 | "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;", BreakpointInterceptor::newProxyInstance),
|
| 1296 | + /* |
| 1297 | + * For dumping dynamically generated classes |
| 1298 | + */ |
| 1299 | + brk("java/lang/ClassLoader", "postDefineClass", "(Ljava/lang/Class;Ljava/security/ProtectionDomain;)V", BreakpointInterceptor::postDefineClass), |
1220 | 1300 |
|
1221 | 1301 | optionalBrk("java/util/ResourceBundle",
|
1222 | 1302 | "getBundleImpl",
|
|
0 commit comments