Skip to content

Commit

Permalink
Change object reachable callback to include scan reason
Browse files Browse the repository at this point in the history
  • Loading branch information
teshull committed Mar 25, 2024
1 parent e9c44db commit fd4c1e0
Show file tree
Hide file tree
Showing 16 changed files with 110 additions and 50 deletions.
Expand Up @@ -531,7 +531,7 @@ protected void onObjectReachable(ImageHeapConstant imageHeapConstant, ScanReason
/* Simulated constants don't have a backing object and don't need to be processed. */
if (object != null) {
try {
type.notifyObjectReachable(universe.getConcurrentAnalysisAccess(), object);
type.notifyObjectReachable(universe.getConcurrentAnalysisAccess(), object, reason);
} catch (UnsupportedFeatureException e) {
/* Enhance the unsupported feature message with the object trace and rethrow. */
StringBuilder backtrace = new StringBuilder();
Expand Down
Expand Up @@ -37,14 +37,14 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;
import org.graalvm.word.WordBase;

import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.ObjectScanner.ScanReason;
import com.oracle.graal.pointsto.PointsToAnalysis;
import com.oracle.graal.pointsto.api.DefaultUnsafePartition;
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
Expand Down Expand Up @@ -631,7 +631,7 @@ public Set<MethodOverrideReachableNotification> getOverrideReachabilityNotificat
return ConcurrentLightHashMap.getOrDefault(this, overrideReachableNotificationsUpdater, method, Collections.emptySet());
}

public <T> void registerObjectReachableCallback(BiConsumer<DuringAnalysisAccess, T> callback) {
public <T> void registerObjectReachableCallback(ObjectReachableCallback<T> callback) {
ConcurrentLightHashSet.addElement(this, objectReachableCallbacksUpdater, callback);
/* Register the callback with already discovered subtypes too. */
for (AnalysisType subType : subTypes) {
Expand All @@ -642,8 +642,8 @@ public <T> void registerObjectReachableCallback(BiConsumer<DuringAnalysisAccess,
}
}

public <T> void notifyObjectReachable(DuringAnalysisAccess access, T object) {
ConcurrentLightHashSet.forEach(this, objectReachableCallbacksUpdater, (BiConsumer<DuringAnalysisAccess, T> c) -> c.accept(access, object));
public <T> void notifyObjectReachable(DuringAnalysisAccess access, T object, ScanReason reason) {
ConcurrentLightHashSet.forEach(this, objectReachableCallbacksUpdater, (ObjectReachableCallback<T> c) -> c.doCallback(access, object, reason));
}

public void registerInstantiatedCallback(Consumer<DuringAnalysisAccess> callback) {
Expand Down Expand Up @@ -1040,7 +1040,7 @@ private void addSubType(AnalysisType subType) {
/* Register the object reachability callbacks with the newly discovered subtype. */
if (!subType.equals(this)) {
/* Subtypes include this type itself. */
ConcurrentLightHashSet.forEach(this, objectReachableCallbacksUpdater, (BiConsumer<DuringAnalysisAccess, Object> callback) -> subType.registerObjectReachableCallback(callback));
ConcurrentLightHashSet.forEach(this, objectReachableCallbacksUpdater, (ObjectReachableCallback<Object> callback) -> subType.registerObjectReachableCallback(callback));
}
assert result : "Tried to add a " + subType + " which is already registered";
}
Expand Down
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.graal.pointsto.meta;

import org.graalvm.nativeimage.hosted.Feature;

import com.oracle.graal.pointsto.ObjectScanner.ScanReason;

public interface ObjectReachableCallback<T> {
void doCallback(Feature.DuringAnalysisAccess access, T obj, ScanReason reason);
}
Expand Up @@ -50,7 +50,6 @@
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BooleanSupplier;
import java.util.stream.Collectors;

Expand All @@ -71,9 +70,11 @@
import org.graalvm.word.WordFactory;

import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.ObjectScanner;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
import com.oracle.graal.pointsto.meta.InvokeInfo;
import com.oracle.graal.pointsto.meta.ObjectReachableCallback;
import com.oracle.svm.core.OS;
import com.oracle.svm.core.RuntimeAssertionsSupport;
import com.oracle.svm.core.SubstrateUtil;
Expand Down Expand Up @@ -221,19 +222,19 @@ public boolean getAsBoolean() {
@Override
public void duringSetup(DuringSetupAccess a) {
DuringSetupAccessImpl access = (DuringSetupAccessImpl) a;
access.registerObjectReachableCallback(OptionKey.class, optionCollector::accept);
access.registerObjectReachableCallback(OptionKey.class, optionCollector::doCallback);

ImageClassLoader imageClassLoader = access.getImageClassLoader();
registerJNIConfiguration(imageClassLoader);
}

/** Collects all {@link OptionKey}s that are reachable at run time. */
private static class OptionCollector implements BiConsumer<DuringAnalysisAccess, OptionKey<?>> {
private static class OptionCollector implements ObjectReachableCallback<OptionKey<?>> {
final ConcurrentHashMap<OptionKey<?>, OptionKey<?>> options = new ConcurrentHashMap<>();
private boolean sealed;

@Override
public void accept(DuringAnalysisAccess access, OptionKey<?> option) {
public void doCallback(DuringAnalysisAccess access, OptionKey<?> option, ObjectScanner.ScanReason reason) {
if (sealed) {
assert options.contains(option) : "All options must have been discovered during static analysis";
} else {
Expand Down
Expand Up @@ -53,13 +53,15 @@
import org.graalvm.nativeimage.hosted.RuntimeReflection;

import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.ObjectScanner;
import com.oracle.graal.pointsto.api.DefaultUnsafePartition;
import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
import com.oracle.graal.pointsto.meta.ObjectReachableCallback;
import com.oracle.svm.common.meta.MultiMethod;
import com.oracle.svm.core.LinkerInvocation;
import com.oracle.svm.core.SubstrateOptions;
Expand Down Expand Up @@ -294,10 +296,10 @@ public void registerObjectReplacer(Function<Object, Object> replacer) {
/**
* Register a callback that is executed when an object of the specified type or any of its
* subtypes is marked as reachable.
*
*
* @since 24.0
*/
public <T> void registerObjectReachableCallback(Class<T> clazz, BiConsumer<DuringAnalysisAccess, T> callback) {
public <T> void registerObjectReachableCallback(Class<T> clazz, ObjectReachableCallback<T> callback) {
getMetaAccess().lookupJavaType(clazz).registerObjectReachableCallback(callback);
}

Expand Down Expand Up @@ -396,6 +398,10 @@ public void registerAsUnsafeAccessed(Field field) {
registerAsUnsafeAccessed(getMetaAccess().lookupJavaField(field), "registered from Feature API");
}

public void registerAsUnsafeAccessed(Field field, Object reason) {
registerAsUnsafeAccessed(getMetaAccess().lookupJavaField(field), reason);
}

public boolean registerAsUnsafeAccessed(AnalysisField aField, Object reason) {
return registerAsUnsafeAccessed(aField, DefaultUnsafePartition.get(), reason);
}
Expand Down Expand Up @@ -426,6 +432,10 @@ public void registerAsRoot(AnalysisMethod aMethod, boolean invokeSpecial, String
bb.addRootMethod(aMethod, invokeSpecial, reason, otherRoots);
}

public void registerAsRoot(AnalysisMethod aMethod, boolean invokeSpecial, ObjectScanner.ScanReason reason, MultiMethod.MultiMethodKey... otherRoots) {
bb.addRootMethod(aMethod, invokeSpecial, reason, otherRoots);
}

public void registerUnsafeFieldsRecomputed(Class<?> clazz) {
getMetaAccess().lookupJavaType(clazz).registerUnsafeFieldsRecomputed();
}
Expand Down
Expand Up @@ -32,6 +32,7 @@

import org.graalvm.nativeimage.hosted.RuntimeReflection;

import com.oracle.graal.pointsto.ObjectScanner;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl;
Expand All @@ -56,7 +57,8 @@ public void duringSetup(DuringSetupAccess a) {
* which notifies us for every reachable {@link Annotation} object in the heap and then checking
* if it is an annotation that was materialized by the JDK, i.e., it is a {@link Proxy}.
*/
private void registerDeclaredMethods(@SuppressWarnings("unused") DuringAnalysisAccess access, Annotation annotation) {
@SuppressWarnings("unused")
private void registerDeclaredMethods(DuringAnalysisAccess access, Annotation annotation, ObjectScanner.ScanReason reason) {
if (Proxy.isProxyClass(annotation.getClass())) {
Class<? extends Annotation> annotationType = annotation.annotationType();
if (processedTypes.add(annotationType)) {
Expand Down
Expand Up @@ -38,6 +38,7 @@

import org.graalvm.nativeimage.impl.clinit.ClassInitializationTracking;

import com.oracle.graal.pointsto.ObjectScanner;
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
Expand Down Expand Up @@ -140,10 +141,11 @@ private static void initializeNativeImagePackagesAtBuildTime(ClassInitialization
public void duringSetup(DuringSetupAccess a) {
FeatureImpl.DuringSetupAccessImpl access = (FeatureImpl.DuringSetupAccessImpl) a;
classInitializationSupport = access.getHostVM().getClassInitializationSupport();
access.registerObjectReachableCallback(Object.class, (ignore, obj) -> checkImageHeapInstance(obj));
access.registerObjectReachableCallback(Object.class, this::checkImageHeapInstance);
}

private void checkImageHeapInstance(Object obj) {
@SuppressWarnings("unused")
private void checkImageHeapInstance(DuringAnalysisAccess access, Object obj, ObjectScanner.ScanReason reason) {
/*
* Note that initializeAtBuildTime also memoizes the class as InitKind.BUILD_TIME, which
* means that the user cannot later manually register it as RERUN or RUN_TIME.
Expand Down
Expand Up @@ -45,6 +45,7 @@

import javax.management.MBeanServerConnection;

import com.oracle.graal.pointsto.ObjectScanner;
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
Expand All @@ -71,19 +72,20 @@ public class DisallowedImageHeapObjectFeature implements InternalFeature {
public void duringSetup(DuringSetupAccess a) {
FeatureImpl.DuringSetupAccessImpl access = (FeatureImpl.DuringSetupAccessImpl) a;
classInitialization = access.getHostVM().getClassInitializationSupport();
access.registerObjectReachableCallback(MBeanServerConnection.class, this::onMBeanServerConnectionReachable);
access.registerObjectReachableCallback(PlatformManagedObject.class, this::onPlatformManagedObjectReachable);
access.registerObjectReachableCallback(Random.class, (a1, obj) -> DisallowedImageHeapObjects.onRandomReachable(obj, this::error));
access.registerObjectReachableCallback(SplittableRandom.class, (a1, obj) -> DisallowedImageHeapObjects.onSplittableRandomReachable(obj, this::error));
access.registerObjectReachableCallback(Thread.class, (a1, obj) -> DisallowedImageHeapObjects.onThreadReachable(obj, this::error));
access.registerObjectReachableCallback(DisallowedImageHeapObjects.CONTINUATION_CLASS, (a1, obj) -> DisallowedImageHeapObjects.onContinuationReachable(obj, this::error));
access.registerObjectReachableCallback(FileDescriptor.class, (a1, obj) -> DisallowedImageHeapObjects.onFileDescriptorReachable(obj, this::error));
access.registerObjectReachableCallback(Buffer.class, (a1, obj) -> DisallowedImageHeapObjects.onBufferReachable(obj, this::error));
access.registerObjectReachableCallback(Cleaner.Cleanable.class, (a1, obj) -> DisallowedImageHeapObjects.onCleanableReachable(obj, this::error));
access.registerObjectReachableCallback(LEGACY_CLEANER_CLASS, (a1, obj) -> DisallowedImageHeapObjects.onCleanableReachable(obj, this::error));
access.registerObjectReachableCallback(Cleaner.class, (a1, obj) -> DisallowedImageHeapObjects.onCleanerReachable(obj, this::error));
access.registerObjectReachableCallback(ZipFile.class, (a1, obj) -> DisallowedImageHeapObjects.onZipFileReachable(obj, this::error));
access.registerObjectReachableCallback(CANCELLABLE_CLASS, (a1, obj) -> DisallowedImageHeapObjects.onCancellableReachable(obj, this::error));
access.registerObjectReachableCallback(MBeanServerConnection.class, (a1, obj, reason) -> onMBeanServerConnectionReachable(obj, this::error));
access.registerObjectReachableCallback(PlatformManagedObject.class, (a1, obj, reason) -> onPlatformManagedObjectReachable(obj, this::error));
access.registerObjectReachableCallback(Random.class, (a1, obj, reason) -> DisallowedImageHeapObjects.onRandomReachable(obj, this::error));
access.registerObjectReachableCallback(SplittableRandom.class, (a1, obj, reason) -> DisallowedImageHeapObjects.onSplittableRandomReachable(obj, this::error));
access.registerObjectReachableCallback(Thread.class, (a1, obj, reason) -> DisallowedImageHeapObjects.onThreadReachable(obj, this::error));
access.registerObjectReachableCallback(DisallowedImageHeapObjects.CONTINUATION_CLASS,
(a1, obj, reason) -> DisallowedImageHeapObjects.onContinuationReachable(obj, this::error));
access.registerObjectReachableCallback(FileDescriptor.class, (a1, obj, reason) -> DisallowedImageHeapObjects.onFileDescriptorReachable(obj, this::error));
access.registerObjectReachableCallback(Buffer.class, (a1, obj, reason) -> DisallowedImageHeapObjects.onBufferReachable(obj, this::error));
access.registerObjectReachableCallback(Cleaner.Cleanable.class, (a1, obj, reason) -> DisallowedImageHeapObjects.onCleanableReachable(obj, this::error));
access.registerObjectReachableCallback(LEGACY_CLEANER_CLASS, (a1, obj, reason) -> DisallowedImageHeapObjects.onCleanableReachable(obj, this::error));
access.registerObjectReachableCallback(Cleaner.class, (a1, obj, reason) -> DisallowedImageHeapObjects.onCleanerReachable(obj, this::error));
access.registerObjectReachableCallback(ZipFile.class, (a1, obj, reason) -> DisallowedImageHeapObjects.onZipFileReachable(obj, this::error));
access.registerObjectReachableCallback(CANCELLABLE_CLASS, (a1, obj, reason) -> DisallowedImageHeapObjects.onCancellableReachable(obj, this::error));

if (SubstrateOptions.DetectUserDirectoriesInImageHeap.getValue()) {
access.registerObjectReachableCallback(String.class, this::onStringReachable);
Expand Down Expand Up @@ -123,7 +125,8 @@ private static String[] getDisallowedSubstrings(String... substrings) {
}).toArray(String[]::new);
}

private void onStringReachable(@SuppressWarnings("unused") DuringAnalysisAccess a, String string) {
@SuppressWarnings("unused")
private void onStringReachable(DuringAnalysisAccess a, String string, ObjectScanner.ScanReason reason) {
if (disallowedSubstrings != null) {
for (String disallowedSubstring : disallowedSubstrings) {
if (string.contains(disallowedSubstring)) {
Expand All @@ -137,7 +140,8 @@ private void onStringReachable(@SuppressWarnings("unused") DuringAnalysisAccess
}
}

private void onByteArrayReachable(@SuppressWarnings("unused") DuringAnalysisAccess a, byte[] bytes) {
@SuppressWarnings("unused")
private void onByteArrayReachable(DuringAnalysisAccess a, byte[] bytes, ObjectScanner.ScanReason reason) {
if (disallowedByteSubstrings != null) {
for (Map.Entry<byte[], Charset> entry : disallowedByteSubstrings.entrySet()) {
byte[] disallowedSubstring = entry.getKey();
Expand All @@ -156,8 +160,8 @@ private void onByteArrayReachable(@SuppressWarnings("unused") DuringAnalysisAcce
/**
* See {@link ManagementSupport} for details why these objects are not allowed.
*/
private void onMBeanServerConnectionReachable(@SuppressWarnings("unused") DuringAnalysisAccess a, MBeanServerConnection serverConnection) {
throw error("Detected a MBean server in the image heap. This is currently not supported, but could be changed in the future. " +
private static void onMBeanServerConnectionReachable(MBeanServerConnection serverConnection, DisallowedImageHeapObjects.DisallowedObjectReporter reporter) {
throw reporter.raise("Detected a MBean server in the image heap. This is currently not supported, but could be changed in the future. " +
"Management beans are registered in many global caches that would need to be cleared and properly re-built at image build time. " +
"Class of disallowed object: " + serverConnection.getClass().getTypeName(),
serverConnection, "Try to avoid initializing the class that stores a MBean server or a MBean in a static field");
Expand All @@ -166,9 +170,9 @@ private void onMBeanServerConnectionReachable(@SuppressWarnings("unused") During
/**
* See {@link ManagementSupport} for details why these objects are not allowed.
*/
private void onPlatformManagedObjectReachable(@SuppressWarnings("unused") DuringAnalysisAccess a, PlatformManagedObject platformManagedObject) {
private static void onPlatformManagedObjectReachable(PlatformManagedObject platformManagedObject, DisallowedImageHeapObjects.DisallowedObjectReporter reporter) {
if (!ManagementSupport.getSingleton().isAllowedPlatformManagedObject(platformManagedObject)) {
throw error("Detected a PlatformManagedObject (a MXBean defined by the virtual machine) in the image heap. " +
throw reporter.raise("Detected a PlatformManagedObject (a MXBean defined by the virtual machine) in the image heap. " +
"This bean is introspecting the VM that runs the image builder, i.e., a VM instance that is no longer available at image runtime. " +
"Class of disallowed object: " + platformManagedObject.getClass().getTypeName(),
platformManagedObject, "Try to avoid initializing the class that stores the object in a static field");
Expand Down

0 comments on commit fd4c1e0

Please sign in to comment.