27
27
import static com .oracle .svm .core .util .VMError .guarantee ;
28
28
import static com .oracle .svm .jni .JNIObjectHandles .nullHandle ;
29
29
import static com .oracle .svm .jvmtiagentbase .Support .callObjectMethod ;
30
+ import static com .oracle .svm .jvmtiagentbase .Support .callObjectMethodL ;
30
31
import static com .oracle .svm .jvmtiagentbase .Support .check ;
31
32
import static com .oracle .svm .jvmtiagentbase .Support .checkJni ;
32
33
import static com .oracle .svm .jvmtiagentbase .Support .checkNoException ;
50
51
import static com .oracle .svm .jvmtiagentbase .jvmti .JvmtiEvent .JVMTI_EVENT_BREAKPOINT ;
51
52
import static com .oracle .svm .jvmtiagentbase .jvmti .JvmtiEvent .JVMTI_EVENT_CLASS_PREPARE ;
52
53
import 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 ;
53
55
import static org .graalvm .word .WordFactory .nullPointer ;
54
56
55
57
import java .nio .ByteBuffer ;
63
65
import java .util .concurrent .ConcurrentMap ;
64
66
import java .util .concurrent .locks .ReentrantLock ;
65
67
68
+ import com .oracle .svm .core .util .JavaClassUtil ;
66
69
import org .graalvm .compiler .core .common .NumUtil ;
67
70
import org .graalvm .nativeimage .StackValue ;
68
71
import org .graalvm .nativeimage .UnmanagedMemory ;
@@ -126,7 +129,7 @@ final class BreakpointInterceptor {
126
129
private static NativeImageAgent agent ;
127
130
128
131
private static Map <Long , Breakpoint > installedBreakpoints ;
129
-
132
+ private static List < String > unsupportedExceptions = new ArrayList <>();
130
133
/**
131
134
* A map from {@link JNIMethodId} to entry point addresses for bound Java {@code native}
132
135
* methods, NOT considering our intercepting functions, i.e., these are the original entry
@@ -1085,6 +1088,52 @@ private static void installBreakpointIfClassLoader(JNIEnvironment jni, JNIObject
1085
1088
}
1086
1089
}
1087
1090
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 , @ SuppressWarnings ("unused" ) JNIObjectHandle protectionDomain ,
1095
+ int classDataLen ,
1096
+ CCharPointer classData , @ SuppressWarnings ("unused" ) CIntPointer newClassDataLen , @ SuppressWarnings ("unused" ) CCharPointerPointer newClassData ) {
1097
+ boolean nameIsNull = name .isNull ();
1098
+ if (isDynamicallyGenerated (jni , loader , nameIsNull , nameIsNull ? "" : fromCString (name ))) {
1099
+ byte [] contents = new byte [classDataLen ];
1100
+ CTypeConversion .asByteBuffer (classData , classDataLen ).get (contents );
1101
+ String definedClassName = nameIsNull ? JavaClassUtil .getClassName (contents ) : fromCString (name );
1102
+ ClassLoaderDefineClassSupport .trace (traceWriter , contents , definedClassName , true );
1103
+ }
1104
+ }
1105
+
1106
+ private static boolean isDynamicallyGenerated (JNIEnvironment jni , JNIObjectHandle classLoader , boolean inputNameIsNull , String definedClassName ) {
1107
+ boolean isDynamicallyGenerated ;
1108
+ // 1. Classloader is null, it's a system class.
1109
+ // The class is not dynamically generated.
1110
+ if (classLoader .equal (nullHandle ())) {
1111
+ isDynamicallyGenerated = false ;
1112
+ } else {
1113
+ // 2. Don't have a name for class before defining.
1114
+ // The class is dynamically generated.
1115
+ if (inputNameIsNull ) {
1116
+ isDynamicallyGenerated = true ;
1117
+ } else {
1118
+ // 3. A dynamically defined class always return null
1119
+ // when call java.lang.ClassLoader.getResource(classname)
1120
+ // This is the accurate but slow way.
1121
+ String asResourceName = definedClassName .replace ('.' , '/' ) + ".class" ;
1122
+ try (CCharPointerHolder resourceNameHolder = toCString (asResourceName );) {
1123
+ JNIObjectHandle resourceNameJString = jniFunctions ().getNewStringUTF ().invoke (jni , resourceNameHolder .get ());
1124
+ if (agent .handles () == null ) {
1125
+ // agent's handles is created at onVMStart.
1126
+ isDynamicallyGenerated = false ;
1127
+ } else {
1128
+ JNIObjectHandle returnValue = callObjectMethodL (jni , classLoader , agent .handles ().javaLangClassLoaderGetResource , resourceNameJString );
1129
+ isDynamicallyGenerated = returnValue .equal (nullHandle ());
1130
+ }
1131
+ }
1132
+ }
1133
+ }
1134
+ return isDynamicallyGenerated ;
1135
+ }
1136
+
1088
1137
private static final CEntryPointLiteral <CFunctionPointer > onBreakpointLiteral = CEntryPointLiteral .create (BreakpointInterceptor .class , "onBreakpoint" ,
1089
1138
JvmtiEnv .class , JNIEnvironment .class , JNIObjectHandle .class , JNIMethodId .class , long .class );
1090
1139
@@ -1094,6 +1143,10 @@ private static void installBreakpointIfClassLoader(JNIEnvironment jni, JNIObject
1094
1143
private static final CEntryPointLiteral <CFunctionPointer > onClassPrepareLiteral = CEntryPointLiteral .create (BreakpointInterceptor .class , "onClassPrepare" ,
1095
1144
JvmtiEnv .class , JNIEnvironment .class , JNIObjectHandle .class , JNIObjectHandle .class );
1096
1145
1146
+ private static final CEntryPointLiteral <CFunctionPointer > onClassFileLoadHookLiteral = CEntryPointLiteral .create (BreakpointInterceptor .class , "onClassFileLoadHook" ,
1147
+ JvmtiEnv .class , JNIEnvironment .class , JNIObjectHandle .class , JNIObjectHandle .class , CCharPointer .class , JNIObjectHandle .class , int .class , CCharPointer .class , CIntPointer .class ,
1148
+ CCharPointerPointer .class );
1149
+
1097
1150
public static void onLoad (JvmtiEnv jvmti , JvmtiEventCallbacks callbacks , TraceWriter writer , NativeImageAgent nativeImageTracingAgent ,
1098
1151
boolean exptlClassLoaderSupport ) {
1099
1152
@@ -1106,6 +1159,7 @@ public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, TraceWr
1106
1159
capabilities .setCanGenerateBreakpointEvents (1 );
1107
1160
capabilities .setCanAccessLocalVariables (1 );
1108
1161
capabilities .setCanGenerateNativeMethodBindEvents (1 );
1162
+ capabilities .setCanGenerateAllClassHookEvents (1 );
1109
1163
if (exptlClassLoaderSupport ) {
1110
1164
capabilities .setCanGetBytecodes (1 );
1111
1165
capabilities .setCanGetConstantPool (1 );
@@ -1125,6 +1179,9 @@ public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, TraceWr
1125
1179
1126
1180
BreakpointInterceptor .boundNativeMethods = new HashMap <>();
1127
1181
Support .check (jvmti .getFunctions ().SetEventNotificationMode ().invoke (jvmti , JvmtiEventMode .JVMTI_ENABLE , JVMTI_EVENT_NATIVE_METHOD_BIND , nullHandle ()));
1182
+
1183
+ callbacks .setClassFileLoadHook (onClassFileLoadHookLiteral .getFunctionPointer ());
1184
+ Support .check (jvmti .getFunctions ().SetEventNotificationMode ().invoke (jvmti , JvmtiEventMode .JVMTI_ENABLE , JVMTI_EVENT_CLASS_FILE_LOAD_HOOK , nullHandle ()));
1128
1185
}
1129
1186
1130
1187
public static void onVMInit (JvmtiEnv jvmti , JNIEnvironment jni ) {
@@ -1269,6 +1326,19 @@ private static void bindNativeBreakpoint(JNIEnvironment jni, NativeBreakpoint bp
1269
1326
}
1270
1327
}
1271
1328
1329
+ public static void reportExceptions () {
1330
+ if (!unsupportedExceptions .isEmpty ()) {
1331
+ System .err .println (unsupportedExceptions .size () + " unsupported features are detected " );
1332
+ StringBuilder errorMsg = new StringBuilder ();
1333
+ for (int i = 0 ; i < unsupportedExceptions .size (); i ++) {
1334
+ errorMsg .append (unsupportedExceptions .get (i )).append ("\n " );
1335
+ }
1336
+ throw new UnsupportedOperationException (errorMsg .toString ());
1337
+ } else {
1338
+ unsupportedExceptions = null ;
1339
+ }
1340
+ }
1341
+
1272
1342
public static void onUnload () {
1273
1343
installedBreakpoints = null ;
1274
1344
nativeBreakpoints = null ;
0 commit comments