Skip to content

Commit 76f0980

Browse files
committed
[GR-62047] Trace all dynamic accesses from native image.
PullRequest: graal/20494
2 parents a9286ad + 1c3e155 commit 76f0980

File tree

11 files changed

+413
-34
lines changed

11 files changed

+413
-34
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.graalvm.nativeimage.impl.ConfigurationCondition;
4242

4343
import com.oracle.svm.configure.ClassNameSupport;
44+
import com.oracle.svm.configure.config.ConfigurationType;
4445
import com.oracle.svm.core.configure.ConditionalRuntimeValue;
4546
import com.oracle.svm.core.configure.RuntimeConditionSet;
4647
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
@@ -49,6 +50,7 @@
4950
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
5051
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
5152
import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
53+
import com.oracle.svm.core.metadata.MetadataTracer;
5254
import com.oracle.svm.core.option.HostedOptionKey;
5355
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
5456
import com.oracle.svm.core.util.ImageHeapMap;
@@ -341,17 +343,22 @@ private static Class<?> forName(String className, ClassLoader classLoader, boole
341343
}
342344

343345
private Object forName0(String className, ClassLoader classLoader) {
344-
var conditional = knownClasses.get(className);
345-
Object result = conditional == null ? null : conditional.getValue();
346346
if (className.endsWith("[]")) {
347347
/* Querying array classes with their "TypeName[]" name always throws */
348-
result = NEGATIVE_QUERY;
348+
return new ClassNotFoundException(className);
349349
}
350+
var conditional = knownClasses.get(className);
351+
Object result = conditional == null ? null : conditional.getValue();
350352
if (result == null) {
351353
result = PredefinedClassesSupport.getLoadedForNameOrNull(className, classLoader);
352354
}
353355
if (result == null && !ClassNameSupport.isValidReflectionName(className)) {
354-
result = NEGATIVE_QUERY;
356+
/* Invalid class names always throw, no need for reflection data */
357+
return new ClassNotFoundException(className);
358+
}
359+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
360+
// NB: the early returns above ensure we do not trace calls with bad type args.
361+
MetadataTracer.singleton().traceReflectionType(className);
355362
}
356363
return result == NEGATIVE_QUERY ? new ClassNotFoundException(className) : result;
357364
}
@@ -445,7 +452,16 @@ public static boolean canUnsafeInstantiateAsInstance(DynamicHub hub) {
445452
break;
446453
}
447454
}
448-
return conditionSet != null && conditionSet.satisfied();
455+
if (conditionSet != null) {
456+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
457+
ConfigurationType type = MetadataTracer.singleton().traceReflectionType(clazz.getName());
458+
if (type != null) {
459+
type.setUnsafeAllocated();
460+
}
461+
}
462+
return conditionSet.satisfied();
463+
}
464+
return false;
449465
}
450466

451467
@Override

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.core.hub;
2626

27+
import static com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberAccessibility;
28+
import static com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration;
2729
import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors;
2830
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
2931
import static com.oracle.svm.core.annotate.TargetElement.CONSTRUCTOR_NAME;
@@ -84,19 +86,21 @@
8486
import java.util.function.BiFunction;
8587
import java.util.function.IntFunction;
8688

87-
import com.oracle.svm.core.TrackDynamicAccessEnabled;
8889
import org.graalvm.nativeimage.AnnotationAccess;
8990
import org.graalvm.nativeimage.ImageSingletons;
9091
import org.graalvm.nativeimage.Platform;
9192
import org.graalvm.nativeimage.Platforms;
9293
import org.graalvm.word.WordBase;
9394

95+
import com.oracle.svm.configure.config.ConfigurationType;
96+
import com.oracle.svm.configure.config.SignatureUtil;
9497
import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse;
9598
import com.oracle.svm.core.BuildPhaseProvider.CompileQueueFinished;
9699
import com.oracle.svm.core.NeverInline;
97100
import com.oracle.svm.core.NeverInlineTrivial;
98101
import com.oracle.svm.core.RuntimeAssertionsSupport;
99102
import com.oracle.svm.core.SubstrateUtil;
103+
import com.oracle.svm.core.TrackDynamicAccessEnabled;
100104
import com.oracle.svm.core.Uninterruptible;
101105
import com.oracle.svm.core.annotate.Alias;
102106
import com.oracle.svm.core.annotate.Delete;
@@ -121,6 +125,7 @@
121125
import com.oracle.svm.core.jdk.ProtectionDomainSupport;
122126
import com.oracle.svm.core.jdk.Resources;
123127
import com.oracle.svm.core.meta.SharedType;
128+
import com.oracle.svm.core.metadata.MetadataTracer;
124129
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
125130
import com.oracle.svm.core.reflect.RuntimeMetadataDecoder;
126131
import com.oracle.svm.core.reflect.RuntimeMetadataDecoder.ConstructorDescriptor;
@@ -704,6 +709,9 @@ private ReflectionMetadata reflectionMetadata() {
704709
}
705710

