Skip to content

[GR-62047] Trace all dynamic accesses from native image. #11081

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.graalvm.nativeimage.impl.ConfigurationCondition;

import com.oracle.svm.configure.ClassNameSupport;
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;
Expand All @@ -47,6 +48,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.option.HostedOptionKey;
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
import com.oracle.svm.core.util.ImageHeapMap;
Expand Down Expand Up @@ -329,17 +331,22 @@ private static Class<?> forName(String className, ClassLoader classLoader, boole
}

private Object forName0(String className, ClassLoader classLoader) {
var conditional = knownClasses.get(className);
Object result = conditional == null ? null : conditional.getValue();
if (className.endsWith("[]")) {
/* Querying array classes with their "TypeName[]" name always throws */
result = NEGATIVE_QUERY;
return new ClassNotFoundException(className);
}
var conditional = knownClasses.get(className);
Object result = conditional == null ? null : conditional.getValue();
if (result == null) {
result = PredefinedClassesSupport.getLoadedForNameOrNull(className, classLoader);
}
if (result == null && !ClassNameSupport.isValidReflectionName(className)) {
result = NEGATIVE_QUERY;
/* Invalid class names always throw, no need for reflection data */
return new ClassNotFoundException(className);
}
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
// NB: the early returns above ensure we do not trace calls with bad type args.
MetadataTracer.singleton().traceReflectionType(className);
}
return result == NEGATIVE_QUERY ? new ClassNotFoundException(className) : result;
}
Expand Down Expand Up @@ -433,7 +440,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -84,19 +86,21 @@
import java.util.function.BiFunction;
import java.util.function.IntFunction;

import com.oracle.svm.core.TrackDynamicAccessEnabled;
import org.graalvm.nativeimage.AnnotationAccess;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.word.WordBase;

import com.oracle.svm.configure.config.ConfigurationType;
import com.oracle.svm.configure.config.SignatureUtil;
import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse;
import com.oracle.svm.core.BuildPhaseProvider.CompileQueueFinished;
import com.oracle.svm.core.NeverInline;
import com.oracle.svm.core.NeverInlineTrivial;
import com.oracle.svm.core.RuntimeAssertionsSupport;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.TrackDynamicAccessEnabled;
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.Delete;
Expand All @@ -121,6 +125,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;
Expand Down Expand Up @@ -704,6 +709,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);
}
Expand All @@ -726,6 +734,30 @@ private static boolean isClassFlagSet(int mask, ReflectionMetadata reflectionMet
return reflectionMetadata != null && (reflectionMetadata.classFlags & mask) != 0;
}

