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 6a0918d8e308..dd2ad14edccb 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 @@ -30,6 +30,7 @@ import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.EXTREMELY_FAST_PATH_PROBABILITY; import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.FAST_PATH_PROBABILITY; import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.LIKELY_PROBABILITY; +import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.SLOW_PATH_PROBABILITY; import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.probability; import static jdk.graal.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER; @@ -59,6 +60,7 @@ import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.identityhashcode.IdentityHashCodeSupport; import com.oracle.svm.core.meta.SharedType; +import com.oracle.svm.core.metadata.MetadataTracer; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils; import com.oracle.svm.core.snippets.SnippetRuntime; @@ -126,8 +128,11 @@ public class SubstrateAllocationSnippets extends AllocationSnippets { private static final SubstrateForeignCallDescriptor NEW_MULTI_ARRAY = SnippetRuntime.findForeignCall(SubstrateAllocationSnippets.class, "newMultiArrayStub", 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 TRACE_ARRAY_HUB = SnippetRuntime.findForeignCall(SubstrateAllocationSnippets.class, "traceArrayHubStub", 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, SLOW_PATH_HUB_OR_UNSAFE_INSTANTIATE_ERROR, TRACE_ARRAY_HUB, + ARRAY_HUB_ERROR}; public void registerForeignCalls(SubstrateForeignCallsProvider foreignCalls) { foreignCalls.register(FOREIGN_CALLS); @@ -365,6 +370,12 @@ private static DynamicHub getCheckedArrayHub(DynamicHub elementType) { if (probability(EXTREMELY_FAST_PATH_PROBABILITY, arrayHub != null)) { DynamicHub nonNullArrayHub = (DynamicHub) PiNode.piCastNonNull(arrayHub, SnippetAnchorNode.anchor()); if (probability(EXTREMELY_FAST_PATH_PROBABILITY, nonNullArrayHub.isInstantiated())) { + if (MetadataTracer.Options.MetadataTracingSupport.getValue()) { + Class clazz = DynamicHub.toClass(elementType); + if (!clazz.isPrimitive()) { + callTraceArrayHubStub(TRACE_ARRAY_HUB, clazz); + } + } return nonNullArrayHub; } } @@ -374,6 +385,18 @@ private static DynamicHub getCheckedArrayHub(DynamicHub elementType) { throw UnreachableNode.unreachable(); } + @NodeIntrinsic(value = ForeignCallNode.class) + private static native void callTraceArrayHubStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Class elementType); + + /** Foreign call: {@link #TRACE_ARRAY_HUB}. */ + @SubstrateForeignCallTarget(stubCallingConvention = true) + private static void traceArrayHubStub(DynamicHub elementType) { + assert MetadataTracer.Options.MetadataTracingSupport.getValue(); + if (probability(SLOW_PATH_PROBABILITY, MetadataTracer.singleton().enabled())) { + MetadataTracer.singleton().traceReflectionType(elementType.getName()); + } + } + @NodeIntrinsic(value = ForeignCallNode.class) private static native void callArrayHubErrorStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Class elementType); 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 ae5423f1cf7c..f89f19c4530a 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 @@ -36,6 +36,7 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.impl.ConfigurationCondition; +import com.oracle.svm.configure.config.ConfigurationType; import com.oracle.svm.core.configure.ConditionalRuntimeValue; import com.oracle.svm.core.configure.RuntimeConditionSet; import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; @@ -43,6 +44,7 @@ import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport; import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; +import com.oracle.svm.core.metadata.MetadataTracer; import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils; import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.VMError; @@ -231,6 +233,9 @@ private static Class forName(String className, ClassLoader classLoader, boole private Object forName0(String className, ClassLoader classLoader) { var conditional = knownClasses.get(className); + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && conditional != null && MetadataTracer.singleton().enabled()) { + MetadataTracer.singleton().traceReflectionType(className); + } Object result = conditional == null ? null : conditional.getValue(); if (result == NEGATIVE_QUERY || className.endsWith("[]")) { /* Querying array classes with their "TypeName[]" name always throws */ @@ -276,7 +281,16 @@ public static boolean canUnsafeInstantiateAsInstance(DynamicHub hub) { break; } } - return conditionSet != null && conditionSet.satisfied(); + if (conditionSet != null) { + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) { + ConfigurationType type = MetadataTracer.singleton().traceReflectionType(clazz.getName()); + if (type != null) { + type.setUnsafeAllocated(); + } + } + return conditionSet.satisfied(); + } + return false; } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index a4d9b0373cc5..8df7bd8935a0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.core.hub; +import static com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberAccessibility; +import static com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration; import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors; import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; import static com.oracle.svm.core.annotate.TargetElement.CONSTRUCTOR_NAME; @@ -86,6 +88,7 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CFunctionPointer; +import com.oracle.svm.configure.config.ConfigurationType; import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse; import com.oracle.svm.core.BuildPhaseProvider.CompileQueueFinished; import com.oracle.svm.core.NeverInline; @@ -116,6 +119,7 @@ import com.oracle.svm.core.jdk.ProtectionDomainSupport; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.meta.SharedType; +import com.oracle.svm.core.metadata.MetadataTracer; import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils; import com.oracle.svm.core.reflect.RuntimeMetadataDecoder; import com.oracle.svm.core.reflect.RuntimeMetadataDecoder.ConstructorDescriptor; @@ -143,6 +147,7 @@ import jdk.internal.reflect.FieldAccessor; import jdk.internal.reflect.Reflection; import jdk.internal.reflect.ReflectionFactory; +import jdk.vm.ci.meta.MetaUtil; import jdk.vm.ci.meta.ResolvedJavaType; import sun.reflect.annotation.AnnotationType; import sun.reflect.generics.factory.GenericsFactory; @@ -694,6 +699,9 @@ private ReflectionMetadata reflectionMetadata() { } private void checkClassFlag(int mask, String methodName) { + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) { + traceClassFlagQuery(mask); + } if (throwMissingRegistrationErrors() && !(isClassFlagSet(mask) && getConditions().satisfied())) { MissingReflectionRegistrationUtils.forBulkQuery(DynamicHub.toClass(this), methodName); } @@ -703,6 +711,28 @@ private boolean isClassFlagSet(int mask) { return (reflectionMetadata() != null && (reflectionMetadata().classFlags & mask) != 0); } + private void traceClassFlagQuery(int mask) { + ConfigurationType type = MetadataTracer.singleton().traceReflectionType(getName()); + if (type == null) { + return; + } + switch (mask) { + case ALL_FIELDS_FLAG -> type.setAllPublicFields(ConfigurationMemberAccessibility.ACCESSED); + case ALL_DECLARED_FIELDS_FLAG -> type.setAllDeclaredFields(ConfigurationMemberAccessibility.ACCESSED); + case ALL_METHODS_FLAG -> type.setAllPublicMethods(ConfigurationMemberAccessibility.QUERIED); + case ALL_DECLARED_METHODS_FLAG -> type.setAllDeclaredMethods(ConfigurationMemberAccessibility.QUERIED); + case ALL_CONSTRUCTORS_FLAG -> type.setAllPublicConstructors(ConfigurationMemberAccessibility.QUERIED); + case ALL_DECLARED_CONSTRUCTORS_FLAG -> type.setAllDeclaredConstructors(ConfigurationMemberAccessibility.QUERIED); + case ALL_CLASSES_FLAG -> type.setAllPublicClasses(); + case ALL_DECLARED_CLASSES_FLAG -> type.setAllDeclaredClasses(); + case ALL_RECORD_COMPONENTS_FLAG -> type.setAllRecordComponents(); + case ALL_PERMITTED_SUBCLASSES_FLAG -> type.setAllPermittedSubclasses(); + case ALL_NEST_MEMBERS_FLAG -> type.setAllNestMembers(); + case ALL_SIGNERS_FLAG -> type.setAllSigners(); + default -> throw VMError.shouldNotReachHere("unknown class flag " + mask); + } + } + /** Executed at runtime. */ private static Object initEnumConstantsAtRuntime(Method values) { try { @@ -1261,6 +1291,16 @@ private void checkField(String fieldName, Field field, boolean publicOnly) throw */ throw new NoSuchFieldException(fieldName); } else { + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) { + ConfigurationMemberDeclaration declaration = publicOnly ? ConfigurationMemberDeclaration.PRESENT : ConfigurationMemberDeclaration.DECLARED; + // register declaring type and field + ConfigurationType declaringType = MetadataTracer.singleton().traceReflectionType(field.getDeclaringClass().getName()); + if (declaringType != null) { + declaringType.addField(fieldName, declaration, false); + } + // register receiver type + MetadataTracer.singleton().traceReflectionType(getName()); + } RuntimeMetadataDecoder decoder = ImageSingletons.lookup(RuntimeMetadataDecoder.class); int fieldModifiers = field.getModifiers(); boolean negative = decoder.isNegative(fieldModifiers); @@ -1328,6 +1368,16 @@ private boolean checkExecutableExists(String methodName, Class[] parameterTyp int methodModifiers = method.getModifiers(); boolean negative = decoder.isNegative(methodModifiers); boolean hiding = decoder.isHiding(methodModifiers); + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) { + ConfigurationMemberDeclaration declaration = publicOnly ? ConfigurationMemberDeclaration.PRESENT : ConfigurationMemberDeclaration.DECLARED; + // register declaring type and method + ConfigurationType declaringType = MetadataTracer.singleton().traceReflectionType(method.getDeclaringClass().getName()); + if (declaringType != null) { + declaringType.addMethod(methodName, toInternalSignature(parameterTypes), declaration); + } + // register receiver type + MetadataTracer.singleton().traceReflectionType(getName()); + } if (throwMissingErrors && hiding) { MissingReflectionRegistrationUtils.forMethod(clazz, methodName, parameterTypes); } @@ -1335,6 +1385,16 @@ private boolean checkExecutableExists(String methodName, Class[] parameterTyp } } + private static String toInternalSignature(Class[] classes) { + StringBuilder sb = new StringBuilder("("); + if (classes != null) { + for (Class clazz : classes) { + sb.append(MetaUtil.toInternalName(clazz.getName())); + } + } + return sb.append(')').toString(); + } + private boolean allElementsRegistered(boolean publicOnly, int allDeclaredElementsFlag, int allPublicElementsFlag) { return isClassFlagSet(allDeclaredElementsFlag) || (publicOnly && isClassFlagSet(allPublicElementsFlag)); } @@ -1824,6 +1884,8 @@ public DynamicHub arrayType() { } if (companion.arrayHub == null) { MissingReflectionRegistrationUtils.forClass(getTypeName() + "[]"); + } else if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled() && !isPrimitive()) { + MetadataTracer.singleton().traceReflectionType(companion.arrayHub.getTypeName()); } return companion.arrayHub; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaIOSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaIOSubstitutions.java index 71a52c7ccff0..f9956c1f5ce6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaIOSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaIOSubstitutions.java @@ -35,8 +35,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; -import jdk.graal.compiler.java.LambdaUtils; - import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; @@ -45,8 +43,11 @@ import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.fieldvaluetransformer.NewInstanceFieldValueTransformer; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.metadata.MetadataTracer; import com.oracle.svm.core.reflect.serialize.MissingSerializationRegistrationUtils; +import jdk.graal.compiler.java.LambdaUtils; + @TargetClass(java.io.FileDescriptor.class) final class Target_java_io_FileDescriptor { @@ -68,8 +69,8 @@ static ObjectStreamClass lookup(Class cl, boolean all) { return null; } - if (Serializable.class.isAssignableFrom(cl)) { - if (!cl.isArray() && !DynamicHub.fromClass(cl).isRegisteredForSerialization()) { + if (Serializable.class.isAssignableFrom(cl) && !cl.isArray()) { + if (!DynamicHub.fromClass(cl).isRegisteredForSerialization()) { boolean isLambda = cl.getTypeName().contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING); boolean isProxy = Proxy.isProxyClass(cl); if (isProxy || isLambda) { @@ -87,6 +88,9 @@ static ObjectStreamClass lookup(Class cl, boolean all) { MissingSerializationRegistrationUtils.missingSerializationRegistration(cl, "type " + cl.getTypeName()); } } + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) { + MetadataTracer.singleton().traceSerializationType(cl.getName()); + } } return Target_java_io_ObjectStreamClass_Caches.localDescs0.get(cl); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java index 3c1c5e164616..454412c9bc2b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java @@ -71,6 +71,7 @@ import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport; import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; +import com.oracle.svm.core.metadata.MetadataTracer; import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.GlobUtils; @@ -385,6 +386,7 @@ public static ResourceStorageEntryBase getAtRuntime(Module module, String resour return null; } } + traceResourceAccess(resourceName, moduleName); if (!entry.getConditions().satisfied()) { return missingMetadata(resourceName, throwOnMissing); } @@ -414,6 +416,12 @@ public static ResourceStorageEntryBase getAtRuntime(Module module, String resour return unconditionalEntry; } + private static void traceResourceAccess(String resourceName, String moduleName) { + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) { + MetadataTracer.singleton().traceResource(resourceName, moduleName); + } + } + private static ConditionalRuntimeValue getEntry(Module module, String canonicalResourceName) { for (var r : layeredSingletons()) { ConditionalRuntimeValue entry = r.resources.get(createStorageKey(module, canonicalResourceName)); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index 36d8bb4e6e21..a70f90a7e80c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -57,6 +57,7 @@ import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.configure.RuntimeConditionSet; import com.oracle.svm.core.jdk.Resources; +import com.oracle.svm.core.metadata.MetadataTracer; import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ReflectionUtil; @@ -293,6 +294,12 @@ public boolean isRegisteredBundleLookup(String baseName, Locale locale, Object c /* Those cases will throw a NullPointerException before any lookup */ return true; } - return registeredBundles.containsKey(baseName) && registeredBundles.get(baseName).satisfied(); + if (registeredBundles.containsKey(baseName)) { + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) { + MetadataTracer.singleton().traceResourceBundle(baseName, locale); + } + return registeredBundles.get(baseName).satisfied(); + } + return false; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethodDescriptor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethodDescriptor.java index 2bb1fe5a58a7..3daeaeb64bab 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethodDescriptor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethodDescriptor.java @@ -98,10 +98,19 @@ public boolean isClassInitializer() { return WRAPPED_CSTRING_EQUIVALENCE.equals(name, INITIALIZER_NAME); } + /** + * Returns the method name as a String. Can be used if the descriptor is known to be a String + * (i.e., it does not come from a JNI call); otherwise, use {@link #getNameConvertToString()}. + */ public String getName() { return (String) name; } + /** + * Returns the method signature as a String. Can be used if the descriptor is known to be a + * String (i.e., it does not come from a JNI call); otherwise, use + * {@link #getSignatureConvertToString()}. + */ public String getSignature() { return (String) signature; } @@ -113,6 +122,13 @@ public String getNameConvertToString() { return name.toString(); } + /** + * Performs a potentially costly conversion to string, only for slow paths. + */ + public String getSignatureConvertToString() { + return signature.toString(); + } + public String getSignatureWithoutReturnType() { String signatureString = signature.toString(); int parametersEnd = signatureString.lastIndexOf(')'); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIReflectionDictionary.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIReflectionDictionary.java index 1600a5f7fdda..2876cf5e2050 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIReflectionDictionary.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIReflectionDictionary.java @@ -41,6 +41,8 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; +import com.oracle.svm.configure.config.ConfigurationMemberInfo; +import com.oracle.svm.configure.config.ConfigurationType; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.heap.Heap; @@ -52,6 +54,7 @@ import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; import com.oracle.svm.core.log.Log; +import com.oracle.svm.core.metadata.MetadataTracer; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.Utf8.WrappedAsciiCString; @@ -185,6 +188,9 @@ public Iterable getClasses() { public static Class getClassObjectByName(CharSequence name) { for (var dictionary : layeredSingletons()) { JNIAccessibleClass clazz = dictionary.classesByName.get(name); + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && clazz != null && MetadataTracer.singleton().enabled()) { + MetadataTracer.singleton().traceJNIType(convertFindClassNameToBinaryName(name.toString())); + } clazz = checkClass(clazz, name); if (clazz != null) { return clazz.getClassObject(); @@ -194,6 +200,16 @@ public static Class getClassObjectByName(CharSequence name) { return null; } + /** + * FindClass's argument is either an internal class name (e.g., {@code pkg/sub/Class}) or an + * array type signature (e.g., {@code [Lpkg/sub/Class;}). Converts the argument to a regular + * binary name (e.g., {@code pkg.sub.Class}. + */ + private static String convertFindClassNameToBinaryName(String name) { + String internalName = (name.charAt(0) != '[') ? ('L' + name + ';') : name; + return MetaUtil.internalNameToJava(internalName, true, true); + } + private static JNIAccessibleClass checkClass(JNIAccessibleClass clazz, CharSequence name) { if (throwMissingRegistrationErrors() && clazz == null) { MissingJNIRegistrationUtils.forClass(name.toString()); @@ -277,6 +293,12 @@ private static JNIAccessibleMethod getDeclaredMethod(Class classObject, JNIAc foundClass = true; JNIAccessibleMethod method = clazz.getMethod(descriptor); if (method != null) { + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) { + ConfigurationType clazzType = MetadataTracer.singleton().traceJNIType(classObject.getName()); + if (clazzType != null) { + clazzType.addMethod(descriptor.getNameConvertToString(), descriptor.getSignatureConvertToString(), ConfigurationMemberInfo.ConfigurationMemberDeclaration.DECLARED); + } + } return method; } } @@ -332,6 +354,12 @@ private static JNIAccessibleField getDeclaredField(Class classObject, CharSeq foundClass = true; JNIAccessibleField field = clazz.getField(name); if (field != null && (field.isStatic() == isStatic || field.isNegative())) { + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) { + ConfigurationType clazzType = MetadataTracer.singleton().traceJNIType(classObject.getName()); + if (clazzType != null) { + clazzType.addField(name.toString(), ConfigurationMemberInfo.ConfigurationMemberDeclaration.DECLARED, false); + } + } return field; } } 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 60a138fe0966..98506173384f 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 @@ -29,11 +29,14 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.Locale; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationSet; +import com.oracle.svm.configure.config.ConfigurationType; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.jdk.RuntimeSupport; @@ -63,7 +66,7 @@ public static class Options { public static final RuntimeOptionKey RecordMetadata = new RuntimeOptionKey<>(""); } - private ConfigurationSet config; + private volatile ConfigurationSet config; private Path recordMetadataPath; @@ -72,6 +75,77 @@ public static MetadataTracer singleton() { return ImageSingletons.lookup(MetadataTracer.class); } + /** + * Returns whether tracing is enabled at run time (using {@code -XX:RecordMetadata=path}). + */ + public boolean enabled() { + VMError.guarantee(Options.MetadataTracingSupport.getValue()); + return recordMetadataPath != null; + } + + /** + * Marks the given type as reachable from reflection. + * + * @return the corresponding {@link ConfigurationType} or {@code null} if tracing is not active + * (e.g., during shutdown). + */ + public ConfigurationType traceReflectionType(String className) { + assert enabled(); + ConfigurationSet configurationSet = config; + if (configurationSet != null) { + return configurationSet.getReflectionConfiguration().getOrCreateType(UnresolvedConfigurationCondition.alwaysTrue(), className); + } + return null; + } + + /** + * Marks the given type as reachable from JNI. + * + * @return the corresponding {@link ConfigurationType} or {@code null} if tracing is not active + * (e.g., during shutdown). + */ + public ConfigurationType traceJNIType(String className) { + assert enabled(); + ConfigurationSet configurationSet = config; + if (configurationSet != null) { + return configurationSet.getJniConfiguration().getOrCreateType(UnresolvedConfigurationCondition.alwaysTrue(), className); + } + return null; + } + + /** + * Marks the given resource within the given (optional) module as reachable. + */ + public void traceResource(String resourceName, String moduleName) { + assert enabled(); + ConfigurationSet configurationSet = config; + if (configurationSet != null) { + configurationSet.getResourceConfiguration().addGlobPattern(UnresolvedConfigurationCondition.alwaysTrue(), resourceName, moduleName); + } + } + + /** + * Marks the given resource bundle within the given locale as reachable. + */ + public void traceResourceBundle(String baseName, Locale locale) { + assert enabled(); + ConfigurationSet configurationSet = config; + if (configurationSet != null) { + configurationSet.getResourceConfiguration().addBundle(UnresolvedConfigurationCondition.alwaysTrue(), baseName, List.of(locale)); + } + } + + /** + * Marks the given type as serializable. + */ + public void traceSerializationType(String className) { + assert enabled(); + ConfigurationSet configurationSet = config; + if (configurationSet != null) { + configurationSet.getReflectionConfiguration().getOrCreateType(UnresolvedConfigurationCondition.alwaysTrue(), className).setSerializable(); + } + } + private static void initialize() { assert Options.MetadataTracingSupport.getValue(); MetadataTracer singleton = MetadataTracer.singleton(); @@ -93,6 +167,7 @@ private static void shutdown() { assert Options.MetadataTracingSupport.getValue(); MetadataTracer singleton = MetadataTracer.singleton(); ConfigurationSet config = singleton.config; + singleton.config = null; // clear config so that shutdown events are not traced. if (config != null) { try { config.writeConfiguration(configFile -> singleton.recordMetadataPath.resolve(configFile.getFileName())); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java index d20b8382277a..47670798b1a4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java @@ -41,6 +41,7 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.configure.RuntimeConditionSet; +import com.oracle.svm.core.metadata.MetadataTracer; import com.oracle.svm.core.reflect.SubstrateConstructorAccessor; import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.VMError; @@ -228,6 +229,9 @@ public Object getSerializationConstructorAccessor(Class rawDeclaringClass, Cl Object constructorAccessor = constructorAccessors.get(new SerializationLookupKey(declaringClass, targetConstructorClass)); if (constructorAccessor != null) { + if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) { + MetadataTracer.singleton().traceSerializationType(declaringClass.getName()); + } return constructorAccessor; } else { String targetConstructorClassName = targetConstructorClass.getName();