Skip to content
Merged
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 @@ -353,6 +353,10 @@ public boolean isDelayed() {
return compilationBehavior == CompilationBehavior.FULLY_DELAYED_TO_APPLICATION_LAYER && buildingSharedLayer;
}

/**
* Ensures this method is compiled in the initial layer. See
* {@link CompilationBehavior#PINNED_TO_INITIAL_LAYER} for more details.
*/
public void setPinnedToInitialLayer(Object reason) {
BigBang bigbang = getUniverse().getBigbang();
AnalysisError.guarantee(bigbang.getHostVM().buildingInitialLayer(), "Methods can only be pinned to the initial layer: %s", this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
Expand All @@ -53,12 +55,28 @@
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
import com.oracle.svm.core.graal.RuntimeCompilation;
import com.oracle.svm.core.headers.LibC;
import com.oracle.svm.core.imagelayer.BuildingImageLayerPredicate;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader;
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
import com.oracle.svm.core.memory.UntrackedNullableNativeMemory;
import com.oracle.svm.core.option.RuntimeOptionKey;
import com.oracle.svm.core.traits.BuiltinTraits.AllAccess;
import com.oracle.svm.core.traits.BuiltinTraits.BuildtimeAccessOnly;
import com.oracle.svm.core.traits.BuiltinTraits.SingleLayer;
import com.oracle.svm.core.traits.SingletonLayeredCallbacks;
import com.oracle.svm.core.traits.SingletonLayeredCallbacksSupplier;
import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.Independent;
import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.InitialLayerOnly;
import com.oracle.svm.core.traits.SingletonTrait;
import com.oracle.svm.core.traits.SingletonTraitKind;
import com.oracle.svm.core.traits.SingletonTraits;
import com.oracle.svm.core.util.ImageHeapList;
import com.oracle.svm.core.util.VMError;

import jdk.graal.compiler.api.replacements.Fold;
import jdk.graal.compiler.options.OptionKey;
import jdk.graal.compiler.word.Word;

/**
Expand All @@ -69,6 +87,7 @@
* stored in {@code argv} is used.
*/
@AutomaticallyRegisteredImageSingleton
@SingletonTraits(access = AllAccess.class, layeredCallbacks = SingleLayer.class, layeredInstallationKind = InitialLayerOnly.class)
public class IsolateArgumentParser {
@SuppressWarnings("unchecked")//
private final List<RuntimeOptionKey<?>> options = (List<RuntimeOptionKey<?>>) ImageHeapList.createGeneric(RuntimeOptionKey.class);
Expand Down Expand Up @@ -183,7 +202,11 @@ protected static List<RuntimeOptionKey<?>> getOptions() {

@Fold
protected static int getOptionCount() {
return getOptions().size();
if (ImageLayerBuildingSupport.firstImageBuild()) {
return getOptions().size();
} else {
return LayeredOptionInfo.singleton().getNumOptions();
}
}

@Uninterruptible(reason = "Still being initialized.")
Expand Down Expand Up @@ -559,10 +582,20 @@ private static CCharPointer startsWith(CCharPointer input, CCharPointer prefix)

@Fold
public static int getOptionIndex(RuntimeOptionKey<?> key) {
List<RuntimeOptionKey<?>> options = getOptions();
for (int i = 0; i < options.size(); i++) {
if (options.get(i) == key) {
return i;
if (ImageLayerBuildingSupport.firstImageBuild()) {
List<RuntimeOptionKey<?>> options = getOptions();
for (int i = 0; i < options.size(); i++) {
if (options.get(i) == key) {
return i;
}
}
} else {
var keyName = key.getName();
var optionNames = LayeredOptionInfo.singleton().getOptionNames();
for (int i = 0; i < optionNames.size(); i++) {
if (optionNames.get(i).equals(keyName)) {
return i;
}
}
}

Expand Down Expand Up @@ -599,4 +632,77 @@ public static boolean isNumeric(byte optionValueType) {
return optionValueType == INTEGER || optionValueType == LONG;
}
}

/**
* Within {@link IsolateArgumentParser} many methods need to be {@link Fold}ed. This class adds
* support so that we can handle these method folds within the application layer.
*/
@Platforms(Platform.HOSTED_ONLY.class)
@AutomaticallyRegisteredImageSingleton(onlyWith = BuildingImageLayerPredicate.class)
@SingletonTraits(access = BuildtimeAccessOnly.class, layeredCallbacks = LayeredCallbacks.class, layeredInstallationKind = Independent.class)
static class LayeredOptionInfo {
private static final int UNSET = -1;
final int numOptions;
final List<String> optionNames;

LayeredOptionInfo() {
this(UNSET, null);
}

LayeredOptionInfo(int numOptions, List<String> optionNames) {
this.numOptions = numOptions;
this.optionNames = optionNames;
}

static LayeredOptionInfo singleton() {
return ImageSingletons.lookup(LayeredOptionInfo.class);
}

int getNumOptions() {
if (numOptions == UNSET) {
throw VMError.shouldNotReachHere("numOptions is unset");
}
return numOptions;
}

List<String> getOptionNames() {
Objects.requireNonNull(optionNames);
return optionNames;
}
}

static class LayeredCallbacks extends SingletonLayeredCallbacksSupplier {

@Override
public SingletonTrait getLayeredCallbacksTrait() {
return new SingletonTrait(SingletonTraitKind.LAYERED_CALLBACKS, new SingletonLayeredCallbacks() {
@Override
public LayeredImageSingleton.PersistFlags doPersist(ImageSingletonWriter writer, Object singleton) {
if (ImageLayerBuildingSupport.firstImageBuild()) {
writer.writeInt("numOptions", IsolateArgumentParser.getOptionCount());
writer.writeStringList("optionNames", IsolateArgumentParser.getOptions().stream().map(OptionKey::getName).toList());
} else {
var metadata = (LayeredOptionInfo) singleton;
writer.writeInt("numOptions", metadata.getNumOptions());
writer.writeStringList("optionNames", metadata.optionNames);
}
return LayeredImageSingleton.PersistFlags.CREATE;
}

@Override
public Class<? extends SingletonLayeredCallbacks.LayeredSingletonInstantiator> getSingletonInstantiator() {
return SingletonInstantiator.class;
}
});
}
}

static class SingletonInstantiator implements SingletonLayeredCallbacks.LayeredSingletonInstantiator {
@Override
public Object createFromLoader(ImageSingletonLoader loader) {
int numOptions = loader.readInt("numOptions");
var optionNames = Collections.unmodifiableList(loader.readStringList("optionNames"));
return new LayeredOptionInfo(numOptions, optionNames);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ public List<String> getInputArguments() {
}
}

/**
* For layered images this method is delayed until the application layer. This is necessary so
* that the method handle can be inlined before analysis.
*/
public static void invokeMain(String[] args) throws Throwable {
String[] mainArgs = args;
if (ImageSingletons.contains(PreMainSupport.class)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,18 @@ public static boolean hasHeapDumpSupport() {
return hasAllOrKeywordMonitoringSupport(MONITORING_HEAPDUMP_NAME) && !Platform.includedIn(WINDOWS_BASE.class);
}

/**
* This needs to be an explicit method so that in layered builds compilation can be deferred to
* the app layer. Otherwise {@link SubstrateOptions#Name} will refer to the initial layer's
* name.
*/
static String determineHeapDumpPath() {
return HeapDumping.getHeapDumpPath(SubstrateOptions.Name.getValue() + ".hprof");
}

public static boolean dumpImageHeap() {
if (hasHeapDumpSupport()) {
String absoluteHeapDumpPath = HeapDumping.getHeapDumpPath(SubstrateOptions.Name.getValue() + ".hprof");
String absoluteHeapDumpPath = determineHeapDumpPath();
try {
HeapDumping.singleton().dumpHeap(absoluteHeapDumpPath, true);
} catch (IOException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,9 @@ <T> T doLookup(Class<T> key, boolean buildtimeAccess, boolean allowMultiLayered)

VMError.guarantee(info.singleton() != null);
Object singleton = info.singleton();
if (singleton == SINGLETON_INSTALLATION_FORBIDDEN) {
throw UserError.abort("Singleton is forbidden in current layer. Key: %s", key.getTypeName());
}
if (!allowMultiLayered) {
var trait = info.traitMap().getTrait(SingletonTraitKind.LAYERED_INSTALLATION_KIND);
trait.ifPresent(t -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import com.oracle.svm.core.meta.SharedType;
import com.oracle.svm.core.util.ByteArrayReader;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.FeatureImpl.AfterCompilationAccessImpl;
import com.oracle.svm.util.ReflectionUtil;

Expand Down Expand Up @@ -110,6 +111,10 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
if (VMInspectionOptions.hasHeapDumpSupport()) {
RuntimeSupport.getRuntimeSupport().addStartupHook(new HeapDumpStartupHook());
RuntimeSupport.getRuntimeSupport().addShutdownHook(new HeapDumpShutdownHook());
if (ImageLayerBuildingSupport.buildingImageLayer()) {
var method = ReflectionUtil.lookupMethod(VMInspectionOptions.class, "determineHeapDumpPath");
((FeatureImpl.BeforeAnalysisAccessImpl) access).getMetaAccess().lookupJavaMethod(method).setFullyDelayedToApplicationLayer();
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ public static void processLayerOptions(EconomicMap<OptionKey<?>, Object> values,

if (isLayerUseOptionEnabled(hostedOptions)) {
SubstrateOptions.ClosedTypeWorldHubLayout.update(values, false);
SubstrateOptions.ParseRuntimeOptions.update(values, false);
if (SubstrateOptions.imageLayerEnabledHandler != null) {
SubstrateOptions.imageLayerEnabledHandler.onOptionEnabled(values);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
*/
package com.oracle.svm.hosted.imagelayer;

import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import org.graalvm.nativeimage.c.type.CCharPointerPointer;
import org.graalvm.nativeimage.hosted.Feature;

import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.svm.core.JavaMainWrapper;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
Expand Down Expand Up @@ -59,6 +61,14 @@ public void beforeAnalysis(BeforeAnalysisAccess a) {
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Runtime.class, "getRuntime")).setPinnedToInitialLayer(pinReason);
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Runtime.class, "gc")).setPinnedToInitialLayer(pinReason);
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(Class.class, "getResource", String.class)).setPinnedToInitialLayer(pinReason);

/* SVM start-up logic should be pinned to the initial layer. */
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(JavaMainWrapper.class, "doRun", int.class, CCharPointerPointer.class)).setPinnedToInitialLayer(pinReason);
/*
* We want the method handle invocation present in this method to be inlined, and that is
* only possible within the application layer.
*/
metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(JavaMainWrapper.class, "invokeMain", String[].class)).setFullyDelayedToApplicationLayer();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.oracle.svm.core.IsolateArgumentParser;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.option.HostedOptionValues;
import com.oracle.svm.core.option.RuntimeOptionKey;
Expand Down Expand Up @@ -65,20 +66,31 @@ public void duringSetup(DuringSetupAccess a) {
public void beforeAnalysis(BeforeAnalysisAccess access) {
FeatureImpl.BeforeAnalysisAccessImpl accessImpl = (FeatureImpl.BeforeAnalysisAccessImpl) access;

boolean extensionLayer = ImageLayerBuildingSupport.buildingExtensionLayer();
UnmodifiableEconomicMap<OptionKey<?>, Object> map = HostedOptionValues.singleton().getMap();
for (OptionKey<?> key : map.getKeys()) {
if (key instanceof RuntimeOptionKey<?> runtimeOptionKey && runtimeOptionKey.shouldRegisterForIsolateArgumentParser()) {
/*
* The list of options IsolateArgumentParser has to parse, is built dynamically, to
* include only options of the current configuration. Here, all options that should
* get parsed by the IsolateArgumentParser are added to this list.
*/
IsolateArgumentParser.singleton().register(runtimeOptionKey);
registerOptionAsRead(accessImpl, runtimeOptionKey.getDescriptor().getDeclaringClass(), runtimeOptionKey.getName());
if (!extensionLayer) {
/*
* The list of options IsolateArgumentParser has to parse, is built dynamically,
* to include only options of the current configuration. Here, all options that
* should get parsed by the IsolateArgumentParser are added to this list.
*/
IsolateArgumentParser.singleton().register(runtimeOptionKey);
registerOptionAsRead(accessImpl, runtimeOptionKey.getDescriptor().getDeclaringClass(), runtimeOptionKey.getName());
} else {
/*
* All runtime options must have already been installed within the base layer.
* Within the extension layer we only confirm they are present.
*/
assert IsolateArgumentParser.getOptionIndex(runtimeOptionKey) >= 0;
}
}
}

IsolateArgumentParser.singleton().sealOptions();
if (!extensionLayer) {
IsolateArgumentParser.singleton().sealOptions();
}
}

@SuppressWarnings("unused")
Expand Down