diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index acdbc6d3ca3c..fb21c4e9fcd9 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -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. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index f0f8fab41521..e5a15741de31 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -665,6 +665,9 @@ public static Path getDebugInfoSourceCacheRoot() { @Option(help = "Omit generation of DebugLineInfo originating from inlined methods") // public static final HostedOptionKey OmitInlinedMethodDebugLineInfo = new HostedOptionKey<>(true); + @Option(help = "Emit debuginfo debug.svm.imagebuild.* sections with detailed image-build options.")// + public static final HostedOptionKey UseImagebuildDebugSections = new HostedOptionKey<>(true); + @Fold public static boolean supportCompileInIsolates() { UserError.guarantee(!ConcealedOptions.SupportCompileInIsolates.getValue() || SpawnIsolates.getValue(), diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java index 56fe1891ad91..fd29913a61bb 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java @@ -191,8 +191,8 @@ public void loadAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoa private List remainingArguments; public void setupHostedOptionParser(List 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()); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/AbstractImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/AbstractImage.java index 708dbf89dce2..77c04e369f8e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/AbstractImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/AbstractImage.java @@ -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); @@ -166,7 +164,7 @@ public NativeImageHeap getHeap() { public abstract long getImageHeapSize(); - public abstract ObjectFile getOrCreateDebugObjectFile(); + public abstract ObjectFile getObjectFile(); public boolean requiresCustomDebugRelocation() { return false; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java index 5ed07df0fcda..9fdedf73111c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java @@ -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; @@ -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; @@ -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; @@ -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; @@ -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. @@ -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; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoFeature.java index b0081f624927..65d48c24611d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoFeature.java @@ -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 @@ -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(); + 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 . + * e.g. readelf -p .debug.svm.imagebuild.arguments helloworld + */ + Function, 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); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageViaCC.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageViaCC.java index a797be032755..2080805593d4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageViaCC.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageViaCC.java @@ -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 fn : config.getLinkerInvocationTransformers()) { inv = fn.apply(inv); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/option/HostedOptionParser.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/option/HostedOptionParser.java index 46fa77d41fa6..e37c375c5ab3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/option/HostedOptionParser.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/option/HostedOptionParser.java @@ -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; @@ -49,12 +50,14 @@ public class HostedOptionParser implements HostedOptionProvider { + private final List arguments; private EconomicMap, Object> hostedValues = OptionValues.newOptionMap(); private EconomicMap, Object> runtimeValues = OptionValues.newOptionMap(); private EconomicMap allHostedOptions = EconomicMap.create(); private EconomicMap allRuntimeOptions = EconomicMap.create(); - public HostedOptionParser(ClassLoader imageClassLoader) { + public HostedOptionParser(ClassLoader imageClassLoader, List arguments) { + this.arguments = Collections.unmodifiableList(arguments); collectOptions(ServiceLoader.load(OptionDescriptors.class, imageClassLoader), allHostedOptions, allRuntimeOptions); } @@ -83,12 +86,12 @@ public static void collectOptions(ServiceLoader optionDescrip }); } - public List parse(List args) { + public List parse() { List remainingArgs = new ArrayList<>(); Set 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); @@ -125,6 +128,10 @@ public List parse(List args) { return remainingArgs; } + public List getArguments() { + return arguments; + } + @Override public EconomicMap, Object> getHostedValues() { return hostedValues; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/option/HostedOptionProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/option/HostedOptionProvider.java index 78cabf7db224..2b7e0b43467e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/option/HostedOptionProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/option/HostedOptionProvider.java @@ -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, Object> getHostedValues(); EconomicMap, Object> getRuntimeValues(); - - default List getAppliedArguments() { - List 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 result, String prefix, EconomicMap, Object> values) { - MapCursor, Object> cursor = values.getEntries(); - while (cursor.advance()) { - result.add(prefix + cursor.getKey().getName() + "=" + cursor.getValue()); - } - } }