2727import static com .oracle .svm .core .util .VMError .guarantee ;
2828import static com .oracle .svm .jni .JNIObjectHandles .nullHandle ;
2929import static com .oracle .svm .jvmtiagentbase .Support .callObjectMethod ;
30+ import static com .oracle .svm .jvmtiagentbase .Support .callObjectMethodL ;
3031import static com .oracle .svm .jvmtiagentbase .Support .check ;
3132import static com .oracle .svm .jvmtiagentbase .Support .checkJni ;
3233import static com .oracle .svm .jvmtiagentbase .Support .checkNoException ;
5051import static com .oracle .svm .jvmtiagentbase .jvmti .JvmtiEvent .JVMTI_EVENT_BREAKPOINT ;
5152import static com .oracle .svm .jvmtiagentbase .jvmti .JvmtiEvent .JVMTI_EVENT_CLASS_PREPARE ;
5253import static com .oracle .svm .jvmtiagentbase .jvmti .JvmtiEvent .JVMTI_EVENT_NATIVE_METHOD_BIND ;
54+ import static com .oracle .svm .jvmtiagentbase .jvmti .JvmtiEvent .JVMTI_EVENT_CLASS_FILE_LOAD_HOOK ;
5355import static org .graalvm .word .WordFactory .nullPointer ;
5456
5557import java .nio .ByteBuffer ;
6365import java .util .concurrent .ConcurrentMap ;
6466import java .util .concurrent .locks .ReentrantLock ;
6567
68+ import com .oracle .svm .core .util .JavaClassUtil ;
6669import org .graalvm .compiler .core .common .NumUtil ;
6770import org .graalvm .nativeimage .StackValue ;
6871import org .graalvm .nativeimage .UnmanagedMemory ;
@@ -126,7 +129,7 @@ final class BreakpointInterceptor {
126129 private static NativeImageAgent agent ;
127130
128131 private static Map <Long , Breakpoint > installedBreakpoints ;
129-
132+ private static List < String > unsupportedExceptions = new ArrayList <>();
130133 /**
131134 * A map from {@link JNIMethodId} to entry point addresses for bound Java {@code native}
132135 * methods, NOT considering our intercepting functions, i.e., these are the original entry
@@ -1085,6 +1088,51 @@ private static void installBreakpointIfClassLoader(JNIEnvironment jni, JNIObject
10851088 }
10861089 }
10871090
1091+ @ CEntryPoint
1092+ @ CEntryPointOptions (prologue = AgentIsolate .Prologue .class )
1093+ private static void onClassFileLoadHook (@ SuppressWarnings ("unused" ) JvmtiEnv jvmti , JNIEnvironment jni ,
1094+ @ SuppressWarnings ("unused" ) JNIObjectHandle classBeingRedefined , JNIObjectHandle loader , CCharPointer name , JNIObjectHandle protectionDomain , int classDataLen ,
1095+ CCharPointer classData , @ SuppressWarnings ("unused" ) CIntPointer newClassDataLen , @ SuppressWarnings ("unused" ) CCharPointerPointer newClassData ) {
1096+ boolean nameIsNull = name .isNull ();
1097+ if (isDynamicallyGenerated (jni , loader , nameIsNull , nameIsNull ? "" : fromCString (name ))) {
1098+ byte [] contents = new byte [classDataLen ];
1099+ CTypeConversion .asByteBuffer (classData , classDataLen ).get (contents );
1100+ String definedClassName = nameIsNull ? JavaClassUtil .getClassName (contents ) : fromCString (name );
1101+ ClassLoaderDefineClassSupport .trace (traceWriter , contents , definedClassName , true );
1102+ }
1103+ }
1104+
1105+ private static boolean isDynamicallyGenerated (JNIEnvironment jni , JNIObjectHandle classLoader , boolean inputNameIsNull , String definedClassName ) {
1106+ boolean isDynamicallyGenerated ;
1107+ // 1. Classloader is null, it's a system class.
1108+ // The class is not dynamically generated.
1109+ if (classLoader .equal (nullHandle ())){
1110+ isDynamicallyGenerated = false ;
1111+ } else {
1112+ // 2. Don't have a name for class before defining.
1113+ // The class is dynamically generated.
1114+ if (inputNameIsNull ) {
1115+ isDynamicallyGenerated = true ;
1116+ } else {
1117+ // 3. A dynamically defined class always return null
1118+ // when call java.lang.ClassLoader.getResource(classname)
1119+ // This is the accurate but slow way.
1120+ String asResourceName = definedClassName .replace ('.' , '/' ) + ".class" ;
1121+ try (CCharPointerHolder resourceNameHolder = toCString (asResourceName );) {
1122+ JNIObjectHandle resourceNameJString = jniFunctions ().getNewStringUTF ().invoke (jni , resourceNameHolder .get ());
1123+ if (agent .handles () == null ) {
1124+ // agent's handles is created at onVMStart.
1125+ isDynamicallyGenerated = false ;
1126+ } else {
1127+ JNIObjectHandle returnValue = callObjectMethodL (jni , classLoader , agent .handles ().javaLangClassLoaderGetResource , resourceNameJString );
1128+ isDynamicallyGenerated = returnValue .equal (nullHandle ());
1129+ }
1130+ }
1131+ }
1132+ }
1133+ return isDynamicallyGenerated ;
1134+ }
1135+
10881136 private static final CEntryPointLiteral <CFunctionPointer > onBreakpointLiteral = CEntryPointLiteral .create (BreakpointInterceptor .class , "onBreakpoint" ,
10891137 JvmtiEnv .class , JNIEnvironment .class , JNIObjectHandle .class , JNIMethodId .class , long .class );
10901138
@@ -1094,6 +1142,9 @@ private static void installBreakpointIfClassLoader(JNIEnvironment jni, JNIObject
10941142 private static final CEntryPointLiteral <CFunctionPointer > onClassPrepareLiteral = CEntryPointLiteral .create (BreakpointInterceptor .class , "onClassPrepare" ,
10951143 JvmtiEnv .class , JNIEnvironment .class , JNIObjectHandle .class , JNIObjectHandle .class );
10961144
1145+ private static final CEntryPointLiteral <CFunctionPointer > onClassFileLoadHookLiteral = CEntryPointLiteral .create (BreakpointInterceptor .class , "onClassFileLoadHook" ,
1146+ JvmtiEnv .class , JNIEnvironment .class , JNIObjectHandle .class , JNIObjectHandle .class , CCharPointer .class , JNIObjectHandle .class , int .class , CCharPointer .class , CIntPointer .class , CCharPointerPointer .class );
1147+
10971148 public static void onLoad (JvmtiEnv jvmti , JvmtiEventCallbacks callbacks , TraceWriter writer , NativeImageAgent nativeImageTracingAgent ,
10981149 boolean exptlClassLoaderSupport ) {
10991150
@@ -1106,6 +1157,7 @@ public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, TraceWr
11061157 capabilities .setCanGenerateBreakpointEvents (1 );
11071158 capabilities .setCanAccessLocalVariables (1 );
11081159 capabilities .setCanGenerateNativeMethodBindEvents (1 );
1160+ capabilities .setCanGenerateAllClassHookEvents (1 );
11091161 if (exptlClassLoaderSupport ) {
11101162 capabilities .setCanGetBytecodes (1 );
11111163 capabilities .setCanGetConstantPool (1 );
@@ -1125,6 +1177,9 @@ public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, TraceWr
11251177
11261178 BreakpointInterceptor .boundNativeMethods = new HashMap <>();
11271179 Support .check (jvmti .getFunctions ().SetEventNotificationMode ().invoke (jvmti , JvmtiEventMode .JVMTI_ENABLE , JVMTI_EVENT_NATIVE_METHOD_BIND , nullHandle ()));
1180+
1181+ callbacks .setClassFileLoadHook (onClassFileLoadHookLiteral .getFunctionPointer ());
1182+ Support .check (jvmti .getFunctions ().SetEventNotificationMode ().invoke (jvmti , JvmtiEventMode .JVMTI_ENABLE , JVMTI_EVENT_CLASS_FILE_LOAD_HOOK , nullHandle ()));
11281183 }
11291184
11301185 public static void onVMInit (JvmtiEnv jvmti , JNIEnvironment jni ) {
@@ -1269,6 +1324,19 @@ private static void bindNativeBreakpoint(JNIEnvironment jni, NativeBreakpoint bp
12691324 }
12701325 }
12711326
1327+ public static void reportExceptions () {
1328+ if (!unsupportedExceptions .isEmpty ()) {
1329+ System .err .println (unsupportedExceptions .size () + " unsupported features are detected " );
1330+ StringBuilder errorMsg = new StringBuilder ();
1331+ for (int i = 0 ; i < unsupportedExceptions .size (); i ++) {
1332+ errorMsg .append (unsupportedExceptions .get (i )).append ("\n " );
1333+ }
1334+ throw new UnsupportedOperationException (errorMsg .toString ());
1335+ } else {
1336+ unsupportedExceptions = null ;
1337+ }
1338+ }
1339+
12721340 public static void onUnload () {
12731341 installedBreakpoints = null ;
12741342 nativeBreakpoints = null ;
0 commit comments