Skip to content

Commit 36f6764

Browse files
committed
[GR-60208] [GR-52447] Trace FFM API calls in native image agent.
PullRequest: graal/20566
2 parents 6692bd7 + 309e6d0 commit 36f6764

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) Adds the Tracing Agent support for applications using the Foreign Function & Memory (FFM) API. The agent generates FFM configuration in _foreign-config.json_. Additionally, support for FFM configurations has been added to the `native-image-configure` tool.
2627
* (GR-47881) Remove the total number of loaded types, fields, and methods from the build output, deprecated these metrics in the build output schema, and removed already deprecated build output metrics.
2728

2829
## GraalVM for JDK 24 (Internal Version 24.2.0)

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)