Skip to content

Commit 06a27f2

Browse files
committed
Trace FFM API calls in native image agent
1 parent e176dc4 commit 06a27f2

File tree

18 files changed

+1576
-139
lines changed

18 files changed

+1576
-139
lines changed

docs/reference-manual/native-image/assets/foreign-config-schema-v0.1.0.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"title": "Specifies if a call state should be captured. Which states to capture is determined at run time. See also: 'java.lang.foreign.Linker.Option.captureCallState'"
2929
},
3030
"critical": {
31-
"type": ["boolean", "object"],
31+
"type": "object",
3232
"title": "see 'java.lang.foreign.Linker.Option.critical'",
3333
"properties": {
3434
"allowHeapAccess": {

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This changelog summarizes major changes to GraalVM Native Image.
2323
* (GR-53985) Add experimental option `ClassForNameRespectsClassLoader` that makes `Class.forName(...)` respect the class loader hierarchy.
2424
* (GR-59869) Implemented initial optimization of Java Vector API (JEP 338) operations in native images. See the compiler changelog for more details.
2525
* (GR-63268) Reflection and JNI queries do not require metadata entries to throw the expected JDK exception when querying a class that doesn't exist under `--exact-reachability-metadata` if the query cannot possibly be a valid class name
26+
* (GR-60208) Add agent support for FFM API configuration.
2627

2728
## GraalVM for JDK 24 (Internal Version 24.2.0)
2829
* (GR-59717) Added `DuringSetupAccess.registerObjectReachabilityHandler` to allow registering a callback that is executed when an object of a specified type is marked as reachable during heap scanning.

substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ private static void traceSerializeBreakpoint(JNIEnvironment env, String function
185185
traceBreakpoint(env, "serialization", nullHandle(), nullHandle(), nullHandle(), function, result, stackTrace, args);
186186
}
187187

188+
private static void traceForeignBreakpoint(JNIEnvironment env, String function, Object result, JNIMethodId[] stackTrace, Object... args) {
189+
traceBreakpoint(env, "foreign", nullHandle(), nullHandle(), nullHandle(), function, result, stackTrace, args);
190+
}
191+
188192
private static void traceBreakpoint(JNIEnvironment env, String context, JNIObjectHandle clazz, JNIObjectHandle declaringClass, JNIObjectHandle callerClass, String function, Object result,
189193
JNIMethodId[] stackTrace, Object[] args) {
190194
if (tracer != null) {
@@ -350,6 +354,54 @@ private static boolean handleGetField(JNIEnvironment jni, JNIObjectHandle thread
350354
return true;
351355
}
352356

357+
private static boolean downcallHandle0(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
358+
JNIObjectHandle receiver = getReceiver(thread);
359+
JNIObjectHandle function = getObjectArgument(thread, 1);
360+
JNIObjectHandle options = getObjectArgument(thread, 2);
361+
362+
JNIObjectHandle result = Support.callObjectMethodLL(jni, receiver, bp.method, function, options);
363+
boolean isValidResult = !clearException(jni) && nullHandle().notEqual(result);
364+
365+
String returnLayoutString = Tracer.UNKNOWN_VALUE;
366+
Object argumentLayoutStrings = Tracer.UNKNOWN_VALUE;
367+
Object optionsStrings = Tracer.UNKNOWN_VALUE;
368+
if (isValidResult) {
369+
NativeImageAgentJNIHandleSet handles = agent.handles();
370+
returnLayoutString = ForeignUtil.getReturnLayoutString(jni, handles, function);
371+
argumentLayoutStrings = ForeignUtil.getArgumentLayoutStrings(jni, handles, function);
372+
optionsStrings = ForeignUtil.getOptionsStrings(jni, handles, options);
373+
}
374+
375+
traceForeignBreakpoint(jni, bp.specification.methodName, isValidResult, state.getFullStackTraceOrNull(), returnLayoutString, argumentLayoutStrings, optionsStrings);
376+
return true;
377+
}
378+
379+
private static boolean upcallStub(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
380+
JNIObjectHandle receiver = getReceiver(thread);
381+
JNIObjectHandle target = getObjectArgument(thread, 1);
382+
JNIObjectHandle function = getObjectArgument(thread, 2);
383+
JNIObjectHandle arena = getObjectArgument(thread, 3);
384+
JNIObjectHandle options = getObjectArgument(thread, 4);
385+
386+
JNIObjectHandle result = Support.callObjectMethodLLLL(jni, receiver, bp.method, target, function, arena, options);
387+
boolean isValidResult = !clearException(jni) && nullHandle().notEqual(result);
388+
389+
String returnLayoutString = Tracer.UNKNOWN_VALUE;
390+
Object argumentLayoutStrings = Tracer.UNKNOWN_VALUE;
391+
Object optionsStrings = Tracer.UNKNOWN_VALUE;
392+
Object targetString = Tracer.UNKNOWN_VALUE;
393+
if (isValidResult) {
394+
NativeImageAgentJNIHandleSet handles = agent.handles();
395+
returnLayoutString = ForeignUtil.getReturnLayoutString(jni, handles, function);
396+
argumentLayoutStrings = ForeignUtil.getArgumentLayoutStrings(jni, handles, function);
397+
optionsStrings = ForeignUtil.getOptionsStrings(jni, handles, options);
398+
targetString = ForeignUtil.getTargetString(jni, handles, target);
399+
}
400+
401+
traceForeignBreakpoint(jni, bp.specification.methodName, isValidResult, state.getFullStackTraceOrNull(), returnLayoutString, argumentLayoutStrings, optionsStrings, targetString);
402+
return true;
403+
}
404+
353405
private static final CEntryPointLiteral<AllocateInstanceFunctionPointer> nativeAllocateInstance = CEntryPointLiteral.create(
354406
BreakpointInterceptor.class, "nativeAllocateInstance", JNIEnvironment.class, JNIObjectHandle.class, JNIObjectHandle.class);
355407

@@ -1713,7 +1765,15 @@ private interface BreakpointHandler {
17131765
optionalBrk("java/lang/Class", "getNestMembers", "()[Ljava/lang/Class;",
17141766
BreakpointInterceptor::getNestMembers),
17151767
optionalBrk("java/lang/Class", "getSigners", "()[Ljava/lang/Object;",
1716-
BreakpointInterceptor::getSigners)
1768+
BreakpointInterceptor::getSigners),
1769+
1770+
/* FFM API was introduced in Java 22 */
1771+
brk("jdk/internal/foreign/abi/AbstractLinker", "downcallHandle0",
1772+
"(Ljava/lang/foreign/FunctionDescriptor;[Ljava/lang/foreign/Linker$Option;)Ljava/lang/invoke/MethodHandle;",
1773+
BreakpointInterceptor::downcallHandle0),
1774+
brk("jdk/internal/foreign/abi/AbstractLinker", "upcallStub",
1775+
"(Ljava/lang/invoke/MethodHandle;Ljava/lang/foreign/FunctionDescriptor;Ljava/lang/foreign/Arena;[Ljava/lang/foreign/Linker$Option;)Ljava/lang/foreign/MemorySegment;",
1776+
BreakpointInterceptor::upcallStub)
17171777
};
17181778

17191779
private static boolean allocateInstance(JNIEnvironment jni, JNIObjectHandle thread, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state) {

0 commit comments

Comments
 (0)