From b8a1c003caa96aa0237c198f84f6726063a7e7a7 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Wed, 12 Feb 2025 15:14:40 +0100 Subject: [PATCH 1/3] Fix incorrect calculation of "other data". --- .../oracle/svm/hosted/ProgressReporter.java | 8 ++++- .../NativeImageDebugInfoStripFeature.java | 29 ++++++++++++------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java index 77a4a1be503a..84155d703087 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java @@ -92,6 +92,7 @@ import com.oracle.svm.hosted.ProgressReporterJsonHelper.ResourceUsageKey; import com.oracle.svm.hosted.c.codegen.CCompilerInvoker; import com.oracle.svm.hosted.image.AbstractImage.NativeImageKind; +import com.oracle.svm.hosted.image.NativeImageDebugInfoStripFeature; import com.oracle.svm.hosted.reflect.ReflectionHostedSupport; import com.oracle.svm.hosted.util.CPUType; import com.oracle.svm.hosted.util.DiagnosticUtils; @@ -607,7 +608,12 @@ public void printCreationEnd(int imageFileSize, int heapObjectCount, long imageH } l.println(); } - long otherBytes = imageFileSize - codeAreaSize - imageHeapSize - debugInfoSize; + long otherBytes = imageFileSize - codeAreaSize - imageHeapSize; + if (!ImageSingletons.lookup(NativeImageDebugInfoStripFeature.class).hasStrippedSuccessfully()) { + // Only subtract if debug info is embedded in file (not stripped). + otherBytes -= debugInfoSize; + } + assert otherBytes >= 0 : "Other bytes should never be negative: " + otherBytes; recordJsonMetric(ImageDetailKey.IMAGE_HEAP_SIZE, imageHeapSize); recordJsonMetric(ImageDetailKey.TOTAL_SIZE, imageFileSize); recordJsonMetric(ImageDetailKey.CODE_AREA_SIZE, codeAreaSize); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoStripFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoStripFeature.java index 740873693b91..4741dbde0c14 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoStripFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoStripFeature.java @@ -30,29 +30,27 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.impl.InternalPlatform; -import com.oracle.graal.pointsto.util.GraalAccess; import com.oracle.objectfile.ObjectFile; import com.oracle.svm.core.BuildArtifacts; import com.oracle.svm.core.BuildArtifacts.ArtifactType; import com.oracle.svm.core.SubstrateOptions; 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.core.util.InterruptImageBuilding; import com.oracle.svm.core.util.UserError; +import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.AfterImageWriteAccessImpl; import com.oracle.svm.hosted.c.util.FileUtils; import com.oracle.svm.util.LogUtils; import jdk.graal.compiler.core.common.SuppressFBWarnings; -import jdk.graal.compiler.debug.DebugContext; -import jdk.graal.compiler.debug.DebugContext.Builder; import jdk.graal.compiler.debug.Indent; -import jdk.graal.compiler.printer.GraalDebugHandlersFactory; @AutomaticallyRegisteredFeature public class NativeImageDebugInfoStripFeature implements InternalFeature { + private Boolean hasStrippedSuccessfully = null; + @Override public boolean isInConfiguration(IsInConfigurationAccess access) { return SubstrateOptions.StripDebugInfo.getValue(); @@ -62,14 +60,14 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { @Override public void afterImageWrite(AfterImageWriteAccess access) { AfterImageWriteAccessImpl accessImpl = (AfterImageWriteAccessImpl) access; - DebugContext debugContext = new Builder(HostedOptionValues.singleton(), new GraalDebugHandlersFactory(GraalAccess.getOriginalSnippetReflection())).build(); - try (Indent indent = debugContext.logAndIndent("Stripping debuginfo")) { + try (Indent indent = accessImpl.getDebugContext().logAndIndent("Stripping debuginfo")) { switch (ObjectFile.getNativeFormat()) { case ELF: - stripLinux(accessImpl); + hasStrippedSuccessfully = stripLinux(accessImpl); break; case PECOFF: - // debug info is always "stripped" to a pdb file + // debug info is always "stripped" to a pdb file by linker + hasStrippedSuccessfully = true; break; case MACH_O: // Not supported. See warning in SubstrateOptions.validateStripDebugInfo @@ -80,14 +78,21 @@ public void afterImageWrite(AfterImageWriteAccess access) { } } + public boolean hasStrippedSuccessfully() { + if (hasStrippedSuccessfully == null) { + throw VMError.shouldNotReachHere("hasStrippedSuccessfully not available yet"); + } + return hasStrippedSuccessfully; + } + @SuppressFBWarnings(value = "", justification = "FB reports null pointer dereferencing although it is not possible in this case.") - private static void stripLinux(AfterImageWriteAccessImpl accessImpl) { + private static boolean stripLinux(AfterImageWriteAccessImpl accessImpl) { String objcopyExe = "objcopy"; String debugExtension = ".debug"; Path imagePath = accessImpl.getImagePath(); if (imagePath == null) { assert !Platform.includedIn(InternalPlatform.NATIVE_ONLY.class); - return; + return false; } Path imageName = imagePath.getFileName(); @@ -102,6 +107,7 @@ private static void stripLinux(AfterImageWriteAccessImpl accessImpl) { if (!objcopyAvailable) { LogUtils.warning("%s not available. The debuginfo will remain embedded in the executable.", objcopyExe); + return false; } else { try { Path outputDirectory = imagePath.getParent(); @@ -121,6 +127,7 @@ private static void stripLinux(AfterImageWriteAccessImpl accessImpl) { /* Strip debug info only. */ FileUtils.executeCommand(objcopyExe, "--strip-debug", imageFilePath); } + return true; } catch (IOException e) { throw UserError.abort("Generation of separate debuginfo file failed", e); } catch (InterruptedException e) { From 79a49ef4b8c075ed6ced9e54a1ccf43c87239afd Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Wed, 12 Feb 2025 15:17:52 +0100 Subject: [PATCH 2/3] Clean up ProgressReporter. --- .../oracle/svm/hosted/ProgressReporter.java | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java index 84155d703087..c7ae54cc9690 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java @@ -353,8 +353,8 @@ private void printExperimentalOptions(ImageClassLoader classLoader) { /* Only check builder arguments, ignore options that were set as part of others. */ continue; } - String origins = ""; - String migrationMessage = OptionUtils.getAnnotationsByType(descriptor, OptionMigrationMessage.class).stream().map(a -> a.value()).collect(Collectors.joining(". ")); + String origins; + String migrationMessage = OptionUtils.getAnnotationsByType(descriptor, OptionMigrationMessage.class).stream().map(OptionMigrationMessage::value).collect(Collectors.joining(". ")); String alternatives = ""; if (optionValue instanceof AccumulatingLocatableMultiOptionValue lmov) { @@ -411,7 +411,7 @@ private void printEnvironmentVariableOptions() { if (envVarValue != null && !envVarValue.isEmpty()) { l().printLineSeparator(); l().yellowBold().a(" ").doclink("Picked up " + SubstrateOptions.NATIVE_IMAGE_OPTIONS_ENV_VAR, "#glossary-picked-up-ni-options").reset().a(":").println(); - for (String arg : JDKArgsUtils.parseArgsFromEnvVar(envVarValue, SubstrateOptions.NATIVE_IMAGE_OPTIONS_ENV_VAR, msg -> UserError.abort(msg))) { + for (String arg : JDKArgsUtils.parseArgsFromEnvVar(envVarValue, SubstrateOptions.NATIVE_IMAGE_OPTIONS_ENV_VAR, UserError::abort)) { l().a(" - '%s'", arg).println(); } } @@ -598,6 +598,7 @@ public void printCreationEnd(int imageFileSize, int heapObjectCount, long imageH recordJsonMetric(ImageDetailKey.IMAGE_HEAP_RESOURCE_COUNT, numResources); l().a(format, ByteFormattingUtil.bytesToHuman(imageHeapSize), Utils.toPercentage(imageHeapSize, imageFileSize)) .doclink("image heap", "#glossary-image-heap").a(":%,9d objects and %,d resources", heapObjectCount, numResources).println(); + long otherBytes = imageFileSize - codeAreaSize - imageHeapSize; if (debugInfoSize > 0) { recordJsonMetric(ImageDetailKey.DEBUG_INFO_SIZE, debugInfoSize); // Optional metric DirectPrinter l = l().a(format, ByteFormattingUtil.bytesToHuman(debugInfoSize), Utils.toPercentage(debugInfoSize, imageFileSize)) @@ -607,11 +608,10 @@ public void printCreationEnd(int imageFileSize, int heapObjectCount, long imageH l.a(" generated in %.1fs", Utils.millisToSeconds(debugInfoTimer.getTotalTime())); } l.println(); - } - long otherBytes = imageFileSize - codeAreaSize - imageHeapSize; - if (!ImageSingletons.lookup(NativeImageDebugInfoStripFeature.class).hasStrippedSuccessfully()) { - // Only subtract if debug info is embedded in file (not stripped). - otherBytes -= debugInfoSize; + if (!ImageSingletons.lookup(NativeImageDebugInfoStripFeature.class).hasStrippedSuccessfully()) { + // Only subtract if debug info is embedded in file (not stripped). + otherBytes -= debugInfoSize; + } } assert otherBytes >= 0 : "Other bytes should never be negative: " + otherBytes; recordJsonMetric(ImageDetailKey.IMAGE_HEAP_SIZE, imageHeapSize); @@ -791,9 +791,7 @@ private void createAdditionalArtifacts(String imageName, NativeImageGenerator ge private void createAdditionalArtifactsOnSuccess(BuildArtifacts artifacts, NativeImageGenerator generator, OptionValues parsedHostedOptions) { Optional buildOutputJSONFile = SubstrateOptions.BuildOutputJSONFile.getValue(parsedHostedOptions).lastValue(); - if (buildOutputJSONFile.isPresent()) { - artifacts.add(ArtifactType.BUILD_INFO, reportBuildOutput(buildOutputJSONFile.get())); - } + buildOutputJSONFile.ifPresent(path -> artifacts.add(ArtifactType.BUILD_INFO, reportBuildOutput(path))); if (generator.getBigbang() != null && ImageBuildStatistics.Options.CollectImageBuildStatistics.getValue(parsedHostedOptions)) { artifacts.add(ArtifactType.BUILD_INFO, reportImageBuildStatistics()); } @@ -951,10 +949,7 @@ private static String truncateClassOrPackageName(String classOrPackageName) { } } - private static class GCStats { - private final long totalCount; - private final long totalTimeMillis; - + private record GCStats(long totalCount, long totalTimeMillis) { private static GCStats getCurrent() { long totalCount = 0; long totalTime = 0; @@ -970,11 +965,6 @@ private static GCStats getCurrent() { } return new GCStats(totalCount, totalTime); } - - GCStats(long totalCount, long totalTimeMillis) { - this.totalCount = totalCount; - this.totalTimeMillis = totalTimeMillis; - } } public abstract static class ReporterClosable implements AutoCloseable { From 3781e8198d0a16f3940bd675aa28065e36b01e8d Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Wed, 12 Feb 2025 15:19:02 +0100 Subject: [PATCH 3/3] Skip debugInfoSize calculation on Darwin. --- .../src/com/oracle/svm/hosted/image/NativeImage.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 b1f3fdcb12ca..9a863c53c074 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 @@ -177,11 +177,12 @@ protected final void write(DebugContext context, Path outputFile) { throw shouldNotReachHere(ex); } debugInfoSize = 0; - String debugIdentifier = OS.getCurrent() == OS.DARWIN ? "__debug" : ".debug"; - for (Element e : objectFile.getElements()) { - String name = e.getName(); - if (name.contains(debugIdentifier) && !name.startsWith(".rela")) { - debugInfoSize += e.getMemSize(objectFile.getDecisionsByElement()); + if (!OS.DARWIN.isCurrent()) { // debug info not available on Darwin + for (Element e : objectFile.getElements()) { + String name = e.getName(); + if (name.contains(".debug") && !name.startsWith(".rela")) { + debugInfoSize += e.getMemSize(objectFile.getDecisionsByElement()); + } } } if (NativeImageOptions.PrintImageElementSizes.getValue()) {