From f1148f979f4662e33e681daad4444ce83ee15fa1 Mon Sep 17 00:00:00 2001 From: Matt D'Souza Date: Mon, 25 Aug 2025 15:10:39 -0400 Subject: [PATCH 1/3] Avoid using streams in runtime tracer code --- .../svm/configure/ConfigurationTypeDescriptor.java | 12 +++++++----- .../com/oracle/svm/core/metadata/MetadataTracer.java | 7 +++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationTypeDescriptor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationTypeDescriptor.java index a7a2ab1ff1f8..8dca1e151017 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationTypeDescriptor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationTypeDescriptor.java @@ -27,7 +27,6 @@ import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collection; -import java.util.stream.Stream; import com.oracle.svm.util.StringUtil; @@ -51,13 +50,16 @@ enum Kind { } static ConfigurationTypeDescriptor fromClass(Class clazz) { - Stream interfacesStream = Arrays.stream(clazz.getInterfaces()) - .map(Class::getTypeName); + Class[] interfaces = clazz.getInterfaces(); + String[] interfaceNames = new String[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + interfaceNames[i] = interfaces[i].getTypeName(); + } if (Proxy.isProxyClass(clazz)) { - return ProxyConfigurationTypeDescriptor.fromInterfaceReflectionNames(interfacesStream.toList()); + return ProxyConfigurationTypeDescriptor.fromInterfaceReflectionNames(Arrays.asList(interfaceNames)); } else if (LambdaUtils.isLambdaClass(clazz)) { String declaringClass = StringUtil.split(clazz.getTypeName(), LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)[0]; - return LambdaConfigurationTypeDescriptor.fromReflectionNames(declaringClass, interfacesStream.toList()); + return LambdaConfigurationTypeDescriptor.fromReflectionNames(declaringClass, Arrays.asList(interfaceNames)); } else { return NamedConfigurationTypeDescriptor.fromReflectionName(clazz.getTypeName()); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java index 0930d040b092..3a302cf336db 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java @@ -236,8 +236,11 @@ public void traceUnsafeAllocatedType(Class clazz) { * Marks the given proxy type as reachable from reflection. */ public void traceProxyType(Class[] interfaces) { - List interfaceNames = Arrays.stream(interfaces).map(Class::getTypeName).toList(); - ProxyConfigurationTypeDescriptor descriptor = new ProxyConfigurationTypeDescriptor(interfaceNames); + String[] interfaceNames = new String[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + interfaceNames[i] = interfaces[i].getTypeName(); + } + ProxyConfigurationTypeDescriptor descriptor = new ProxyConfigurationTypeDescriptor(Arrays.asList(interfaceNames)); traceReflectionTypeImpl(descriptor); } From 66da937789db93abbca998fdb6070736b6b60bb6 Mon Sep 17 00:00:00 2001 From: Matt D'Souza Date: Mon, 25 Aug 2025 15:58:49 -0400 Subject: [PATCH 2/3] Move DisableTracing helper classes to bottom of class; clear debugWriter --- .../svm/core/metadata/MetadataTracer.java | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java index 3a302cf336db..41c0bc771dce 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java @@ -139,6 +139,7 @@ private void shutdown() { if (debugWriter != null) { debugWriter.close(); + debugWriter = null; } } @@ -409,37 +410,6 @@ private void debugMethod(ConfigurationTypeDescriptor typeDescriptor, String meth debug("method registered for reflection", typeDescriptor + "." + methodName + internalSignature); } - /** - * Disables tracing on the current thread from instantiation until {@link #close}. - */ - public sealed interface DisableTracing extends AutoCloseable { - @Override - void close(); - } - - private final class DisableTracingImpl implements DisableTracing { - final String oldReason; - - private DisableTracingImpl(String reason) { - this.oldReason = disableTracingReason.get(); - disableTracingReason.set(reason); - } - - @Override - public void close() { - disableTracingReason.set(oldReason); - } - } - - private static final class DisableTracingNoOp implements DisableTracing { - private static final DisableTracingNoOp INSTANCE = new DisableTracingNoOp(); - - @Override - public void close() { - // do nothing - } - } - /** * Disables tracing on the current thread from instantiation until {@link DisableTracing#close}. * Should be used in a try-with-resources block. @@ -541,6 +511,37 @@ static RuntimeSupport.Hook checkImproperOptionUsageHook() { } }; } + + /** + * Disables tracing on the current thread from instantiation until {@link #close}. + */ + public sealed interface DisableTracing extends AutoCloseable { + @Override + void close(); + } + + private final class DisableTracingImpl implements DisableTracing { + final String oldReason; + + private DisableTracingImpl(String reason) { + this.oldReason = disableTracingReason.get(); + disableTracingReason.set(reason); + } + + @Override + public void close() { + disableTracingReason.set(oldReason); + } + } + + private static final class DisableTracingNoOp implements DisableTracing { + private static final DisableTracingNoOp INSTANCE = new DisableTracingNoOp(); + + @Override + public void close() { + // do nothing + } + } } record TraceOptions(Path path, boolean merge, Path debugLog) { From 3865b645288fee1d721f0fddc59c371c7752ae27 Mon Sep 17 00:00:00 2001 From: Matt D'Souza Date: Mon, 25 Aug 2025 15:56:10 -0400 Subject: [PATCH 3/3] Move unsafe allocation tracing to the fast path --- .../snippets/SubstrateAllocationSnippets.java | 17 ++++++++++++++++- .../svm/core/hub/ClassForNameSupport.java | 3 --- .../svm/core/metadata/MetadataTracer.java | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java index 40f0bfa8c694..3e35d95a5ec7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java @@ -36,6 +36,7 @@ import java.util.Arrays; import java.util.Map; +import com.oracle.svm.core.metadata.MetadataTracer; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.word.LocationIdentity; import org.graalvm.word.UnsignedWord; @@ -122,10 +123,12 @@ public class SubstrateAllocationSnippets extends AllocationSnippets { public static final LocationIdentity[] GC_LOCATIONS = new LocationIdentity[]{TLAB_START_IDENTITY, TLAB_TOP_IDENTITY, TLAB_END_IDENTITY, IdentityHashCodeSupport.IDENTITY_HASHCODE_SALT_LOCATION}; private static final SubstrateForeignCallDescriptor NEW_MULTI_ARRAY = SnippetRuntime.findForeignCall(SubstrateAllocationSnippets.class, "newMultiArrayStub", NO_SIDE_EFFECT); + private static final SubstrateForeignCallDescriptor TRACE_UNSAFE_ALLOCATION = SnippetRuntime.findForeignCall(SubstrateAllocationSnippets.class, "traceUnsafeAllocation", NO_SIDE_EFFECT); private static final SubstrateForeignCallDescriptor SLOW_PATH_HUB_OR_UNSAFE_INSTANTIATE_ERROR = SnippetRuntime.findForeignCall(SubstrateAllocationSnippets.class, "slowPathHubOrUnsafeInstantiationError", NO_SIDE_EFFECT); private static final SubstrateForeignCallDescriptor ARRAY_HUB_ERROR = SnippetRuntime.findForeignCall(SubstrateAllocationSnippets.class, "arrayHubErrorStub", NO_SIDE_EFFECT); - private static final SubstrateForeignCallDescriptor[] FOREIGN_CALLS = new SubstrateForeignCallDescriptor[]{NEW_MULTI_ARRAY, SLOW_PATH_HUB_OR_UNSAFE_INSTANTIATE_ERROR, ARRAY_HUB_ERROR}; + private static final SubstrateForeignCallDescriptor[] FOREIGN_CALLS = new SubstrateForeignCallDescriptor[]{NEW_MULTI_ARRAY, TRACE_UNSAFE_ALLOCATION, SLOW_PATH_HUB_OR_UNSAFE_INSTANTIATE_ERROR, + ARRAY_HUB_ERROR}; public void registerForeignCalls(SubstrateForeignCallsProvider foreignCalls) { foreignCalls.register(FOREIGN_CALLS); @@ -309,6 +312,9 @@ protected Object newmultiarray(DynamicHub hub, @ConstantParameter int rank, @Con public DynamicHub validateNewInstanceClass(DynamicHub hub) { if (probability(EXTREMELY_FAST_PATH_PROBABILITY, hub != null)) { DynamicHub nonNullHub = (DynamicHub) PiNode.piCastNonNull(hub, SnippetAnchorNode.anchor()); + if (probability(EXTREMELY_FAST_PATH_PROBABILITY, MetadataTracer.enabled())) { + traceUnsafeAllocationStub(TRACE_UNSAFE_ALLOCATION, DynamicHub.toClass(nonNullHub)); + } if (probability(EXTREMELY_FAST_PATH_PROBABILITY, nonNullHub.canUnsafeInstantiateAsInstanceFastPath())) { return nonNullHub; } @@ -344,6 +350,15 @@ private static Object newMultiArrayRecursion(DynamicHub hub, int rank, Word dime return result; } + @NodeIntrinsic(value = ForeignCallWithExceptionNode.class) + private static native DynamicHub traceUnsafeAllocationStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Class hub); + + /** Foreign call: {@link #TRACE_UNSAFE_ALLOCATION}. */ + @SubstrateForeignCallTarget(stubCallingConvention = true) + private static void traceUnsafeAllocation(DynamicHub hub) { + MetadataTracer.singleton().traceUnsafeAllocatedType(DynamicHub.toClass(hub)); + } + @NodeIntrinsic(value = ForeignCallWithExceptionNode.class) private static native DynamicHub slowPathHubOrUnsafeInstantiationErrorStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Class hub); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java index 014271f1d4ef..8160162bf045 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java @@ -510,9 +510,6 @@ public static Throwable getSavedException(String className) { */ public static boolean canUnsafeInstantiateAsInstance(DynamicHub hub) { Class clazz = DynamicHub.toClass(hub); - if (MetadataTracer.enabled()) { - MetadataTracer.singleton().traceUnsafeAllocatedType(clazz); - } RuntimeConditionSet conditionSet = null; for (var singleton : layeredSingletons()) { conditionSet = singleton.unsafeInstantiatedClasses.get(clazz); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java index 41c0bc771dce..da61f10e3fe8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java @@ -633,7 +633,7 @@ public List> getRequiredFeatures() { } @Override - public void beforeAnalysis(BeforeAnalysisAccess access) { + public void duringSetup(DuringSetupAccess access) { if (MetadataTracer.Options.MetadataTracingSupport.getValue()) { ImageSingletons.add(MetadataTracer.class, new MetadataTracer()); RuntimeSupport.getRuntimeSupport().addInitializationHook(MetadataTracer.initializeMetadataTracingHook());