private void traceClassFlagQuery(int mask) {
ConfigurationType type = MetadataTracer.singleton().traceReflectionType(getName());
if (type == null) {
return;
}
// TODO (GR-64765): We over-approximate member accessibility here because we don't trace
// accesses. Once we trace accesses, it will suffice to register the class for reflection.
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.ACCESSED);
case ALL_DECLARED_METHODS_FLAG -> type.setAllDeclaredMethods(ConfigurationMemberAccessibility.ACCESSED);
case ALL_CONSTRUCTORS_FLAG -> type.setAllPublicConstructors(ConfigurationMemberAccessibility.ACCESSED);
case ALL_DECLARED_CONSTRUCTORS_FLAG -> type.setAllDeclaredConstructors(ConfigurationMemberAccessibility.ACCESSED);
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 {
Expand Down Expand Up @@ -1282,6 +1314,11 @@ public Field getField(String fieldName) throws NoSuchFieldException, SecurityExc
private void checkField(String fieldName, Field field, boolean publicOnly) throws NoSuchFieldException {
boolean throwMissingErrors = throwMissingRegistrationErrors();
Class<?> clazz = DynamicHub.toClass(this);

if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
traceFieldLookup(fieldName, field, publicOnly);
}

if (field == null) {
if (throwMissingErrors && !allElementsRegistered(publicOnly, ALL_DECLARED_FIELDS_FLAG, ALL_FIELDS_FLAG)) {
MissingReflectionRegistrationUtils.forField(clazz, fieldName);
Expand All @@ -1305,6 +1342,25 @@ private void checkField(String fieldName, Field field, boolean publicOnly) throw
}
}

private void traceFieldLookup(String fieldName, Field field, boolean publicOnly) {
ConfigurationMemberDeclaration declaration = publicOnly ? ConfigurationMemberDeclaration.PRESENT : ConfigurationMemberDeclaration.DECLARED;
if (field != null) {
// 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());
} else {
// register receiver type and negative field query
ConfigurationType receiverType = MetadataTracer.singleton().traceReflectionType(getName());
if (receiverType != null) {
receiverType.addField(fieldName, declaration, false);
}
}
}

@Substitute
private Method getMethod(String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
Objects.requireNonNull(methodName);
Expand Down Expand Up @@ -1340,6 +1396,11 @@ private void checkConstructor(Class<?>[] parameterTypes, Constructor<?> construc
private boolean checkExecutableExists(String methodName, Class<?>[] parameterTypes, Executable method, boolean publicOnly) {
boolean throwMissingErrors = throwMissingRegistrationErrors();
Class<?> clazz = DynamicHub.toClass(this);

if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
traceMethodLookup(methodName, parameterTypes, method, publicOnly);
}

if (method == null) {
boolean isConstructor = methodName.equals(CONSTRUCTOR_NAME);
int allDeclaredFlag = isConstructor ? ALL_DECLARED_CONSTRUCTORS_FLAG : ALL_DECLARED_METHODS_FLAG;
Expand All @@ -1366,6 +1427,38 @@ private boolean checkExecutableExists(String methodName, Class<?>[] parameterTyp
}
}

private void traceMethodLookup(String methodName, Class<?>[] parameterTypes, Executable method, boolean publicOnly) {
ConfigurationMemberDeclaration declaration = publicOnly ? ConfigurationMemberDeclaration.PRESENT : ConfigurationMemberDeclaration.DECLARED;
if (method != null) {
// 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());
} else {
// register receiver type and negative method query
ConfigurationType receiverType = MetadataTracer.singleton().traceReflectionType(getName());
if (receiverType != null) {
receiverType.addMethod(methodName, toInternalSignature(parameterTypes), declaration, ConfigurationMemberAccessibility.QUERIED);
}
}
}

private static String toInternalSignature(Class<?>[] classes) {
List<String> names;
if (classes == null) {
names = List.of();
} else {
names = new ArrayList<>(classes.length);
for (Class<?> aClass : classes) {
names.add(aClass.getName());
}
}
return SignatureUtil.toInternalSignature(names);
}

private boolean allElementsRegistered(boolean publicOnly, int allDeclaredElementsFlag, int allPublicElementsFlag) {
return isClassFlagSet(allDeclaredElementsFlag) || (publicOnly && isClassFlagSet(allPublicElementsFlag));
}
Expand Down Expand Up @@ -1882,6 +1975,8 @@ public DynamicHub arrayType() {
}
if (companion.arrayHub == null) {
MissingReflectionRegistrationUtils.forClass(getTypeName() + "[]");
} else if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
MetadataTracer.singleton().traceReflectionType(companion.arrayHub.getTypeName());
}
return companion.arrayHub;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {

Expand All @@ -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) {
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.hub.LayoutEncoding;
import com.oracle.svm.core.metadata.MetadataTracer;
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
import com.oracle.svm.core.snippets.KnownIntrinsics;

Expand Down Expand Up @@ -386,6 +387,9 @@ private static void set(Object a, int index, Object value) {
@Substitute
private static Object newArray(Class<?> componentType, int length)
throws NegativeArraySizeException {
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
MetadataTracer.singleton().traceReflectionType(componentType.arrayType().getName());
}
return KnownIntrinsics.unvalidatedNewArray(componentType, length);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -385,6 +386,9 @@ public static ResourceStorageEntryBase getAtRuntime(Module module, String resour
return null;
}
}
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
MetadataTracer.singleton().traceResource(resourceName, moduleName);
}
if (!entry.getConditions().satisfied()) {
return missingMetadata(resourceName, throwOnMissing);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
return registeredBundles.get(baseName).satisfied();
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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(')');
Expand Down
Loading