706711
private void checkClassFlag(int mask, String methodName) {
712+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
713+
traceClassFlagQuery(mask);
714+
}
707715
if (throwMissingRegistrationErrors() && !(isClassFlagSet(mask) && getConditions().satisfied())) {
708716
MissingReflectionRegistrationUtils.forBulkQuery(DynamicHub.toClass(this), methodName);
709717
}
@@ -726,6 +734,30 @@ private static boolean isClassFlagSet(int mask, ReflectionMetadata reflectionMet
726734
return reflectionMetadata != null && (reflectionMetadata.classFlags & mask) != 0;
727735
}
728736

737+
private void traceClassFlagQuery(int mask) {
738+
ConfigurationType type = MetadataTracer.singleton().traceReflectionType(getName());
739+
if (type == null) {
740+
return;
741+
}
742+
// TODO (GR-64765): We over-approximate member accessibility here because we don't trace
743+
// accesses. Once we trace accesses, it will suffice to register the class for reflection.
744+
switch (mask) {
745+
case ALL_FIELDS_FLAG -> type.setAllPublicFields(ConfigurationMemberAccessibility.ACCESSED);
746+
case ALL_DECLARED_FIELDS_FLAG -> type.setAllDeclaredFields(ConfigurationMemberAccessibility.ACCESSED);
747+
case ALL_METHODS_FLAG -> type.setAllPublicMethods(ConfigurationMemberAccessibility.ACCESSED);
748+
case ALL_DECLARED_METHODS_FLAG -> type.setAllDeclaredMethods(ConfigurationMemberAccessibility.ACCESSED);
749+
case ALL_CONSTRUCTORS_FLAG -> type.setAllPublicConstructors(ConfigurationMemberAccessibility.ACCESSED);
750+
case ALL_DECLARED_CONSTRUCTORS_FLAG -> type.setAllDeclaredConstructors(ConfigurationMemberAccessibility.ACCESSED);
751+
case ALL_CLASSES_FLAG -> type.setAllPublicClasses();
752+
case ALL_DECLARED_CLASSES_FLAG -> type.setAllDeclaredClasses();
753+
case ALL_RECORD_COMPONENTS_FLAG -> type.setAllRecordComponents();
754+
case ALL_PERMITTED_SUBCLASSES_FLAG -> type.setAllPermittedSubclasses();
755+
case ALL_NEST_MEMBERS_FLAG -> type.setAllNestMembers();
756+
case ALL_SIGNERS_FLAG -> type.setAllSigners();
757+
default -> throw VMError.shouldNotReachHere("unknown class flag " + mask);
758+
}
759+
}
760+
729761
/** Executed at runtime. */
730762
private static Object initEnumConstantsAtRuntime(Method values) {
731763
try {
@@ -1282,6 +1314,11 @@ public Field getField(String fieldName) throws NoSuchFieldException, SecurityExc
12821314
private void checkField(String fieldName, Field field, boolean publicOnly) throws NoSuchFieldException {
12831315
boolean throwMissingErrors = throwMissingRegistrationErrors();
12841316
Class<?> clazz = DynamicHub.toClass(this);
1317+
1318+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
1319+
traceFieldLookup(fieldName, field, publicOnly);
1320+
}
1321+
12851322
if (field == null) {
12861323
if (throwMissingErrors && !allElementsRegistered(publicOnly, ALL_DECLARED_FIELDS_FLAG, ALL_FIELDS_FLAG)) {
12871324
MissingReflectionRegistrationUtils.forField(clazz, fieldName);
@@ -1305,6 +1342,25 @@ private void checkField(String fieldName, Field field, boolean publicOnly) throw
13051342
}
13061343
}
13071344

1345+
private void traceFieldLookup(String fieldName, Field field, boolean publicOnly) {
1346+
ConfigurationMemberDeclaration declaration = publicOnly ? ConfigurationMemberDeclaration.PRESENT : ConfigurationMemberDeclaration.DECLARED;
1347+
if (field != null) {
1348+
// register declaring type and field
1349+
ConfigurationType declaringType = MetadataTracer.singleton().traceReflectionType(field.getDeclaringClass().getName());
1350+
if (declaringType != null) {
1351+
declaringType.addField(fieldName, declaration, false);
1352+
}
1353+
// register receiver type
1354+
MetadataTracer.singleton().traceReflectionType(getName());
1355+
} else {
1356+
// register receiver type and negative field query
1357+
ConfigurationType receiverType = MetadataTracer.singleton().traceReflectionType(getName());
1358+
if (receiverType != null) {
1359+
receiverType.addField(fieldName, declaration, false);
1360+
}
1361+
}
1362+
}
1363+
13081364
@Substitute
13091365
private Method getMethod(String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
13101366
Objects.requireNonNull(methodName);
@@ -1340,6 +1396,11 @@ private void checkConstructor(Class<?>[] parameterTypes, Constructor<?> construc
13401396
private boolean checkExecutableExists(String methodName, Class<?>[] parameterTypes, Executable method, boolean publicOnly) {
13411397
boolean throwMissingErrors = throwMissingRegistrationErrors();
13421398
Class<?> clazz = DynamicHub.toClass(this);
1399+
1400+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
1401+
traceMethodLookup(methodName, parameterTypes, method, publicOnly);
1402+
}
1403+
13431404
if (method == null) {
13441405
boolean isConstructor = methodName.equals(CONSTRUCTOR_NAME);
13451406
int allDeclaredFlag = isConstructor ? ALL_DECLARED_CONSTRUCTORS_FLAG : ALL_DECLARED_METHODS_FLAG;
@@ -1366,6 +1427,38 @@ private boolean checkExecutableExists(String methodName, Class<?>[] parameterTyp
13661427
}
13671428
}
13681429

1430+
private void traceMethodLookup(String methodName, Class<?>[] parameterTypes, Executable method, boolean publicOnly) {
1431+
ConfigurationMemberDeclaration declaration = publicOnly ? ConfigurationMemberDeclaration.PRESENT : ConfigurationMemberDeclaration.DECLARED;
1432+
if (method != null) {
1433+
// register declaring type and method
1434+
ConfigurationType declaringType = MetadataTracer.singleton().traceReflectionType(method.getDeclaringClass().getName());
1435+
if (declaringType != null) {
1436+
declaringType.addMethod(methodName, toInternalSignature(parameterTypes), declaration);
1437+
}
1438+
// register receiver type
1439+
MetadataTracer.singleton().traceReflectionType(getName());
1440+
} else {
1441+
// register receiver type and negative method query
1442+
ConfigurationType receiverType = MetadataTracer.singleton().traceReflectionType(getName());
1443+
if (receiverType != null) {
1444+
receiverType.addMethod(methodName, toInternalSignature(parameterTypes), declaration, ConfigurationMemberAccessibility.QUERIED);
1445+
}
1446+
}
1447+
}
1448+
1449+
private static String toInternalSignature(Class<?>[] classes) {
1450+
List<String> names;
1451+
if (classes == null) {
1452+
names = List.of();
1453+
} else {
1454+
names = new ArrayList<>(classes.length);
1455+
for (Class<?> aClass : classes) {
1456+
names.add(aClass.getName());
1457+
}
1458+
}
1459+
return SignatureUtil.toInternalSignature(names);
1460+
}
1461+
13691462
private boolean allElementsRegistered(boolean publicOnly, int allDeclaredElementsFlag, int allPublicElementsFlag) {
13701463
return isClassFlagSet(allDeclaredElementsFlag) || (publicOnly && isClassFlagSet(allPublicElementsFlag));
13711464
}
@@ -1882,6 +1975,8 @@ public DynamicHub arrayType() {
18821975
}
18831976
if (companion.arrayHub == null) {
18841977
MissingReflectionRegistrationUtils.forClass(getTypeName() + "[]");
1978+
} else if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
1979+
MetadataTracer.singleton().traceReflectionType(companion.arrayHub.getTypeName());
18851980
}
18861981
return companion.arrayHub;
18871982
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaIOSubstitutions.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535
import java.util.concurrent.ConcurrentMap;
3636
import java.util.stream.Collectors;
3737

38-
import jdk.graal.compiler.java.LambdaUtils;
39-
4038
import com.oracle.svm.core.annotate.Alias;
4139
import com.oracle.svm.core.annotate.RecomputeFieldValue;
4240
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
@@ -45,8 +43,11 @@
4543
import com.oracle.svm.core.annotate.TargetElement;
4644
import com.oracle.svm.core.fieldvaluetransformer.NewInstanceFieldValueTransformer;
4745
import com.oracle.svm.core.hub.DynamicHub;
46+
import com.oracle.svm.core.metadata.MetadataTracer;
4847
import com.oracle.svm.core.reflect.serialize.MissingSerializationRegistrationUtils;
4948

49+
import jdk.graal.compiler.java.LambdaUtils;
50+
5051
@TargetClass(java.io.FileDescriptor.class)
5152
final class Target_java_io_FileDescriptor {
5253

@@ -68,8 +69,8 @@ static ObjectStreamClass lookup(Class<?> cl, boolean all) {
6869
return null;
6970
}
7071

71-
if (Serializable.class.isAssignableFrom(cl)) {
72-
if (!cl.isArray() && !DynamicHub.fromClass(cl).isRegisteredForSerialization()) {
72+
if (Serializable.class.isAssignableFrom(cl) && !cl.isArray()) {
73+
if (!DynamicHub.fromClass(cl).isRegisteredForSerialization()) {
7374
boolean isLambda = cl.getTypeName().contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING);
7475
boolean isProxy = Proxy.isProxyClass(cl);
7576
if (isProxy || isLambda) {
@@ -87,6 +88,9 @@ static ObjectStreamClass lookup(Class<?> cl, boolean all) {
8788
MissingSerializationRegistrationUtils.missingSerializationRegistration(cl, "type " + cl.getTypeName());
8889
}
8990
}
91+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
92+
MetadataTracer.singleton().traceSerializationType(cl.getName());
93+
}
9094
}
9195

9296
return Target_java_io_ObjectStreamClass_Caches.localDescs0.get(cl);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangReflectSubstitutions.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.oracle.svm.core.config.ConfigurationValues;
3838
import com.oracle.svm.core.hub.DynamicHub;
3939
import com.oracle.svm.core.hub.LayoutEncoding;
40+
import com.oracle.svm.core.metadata.MetadataTracer;
4041
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
4142
import com.oracle.svm.core.snippets.KnownIntrinsics;
4243

@@ -386,6 +387,9 @@ private static void set(Object a, int index, Object value) {
386387
@Substitute
387388
private static Object newArray(Class<?> componentType, int length)
388389
throws NegativeArraySizeException {
390+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
391+
MetadataTracer.singleton().traceReflectionType(componentType.arrayType().getName());
392+
}
389393
return KnownIntrinsics.unvalidatedNewArray(componentType, length);
390394
}
391395

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
7474
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
7575
import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
76+
import com.oracle.svm.core.metadata.MetadataTracer;
7677
import com.oracle.svm.core.util.ImageHeapMap;
7778
import com.oracle.svm.core.util.VMError;
7879
import com.oracle.svm.util.GlobUtils;
@@ -401,6 +402,9 @@ public static ResourceStorageEntryBase getAtRuntime(Module module, String resour
401402
return null;
402403
}
403404
}
405+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
406+
MetadataTracer.singleton().traceResource(resourceName, moduleName);
407+
}
404408
if (!entry.getConditions().satisfied()) {
405409
return missingMetadata(resourceName, throwOnMissing);
406410
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import com.oracle.svm.core.SubstrateUtil;
5858
import com.oracle.svm.core.configure.RuntimeConditionSet;
5959
import com.oracle.svm.core.jdk.Resources;
60+
import com.oracle.svm.core.metadata.MetadataTracer;
6061
import com.oracle.svm.core.util.ImageHeapMap;
6162
import com.oracle.svm.core.util.VMError;
6263
import com.oracle.svm.util.ReflectionUtil;
@@ -293,6 +294,12 @@ public boolean isRegisteredBundleLookup(String baseName, Locale locale, Object c
293294
/* Those cases will throw a NullPointerException before any lookup */
294295
return true;
295296
}
296-
return registeredBundles.containsKey(baseName) && registeredBundles.get(baseName).satisfied();
297+
if (registeredBundles.containsKey(baseName)) {
298+
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
299+
MetadataTracer.singleton().traceResourceBundle(baseName);
300+
}
301+
return registeredBundles.get(baseName).satisfied();
302+
}
303+
return false;
297304
}
298305
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethodDescriptor.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,19 @@ public boolean isClassInitializer() {
9898
return WRAPPED_CSTRING_EQUIVALENCE.equals(name, INITIALIZER_NAME);
9999
}
100100

101+
/**
102+
* Returns the method name as a String. Can be used if the descriptor is known to be a String
103+
* (i.e., it does not come from a JNI call); otherwise, use {@link #getNameConvertToString()}.
104+
*/
101105
public String getName() {
102106
return (String) name;
103107
}
104108

109+
/**
110+
* Returns the method signature as a String. Can be used if the descriptor is known to be a
111+
* String (i.e., it does not come from a JNI call); otherwise, use
112+
* {@link #getSignatureConvertToString()}.
113+
*/
105114
public String getSignature() {
106115
return (String) signature;
107116
}
@@ -113,6 +122,13 @@ public String getNameConvertToString() {
113122
return name.toString();
114123
}
115124

125+
/**
126+
* Performs a potentially costly conversion to string, only for slow paths.
127+
*/
128+
public String getSignatureConvertToString() {
129+
return signature.toString();
130+
}
131+
116132
public String getSignatureWithoutReturnType() {
117133
String signatureString = signature.toString();
118134
int parametersEnd = signatureString.lastIndexOf(')');

0 commit comments

Comments
 (0)