Skip to content

Commit c5df0c3

Browse files
committed
[GR-66161] Fix native tracing when metadata is missing.
PullRequest: graal/21162
2 parents 7633fd4 + 586afd5 commit c5df0c3

File tree

13 files changed

+179
-114
lines changed

13 files changed

+179
-114
lines changed

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ private Object forName0(String className, ClassLoader classLoader) {
411411
/* Invalid class names always throw, no need for reflection data */
412412
return new ClassNotFoundException(className);
413413
}
414-
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
414+
if (MetadataTracer.enabled()) {
415415
// NB: the early returns above ensure we do not trace calls with bad type args.
416416
MetadataTracer.singleton().traceReflectionType(className);
417417
}
@@ -500,6 +500,12 @@ public static Throwable getSavedException(String className) {
500500
*/
501501
public static boolean canUnsafeInstantiateAsInstance(DynamicHub hub) {
502502
Class<?> clazz = DynamicHub.toClass(hub);
503+
if (MetadataTracer.enabled()) {
504+
ConfigurationType type = MetadataTracer.singleton().traceReflectionType(clazz.getName());
505+
if (type != null) {
506+
type.setUnsafeAllocated();
507+
}
508+
}
503509
RuntimeConditionSet conditionSet = null;
504510
for (var singleton : layeredSingletons()) {
505511
conditionSet = singleton.unsafeInstantiatedClasses.get(clazz);
@@ -508,12 +514,6 @@ public static boolean canUnsafeInstantiateAsInstance(DynamicHub hub) {
508514
}
509515
}
510516
if (conditionSet != null) {
511-
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
512-
ConfigurationType type = MetadataTracer.singleton().traceReflectionType(clazz.getName());
513-
if (type != null) {
514-
type.setUnsafeAllocated();
515-
}
516-
}
517517
return conditionSet.satisfied();
518518
}
519519
return false;

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ private ReflectionMetadata reflectionMetadata() {
709709
}
710710

711711
private void checkClassFlag(int mask, String methodName) {
712-
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
712+
if (MetadataTracer.enabled()) {
713713
traceClassFlagQuery(mask);
714714
}
715715
if (throwMissingRegistrationErrors() && !(isClassFlagSet(mask) && getConditions().satisfied())) {
@@ -1315,7 +1315,7 @@ private void checkField(String fieldName, Field field, boolean publicOnly) throw
13151315
boolean throwMissingErrors = throwMissingRegistrationErrors();
13161316
Class<?> clazz = DynamicHub.toClass(this);
13171317

1318-
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
1318+
if (MetadataTracer.enabled()) {
13191319
traceFieldLookup(fieldName, field, publicOnly);
13201320
}
13211321

@@ -1397,7 +1397,7 @@ private boolean checkExecutableExists(String methodName, Class<?>[] parameterTyp
13971397
boolean throwMissingErrors = throwMissingRegistrationErrors();
13981398
Class<?> clazz = DynamicHub.toClass(this);
13991399

1400-
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
1400+
if (MetadataTracer.enabled()) {
14011401
traceMethodLookup(methodName, parameterTypes, method, publicOnly);
14021402
}
14031403

@@ -1973,14 +1973,19 @@ public DynamicHub arrayType() {
19731973
if (toClass(this) == void.class) {
19741974
throw new UnsupportedOperationException(new IllegalArgumentException());
19751975
}
1976+
if (MetadataTracer.enabled()) {
1977+
MetadataTracer.singleton().traceReflectionType(arrayTypeName());
1978+
}
19761979
if (companion.arrayHub == null) {
1977-
MissingReflectionRegistrationUtils.reportClassAccess(getTypeName() + "[]");
1978-
} else if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
1979-
MetadataTracer.singleton().traceReflectionType(companion.arrayHub.getTypeName());
1980+
MissingReflectionRegistrationUtils.reportClassAccess(arrayTypeName());
19801981
}
19811982
return companion.arrayHub;
19821983
}
19831984

1985+
private String arrayTypeName() {
1986+
return getTypeName() + "[]";
1987+
}
1988+
19841989
@KeepOriginal
19851990
private native Class<?> elementType();
19861991

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ static ObjectStreamClass lookup(Class<?> cl, boolean all) {
6565
}
6666

6767
if (Serializable.class.isAssignableFrom(cl) && !cl.isArray()) {
68+
if (MetadataTracer.enabled()) {
69+
MetadataTracer.singleton().traceSerializationType(cl.getName());
70+
}
6871
if (!DynamicHub.fromClass(cl).isRegisteredForSerialization()) {
6972
MissingSerializationRegistrationUtils.reportSerialization(cl);
7073
}
71-
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
72-
MetadataTracer.singleton().traceSerializationType(cl.getName());
73-
}
7474
}
7575

7676
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ private static void set(Object a, int index, Object value) {
387387
@Substitute
388388
private static Object newArray(Class<?> componentType, int length)
389389
throws NegativeArraySizeException {
390-
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
390+
if (MetadataTracer.enabled()) {
391391
MetadataTracer.singleton().traceReflectionType(componentType.arrayType().getName());
392392
}
393393
return KnownIntrinsics.unvalidatedNewArray(componentType, length);

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

Lines changed: 106 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import org.graalvm.nativeimage.Platforms;
5555
import org.graalvm.nativeimage.impl.ConfigurationCondition;
5656

57+
import com.oracle.svm.core.AlwaysInline;
5758
import com.oracle.svm.core.BuildPhaseProvider;
5859
import com.oracle.svm.core.ClassLoaderSupport.ConditionWithOrigin;
5960
import com.oracle.svm.core.MissingRegistrationUtils;
@@ -65,7 +66,6 @@
6566
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
6667
import com.oracle.svm.core.feature.InternalFeature;
6768
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
68-
import com.oracle.svm.core.jdk.resources.MissingResourceRegistrationError;
6969
import com.oracle.svm.core.jdk.resources.MissingResourceRegistrationUtils;
7070
import com.oracle.svm.core.jdk.resources.ResourceExceptionEntry;
7171
import com.oracle.svm.core.jdk.resources.ResourceStorageEntry;
@@ -428,54 +428,47 @@ private static boolean wasAlreadyInCanonicalForm(String resourceName, String can
428428
return resourceName.equals(canonicalResourceName) || removeTrailingSlash(resourceName).equals(canonicalResourceName);
429429
}
430430

431-
public static ResourceStorageEntryBase getAtRuntime(String name, boolean throwOnMissing) {
432-
return getAtRuntime(null, name, throwOnMissing);
431+
public static ResourceStorageEntryBase getAtRuntime(String name) {
432+
return getAtRuntime(null, name, false);
433433
}
434434

435435
/**
436-
* If {@code throwOnMissing} is false, we have to distinguish an entry that was in the metadata
437-
* from one that was not, so the caller can correctly throw the
438-
* {@link MissingResourceRegistrationError}. This is needed because different modules can be
439-
* tried on the same resource name, causing an unexpected exception if we throw directly.
436+
* Looks up a resource from {@code module} with name {@code resourceName}.
437+
* <p>
438+
* The {@code probe} parameter indicates whether the caller is probing for the existence of a
439+
* resource. If {@code probe} is true, failed resource lookups return will not throw missing
440+
* registration errors and may instead return {@link #MISSING_METADATA_MARKER}.
441+
* <p>
442+
* Tracing note: When this method is used for probing, only successful metadata matches will be
443+
* traced. If a probing result is {@link #MISSING_METADATA_MARKER}, the caller must explicitly
444+
* trace the missing metadata.
440445
*/
441-
public static ResourceStorageEntryBase getAtRuntime(Module module, String resourceName, boolean throwOnMissing) {
446+
public static ResourceStorageEntryBase getAtRuntime(Module module, String resourceName, boolean probe) {
442447
VMError.guarantee(ImageInfo.inImageRuntimeCode(), "This function should be used only at runtime.");
443448
String canonicalResourceName = NativeImageResourcePathRepresentation.toCanonicalForm(resourceName);
444449
String moduleName = moduleName(module);
445450
ConditionalRuntimeValue<ResourceStorageEntryBase> entry = getEntry(module, canonicalResourceName);
446451
if (entry == null) {
447452
if (MissingRegistrationUtils.throwMissingRegistrationErrors()) {
448-
for (var r : layeredSingletons()) {
449-
MapCursor<RequestedPattern, RuntimeConditionSet> cursor = r.requestedPatterns.getEntries();
450-
while (cursor.advance()) {
451-
RequestedPattern moduleResourcePair = cursor.getKey();
452-
if (Objects.equals(moduleName, moduleResourcePair.module) &&
453-
((matchResource(moduleResourcePair.resource, resourceName) || matchResource(moduleResourcePair.resource, canonicalResourceName)) &&
454-
cursor.getValue().satisfied())) {
455-
return null;
456-
}
457-
}
458-
459-
String glob = GlobUtils.transformToTriePath(resourceName, moduleName);
460-
String canonicalGlob = GlobUtils.transformToTriePath(canonicalResourceName, moduleName);
461-
GlobTrieNode<ConditionWithOrigin> globsTrie = r.getResourcesTrieRoot();
462-
if (CompressedGlobTrie.match(globsTrie, glob) ||
463-
CompressedGlobTrie.match(globsTrie, canonicalGlob)) {
464-
return null;
465-
}
466-
return missingMetadata(module, canonicalGlob, throwOnMissing);
453+
if (missingResourceMatchesIncludePattern(resourceName, moduleName) || missingResourceMatchesIncludePattern(canonicalResourceName, moduleName)) {
454+
// This resource name matches a pattern/glob from the provided metadata, but no
455+
// resource with the name actually exists. Do not report missing metadata.
456+
traceResource(resourceName, moduleName);
457+
return null;
467458
}
468-
469-
return missingMetadata(module, resourceName, throwOnMissing);
459+
traceResourceMissingMetadata(resourceName, moduleName, probe);
460+
return missingMetadata(module, resourceName, probe);
470461
} else {
462+
// NB: Without exact reachability metadata, resource include patterns are not
463+
// stored in the image heap, so we cannot reliably identify if the resource was
464+
// included at build time. Assume it is missing.
465+
traceResourceMissingMetadata(resourceName, moduleName, probe);
471466
return null;
472467
}
473468
}
474-
if (MetadataTracer.Options.MetadataTracingSupport.getValue() && MetadataTracer.singleton().enabled()) {
475-
MetadataTracer.singleton().traceResource(resourceName, moduleName);
476-
}
469+
traceResource(resourceName, moduleName);
477470
if (!entry.getConditions().satisfied()) {
478-
return missingMetadata(module, resourceName, throwOnMissing);
471+
return missingMetadata(module, resourceName, probe);
479472
}
480473

481474
ResourceStorageEntryBase unconditionalEntry = entry.getValue();
@@ -503,6 +496,51 @@ public static ResourceStorageEntryBase getAtRuntime(Module module, String resour
503496
return unconditionalEntry;
504497
}
505498

499+
@AlwaysInline("tracing should fold away when disabled")
500+
private static void traceResource(String resourceName, String moduleName) {
501+
if (MetadataTracer.enabled()) {
502+
MetadataTracer.singleton().traceResource(resourceName, moduleName);
503+
}
504+
}
505+
506+
@AlwaysInline("tracing should fold away when disabled")
507+
private static void traceResourceMissingMetadata(String resourceName, String moduleName) {
508+
traceResourceMissingMetadata(resourceName, moduleName, false);
509+
}
510+
511+
@AlwaysInline("tracing should fold away when disabled")
512+
private static void traceResourceMissingMetadata(String resourceName, String moduleName, boolean probe) {
513+
if (MetadataTracer.enabled() && !probe) {
514+
// Do not trace missing metadata for probing queries, otherwise we'll trace an entry for
515+
// every module. The caller is responsible for tracing missing entries if it uses
516+
// probing.
517+
MetadataTracer.singleton().traceResource(resourceName, moduleName);
518+
}
519+
}
520+
521+
/**
522+
* Checks whether the given missing resource is matched by a pattern/glob registered at build
523+
* time. In such a case, we should not report missing metadata.
524+
*/
525+
private static boolean missingResourceMatchesIncludePattern(String resourceName, String moduleName) {
526+
VMError.guarantee(MissingRegistrationUtils.throwMissingRegistrationErrors(), "include patterns are only stored in the image with exact reachability metadata");
527+
String glob = GlobUtils.transformToTriePath(resourceName, moduleName);
528+
for (var r : layeredSingletons()) {
529+
MapCursor<RequestedPattern, RuntimeConditionSet> cursor = r.requestedPatterns.getEntries();
530+
while (cursor.advance()) {
531+
RequestedPattern moduleResourcePair = cursor.getKey();
532+
if (Objects.equals(moduleName, moduleResourcePair.module) && matchResource(moduleResourcePair.resource, resourceName) && cursor.getValue().satisfied()) {
533+
return true;
534+
}
535+
}
536+
537+
if (CompressedGlobTrie.match(r.getResourcesTrieRoot(), glob)) {
538+
return true;
539+
}
540+
}
541+
return false;
542+
}
543+
506544
private static ConditionalRuntimeValue<ResourceStorageEntryBase> getEntry(Module module, String canonicalResourceName) {
507545
for (var r : layeredSingletons()) {
508546
ConditionalRuntimeValue<ResourceStorageEntryBase> entry = r.resources.get(createStorageKey(module, canonicalResourceName));
@@ -513,8 +551,8 @@ private static ConditionalRuntimeValue<ResourceStorageEntryBase> getEntry(Module
513551
return null;
514552
}
515553

516-
private static ResourceStorageEntryBase missingMetadata(Module module, String resourceName, boolean throwOnMissing) {
517-
if (throwOnMissing) {
554+
private static ResourceStorageEntryBase missingMetadata(Module module, String resourceName, boolean probe) {
555+
if (!probe) {
518556
MissingResourceRegistrationUtils.reportResourceAccess(module, resourceName);
519557
}
520558
return MISSING_METADATA_MARKER;
@@ -544,42 +582,45 @@ public static URL createURL(Module module, String resourceName) {
544582
return urls.hasMoreElements() ? urls.nextElement() : null;
545583
}
546584

547-
public static InputStream createInputStream(String resourceName) {
548-
return createInputStream(null, resourceName);
549-
}
550-
551585
/* Avoid pulling in the URL class when only an InputStream is needed. */
552586
public static InputStream createInputStream(Module module, String resourceName) {
553587
if (resourceName == null) {
554588
return null;
555589
}
590+
ResourceStorageEntryBase entry = findResourceForInputStream(module, resourceName);
591+
if (entry == MISSING_METADATA_MARKER) {
592+
traceResourceMissingMetadata(resourceName, moduleName(module));
593+
MissingResourceRegistrationUtils.reportResourceAccess(module, resourceName);
594+
return null;
595+
} else if (entry == null) {
596+
return null;
597+
}
598+
List<byte[]> data = entry.getData();
599+
return data.isEmpty() ? null : new ByteArrayInputStream(data.get(0));
600+
}
556601

557-
ResourceStorageEntryBase entry = getAtRuntime(module, resourceName, false);
558-
boolean isInMetadata = entry != MISSING_METADATA_MARKER;
559-
if (moduleName(module) == null && (entry == MISSING_METADATA_MARKER || entry == null)) {
602+
private static ResourceStorageEntryBase findResourceForInputStream(Module module, String resourceName) {
603+
ResourceStorageEntryBase result = getAtRuntime(module, resourceName, true);
604+
if (moduleName(module) == null && (result == MISSING_METADATA_MARKER || result == null)) {
560605
/*
561606
* If module is not specified or is an unnamed module and entry was not found as
562607
* classpath-resource we have to search for the resource in all modules in the image.
563608
*/
564609
for (Module m : RuntimeModuleSupport.singleton().getBootLayer().modules()) {
565-
entry = getAtRuntime(m, resourceName, false);
610+
ResourceStorageEntryBase entry = getAtRuntime(m, resourceName, true);
566611
if (entry != MISSING_METADATA_MARKER) {
567-
isInMetadata = true;
568-
}
569-
if (entry != null && entry != MISSING_METADATA_MARKER) {
570-
break;
612+
if (entry != null) {
613+
// resource found
614+
return entry;
615+
} else {
616+
// found a negative query. remember this result but keep trying in case some
617+
// other module supplies an actual resource.
618+
result = null;
619+
}
571620
}
572621
}
573622
}
574-
575-
if (!isInMetadata) {
576-
MissingResourceRegistrationUtils.reportResourceAccess(module, resourceName);
577-
}
578-
if (entry == null || entry == MISSING_METADATA_MARKER) {
579-
return null;
580-
}
581-
List<byte[]> data = entry.getData();
582-
return data.isEmpty() ? null : new ByteArrayInputStream(data.get(0));
623+
return result;
583624
}
584625

585626
public static Enumeration<URL> createURLs(String resourceName) {
@@ -595,23 +636,24 @@ public static Enumeration<URL> createURLs(Module module, String resourceName) {
595636

596637
List<URL> resourcesURLs = new ArrayList<>();
597638
String canonicalResourceName = NativeImageResourcePathRepresentation.toCanonicalForm(resourceName);
598-
boolean shouldAppendTrailingSlash = hasTrailingSlash(resourceName);
639+
if (hasTrailingSlash(resourceName)) {
640+
canonicalResourceName += "/";
641+
}
599642

600643
/* If moduleName was unspecified we have to consider all modules in the image */
601644
if (moduleName(module) == null) {
602645
for (Module m : RuntimeModuleSupport.singleton().getBootLayer().modules()) {
603-
ResourceStorageEntryBase entry = getAtRuntime(m, resourceName, false);
604-
if (entry == MISSING_METADATA_MARKER) {
605-
continue;
646+
ResourceStorageEntryBase entry = getAtRuntime(m, resourceName, true);
647+
if (entry != MISSING_METADATA_MARKER) {
648+
missingMetadata = false;
649+
addURLEntries(resourcesURLs, (ResourceStorageEntry) entry, m, canonicalResourceName);
606650
}
607-
missingMetadata = false;
608-
addURLEntries(resourcesURLs, (ResourceStorageEntry) entry, m, shouldAppendTrailingSlash ? canonicalResourceName + '/' : canonicalResourceName);
609651
}
610652
}
611-
ResourceStorageEntryBase explicitEntry = getAtRuntime(module, resourceName, false);
653+
ResourceStorageEntryBase explicitEntry = getAtRuntime(module, resourceName, true);
612654
if (explicitEntry != MISSING_METADATA_MARKER) {
613655
missingMetadata = false;
614-
addURLEntries(resourcesURLs, (ResourceStorageEntry) explicitEntry, module, shouldAppendTrailingSlash ? canonicalResourceName + '/' : canonicalResourceName);
656+
addURLEntries(resourcesURLs, (ResourceStorageEntry) explicitEntry, module, canonicalResourceName);
615657
}
616658

617659
if (missingMetadata) {

0 commit comments

Comments
 (0)