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.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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.