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
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This changelog summarizes major changes to GraalVM Native Image.

## Version 23.0.0
* (GR-40187) Report invalid use of SVM specific classes on image class- or module-path as error. As a temporary workaround, -H:+TolerateBuilderClassesOnImageClasspath allows turning the error into a warning.
* (GR-41196) Provide `.debug.svm.imagebuild.*` sections that contain build options and properties used in the build of the image.

## Version 22.3.0
* (GR-35721) Remove old build output style and the `-H:±BuildOutputUseNewStyle` option.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,9 @@ public static Path getDebugInfoSourceCacheRoot() {
@Option(help = "Omit generation of DebugLineInfo originating from inlined methods") //
public static final HostedOptionKey<Boolean> OmitInlinedMethodDebugLineInfo = new HostedOptionKey<>(true);

@Option(help = "Emit debuginfo debug.svm.imagebuild.* sections with detailed image-build options.")//
public static final HostedOptionKey<Boolean> UseImagebuildDebugSections = new HostedOptionKey<>(true);

@Fold
public static boolean supportCompileInIsolates() {
UserError.guarantee(!ConcealedOptions.SupportCompileInIsolates.getValue() || SpawnIsolates.getValue(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ public void loadAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoa
private List<String> remainingArguments;

public void setupHostedOptionParser(List<String> arguments) {
hostedOptionParser = new HostedOptionParser(getClassLoader());
remainingArguments = Collections.unmodifiableList((hostedOptionParser.parse(arguments)));
hostedOptionParser = new HostedOptionParser(getClassLoader(), arguments);
remainingArguments = Collections.unmodifiableList((hostedOptionParser.parse()));
parsedHostedOptions = new OptionValues(hostedOptionParser.getHostedValues());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,7 @@ public NativeLibraries getNativeLibs() {
public abstract void build(String imageName, DebugContext debug);

/**
* Write the image to the named file. This also writes debug information -- either to the same
* or a different file, as decided by the implementation of {@link #getOrCreateDebugObjectFile}.
* If {@link #getOrCreateDebugObjectFile} is not called, no debug information is written.
* Write the image to the named file.
*/
public abstract LinkerInvocation write(DebugContext debug, Path outputDirectory, Path tempDirectory, String imageName, BeforeImageWriteAccessImpl config);

Expand Down Expand Up @@ -166,7 +164,7 @@ public NativeImageHeap getHeap() {

public abstract long getImageHeapSize();

public abstract ObjectFile getOrCreateDebugObjectFile();
public abstract ObjectFile getObjectFile();

public boolean requiresCustomDebugRelocation() {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@

import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.util.Timer;
import com.oracle.graal.pointsto.util.TimerCollection;
import com.oracle.objectfile.BasicProgbitsSectionImpl;
import com.oracle.objectfile.BuildDependency;
import com.oracle.objectfile.LayoutDecision;
Expand All @@ -75,7 +73,6 @@
import com.oracle.objectfile.ObjectFile.RelocationKind;
import com.oracle.objectfile.ObjectFile.Section;
import com.oracle.objectfile.SectionName;
import com.oracle.objectfile.debuginfo.DebugInfoProvider;
import com.oracle.svm.core.BuildArtifacts;
import com.oracle.svm.core.BuildArtifacts.ArtifactType;
import com.oracle.svm.core.FrameAccess;
Expand All @@ -92,18 +89,16 @@
import com.oracle.svm.core.c.CUnsigned;
import com.oracle.svm.core.c.function.GraalIsolateHeader;
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.graal.code.CGlobalDataInfo;
import com.oracle.svm.core.graal.code.CGlobalDataReference;
import com.oracle.svm.core.image.ImageHeapLayoutInfo;
import com.oracle.svm.core.image.ImageHeapPartition;
import com.oracle.svm.core.meta.MethodPointer;
import com.oracle.svm.core.option.HostedOptionValues;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.NativeImageOptions;
import com.oracle.svm.hosted.ProgressReporter;
import com.oracle.svm.hosted.c.CGlobalDataFeature;
import com.oracle.svm.hosted.c.NativeLibraries;
import com.oracle.svm.hosted.c.codegen.CSourceCodeWriter;
Expand All @@ -112,7 +107,6 @@
import com.oracle.svm.hosted.code.CEntryPointData;
import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo;
import com.oracle.svm.hosted.image.RelocatableBuffer.Info;
import com.oracle.svm.hosted.image.sources.SourceManager;
import com.oracle.svm.hosted.meta.HostedMetaAccess;
import com.oracle.svm.hosted.meta.HostedMethod;
import com.oracle.svm.hosted.meta.HostedUniverse;
Expand Down Expand Up @@ -465,18 +459,6 @@ public void build(String imageName, DebugContext debug) {
(offset, symbolName, isGlobalSymbol) -> defineRelocationForSymbol(symbolName, offset));
defineDataSymbol(CGlobalDataInfo.CGLOBALDATA_BASE_SYMBOL_NAME, rwDataSection, RWDATA_CGLOBALS_PARTITION_OFFSET);

/*
* If we constructed debug info give the object file a chance to install it
*/
if (SubstrateOptions.GenerateDebugInfo.getValue(HostedOptionValues.singleton()) > 0) {
Timer timer = TimerCollection.singleton().get(TimerCollection.Registry.DEBUG_INFO);
try (Timer.StopTimer t = timer.start()) {
ImageSingletons.add(SourceManager.class, new SourceManager());
DebugInfoProvider provider = new NativeImageDebugInfoProvider(debug, codeCache, heap, metaAccess);
objectFile.installDebugInfo(provider);
}
ProgressReporter.singleton().setDebugInfoTimer(timer);
}
// - Write the heap to its own section.
// Dynamic linkers/loaders generally don't ensure any alignment to more than page
// boundaries, so we take care of this ourselves in CommittedMemoryProvider, if we can.
Expand Down Expand Up @@ -770,13 +752,8 @@ public long getImageHeapSize() {
}

@Override
public ObjectFile getOrCreateDebugObjectFile() {
assert objectFile != null;
/*
* FIXME: use ObjectFile.getOrCreateDebugObject, which knows how/whether to split (but is
* somewhat unimplemented right now, i.e. doesn't actually implement splitting, even on
* Mach-O where this is customary).
*/
public ObjectFile getObjectFile() {
assert objectFile != null : "objectFile accessed before set";
return objectFile;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,48 @@
*/
package com.oracle.svm.hosted.image;

import java.lang.management.ManagementFactory;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;

import com.oracle.graal.pointsto.util.GraalAccess;
import com.oracle.graal.pointsto.util.Timer;
import com.oracle.graal.pointsto.util.TimerCollection;
import com.oracle.objectfile.BasicProgbitsSectionImpl;
import com.oracle.objectfile.debuginfo.DebugInfoProvider;
import com.oracle.objectfile.io.AssemblyBuffer;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.UniqueShortNameProvider;
import com.oracle.svm.core.UniqueShortNameProviderDefaultImpl;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.option.HostedOptionValues;
import com.oracle.svm.hosted.FeatureImpl;
import org.graalvm.nativeimage.ImageSingletons;
import com.oracle.svm.hosted.ProgressReporter;
import com.oracle.svm.hosted.image.sources.SourceManager;

import java.util.List;

/**
* An automatic feature class which ensures that the Linux debug unique short name provider is
* registered when generating debug info for Linux.
*/
@AutomaticallyRegisteredFeature
@SuppressWarnings("unused")
class NativeImageDebugInfoFeature implements InternalFeature {

@Override
public boolean isInConfiguration(IsInConfigurationAccess access) {
return SubstrateOptions.GenerateDebugInfo.getValue() > 0;
}

@Override
public void afterRegistration(AfterRegistrationAccess access) {
/*
* Ensure that the Linux debug unique short name provider is registered when generating
* debug info for Linux.
*/
if (!UniqueShortNameProviderDefaultImpl.UseDefault.useDefaultProvider()) {
if (!ImageSingletons.contains(UniqueShortNameProvider.class)) {
// configure a BFD mangler to provide unique short names for method and field
Expand All @@ -62,4 +86,56 @@ public void afterRegistration(AfterRegistrationAccess access) {
}
}
}

@Override
@SuppressWarnings("try")
public void beforeImageWrite(BeforeImageWriteAccess access) {
Timer timer = TimerCollection.singleton().get(TimerCollection.Registry.DEBUG_INFO);
try (Timer.StopTimer t = timer.start()) {
ImageSingletons.add(SourceManager.class, new SourceManager());
var accessImpl = (FeatureImpl.BeforeImageWriteAccessImpl) access;
var image = accessImpl.getImage();
var debugContext = new DebugContext.Builder(HostedOptionValues.singleton(), new GraalDebugHandlersFactory(GraalAccess.getOriginalSnippetReflection())).build();
Comment on lines +96 to +98
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the use of var encouraged in the GraalVM code base?
If so, I will need to keep that in mind :)

(I am personally not a fan, but that's just my personal taste)

Copy link
Member

@olpaw olpaw Oct 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if it's easy from the context to guess what type the local is or the types are rather trivial, it's fine to use var.
If the type is non-trivial and being explicit actually helps to understand the code better then not using var is recommended.

DebugInfoProvider provider = new NativeImageDebugInfoProvider(debugContext, image.getCodeCache(), image.getHeap(), accessImpl.getHostedMetaAccess());
var objectFile = image.getObjectFile();
objectFile.installDebugInfo(provider);

if (Platform.includedIn(Platform.LINUX.class) && SubstrateOptions.UseImagebuildDebugSections.getValue()) {
/*-
* Provide imagebuild infos as special debug.svm.imagebuild.* sections
* The contents of these sections can be dumped with:
* readelf -p .<sectionName> <debuginfo file>
* e.g. readelf -p .debug.svm.imagebuild.arguments helloworld
*/
Function<List<String>, BasicProgbitsSectionImpl> makeSectionImpl = customInfo -> {
var content = AssemblyBuffer.createOutputAssembler(objectFile.getByteOrder());
for (String elem : customInfo) {
content.writeString(elem);
}
return new BasicProgbitsSectionImpl(content.getBlob()) {
@Override
public boolean isLoadable() {
return false;
}
};
};

var imageClassLoader = accessImpl.getImageClassLoader();

var classPath = imageClassLoader.classpath().stream().map(Path::toString).collect(Collectors.toList());
objectFile.newUserDefinedSection(".debug.svm.imagebuild.classpath", makeSectionImpl.apply(classPath));
var modulePath = imageClassLoader.modulepath().stream().map(Path::toString).collect(Collectors.toList());
objectFile.newUserDefinedSection(".debug.svm.imagebuild.modulepath", makeSectionImpl.apply(modulePath));
/* Get original arguments that got passed to the builder when it got started */
var builderArguments = imageClassLoader.classLoaderSupport.getHostedOptionParser().getArguments();
objectFile.newUserDefinedSection(".debug.svm.imagebuild.arguments", makeSectionImpl.apply(builderArguments));
/* System properties that got passed to the VM that runs the builder */
var builderProperties = ManagementFactory.getRuntimeMXBean().getInputArguments().stream()
.filter(arg -> arg.startsWith("-D"))
.sorted().collect(Collectors.toList());
objectFile.newUserDefinedSection(".debug.svm.imagebuild.java.properties", makeSectionImpl.apply(builderProperties));
}
}
ProgressReporter.singleton().setDebugInfoTimer(timer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public LinkerInvocation write(DebugContext debug, Path outputDirectory, Path tem
*/

LinkerInvocation inv = CCLinkerInvocation.getLinkerInvocation(imageKind, nativeLibs, codeCache.getCCInputFiles(tempDirectory, imageName),
outputDirectory, tempDirectory, imageName, codeCache.getSymbols(this.getOrCreateDebugObjectFile()));
outputDirectory, tempDirectory, imageName, codeCache.getSymbols(getObjectFile()));
for (Function<LinkerInvocation, LinkerInvocation> fn : config.getLinkerInvocationTransformers()) {
inv = fn.apply(inv);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceLoader;
Expand All @@ -49,12 +50,14 @@

public class HostedOptionParser implements HostedOptionProvider {

private final List<String> arguments;
private EconomicMap<OptionKey<?>, Object> hostedValues = OptionValues.newOptionMap();
private EconomicMap<OptionKey<?>, Object> runtimeValues = OptionValues.newOptionMap();
private EconomicMap<String, OptionDescriptor> allHostedOptions = EconomicMap.create();
private EconomicMap<String, OptionDescriptor> allRuntimeOptions = EconomicMap.create();

public HostedOptionParser(ClassLoader imageClassLoader) {
public HostedOptionParser(ClassLoader imageClassLoader, List<String> arguments) {
this.arguments = Collections.unmodifiableList(arguments);
collectOptions(ServiceLoader.load(OptionDescriptors.class, imageClassLoader), allHostedOptions, allRuntimeOptions);
}

Expand Down Expand Up @@ -83,12 +86,12 @@ public static void collectOptions(ServiceLoader<OptionDescriptors> optionDescrip
});
}

public List<String> parse(List<String> args) {
public List<String> parse() {

List<String> remainingArgs = new ArrayList<>();
Set<String> errors = new HashSet<>();
InterruptImageBuilding interrupt = null;
for (String arg : args) {
for (String arg : arguments) {
boolean isImageBuildOption = false;
try {
isImageBuildOption |= SubstrateOptionsParser.parseHostedOption(SubstrateOptionsParser.HOSTED_OPTION_PREFIX, allHostedOptions, hostedValues, PLUS_MINUS, errors, arg, System.out);
Expand Down Expand Up @@ -125,6 +128,10 @@ public List<String> parse(List<String> args) {
return remainingArgs;
}

public List<String> getArguments() {
return arguments;
}

@Override
public EconomicMap<OptionKey<?>, Object> getHostedValues() {
return hostedValues;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,11 @@
*/
package com.oracle.svm.hosted.option;

import java.util.ArrayList;
import java.util.List;

import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.MapCursor;
import org.graalvm.compiler.options.OptionKey;

import com.oracle.svm.core.option.SubstrateOptionsParser;

public interface HostedOptionProvider {
EconomicMap<OptionKey<?>, Object> getHostedValues();

EconomicMap<OptionKey<?>, Object> getRuntimeValues();

default List<String> getAppliedArguments() {
List<String> result = new ArrayList<>();
HostedOptionProviderHelper.addArguments(result, SubstrateOptionsParser.HOSTED_OPTION_PREFIX, getHostedValues());
HostedOptionProviderHelper.addArguments(result, SubstrateOptionsParser.RUNTIME_OPTION_PREFIX, getRuntimeValues());
return result;
}
}

class HostedOptionProviderHelper {
static void addArguments(List<String> result, String prefix, EconomicMap<OptionKey<?>, Object> values) {
MapCursor<OptionKey<?>, Object> cursor = values.getEntries();
while (cursor.advance()) {
result.add(prefix + cursor.getKey().getName() + "=" + cursor.getValue());
}
}
}