From d69ec31dccde73cc710ef37ec5ef9f652bb915e3 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Thu, 10 Nov 2022 15:51:15 +0100 Subject: [PATCH 1/2] More user-friendly reporting of internal errors. In case of internal errors, the build process generates an error report and no longer shows daunting stack traces. Instead, it fails with a clear message that tells users how to proceed: inspect the error report and, if unable to resolve the problem, file an issue with the error report. Inspired by HotSpot, the default filename for error reports is `./svm_err__pid.md`. --- substratevm/CHANGELOG.md | 1 + .../com/oracle/svm/core/BuildArtifacts.java | 1 + .../src/com/oracle/svm/core/VM.java | 6 +- .../hosted/NativeImageGeneratorRunner.java | 7 +- .../oracle/svm/hosted/NativeImageOptions.java | 24 ++++ .../oracle/svm/hosted/ProgressReporter.java | 80 ++++++++--- .../src/com/oracle/svm/hosted/VMFeature.java | 2 +- .../image/NativeImageDebugInfoFeature.java | 21 +-- .../svm/hosted/util/DiagnosticUtils.java | 54 ++++++++ .../svm/hosted/util/VMErrorReporter.java | 124 ++++++++++++++++++ 10 files changed, 277 insertions(+), 43 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/util/DiagnosticUtils.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/util/VMErrorReporter.java diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 573cbe3ca392..e80e3ede9681 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -9,6 +9,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-41674) Class instanceOf and isAssignableFrom checks do need to make the checked type reachable. * (GR-41100) Add support for `-XX:HeapDumpPath` to control where heap dumps are created. * (GR-42148) Adjust build output to report types (primitives, classes, interfaces, and arrays) instead of classes and revise the output schema of `-H:BuildOutputJSONFile`. +* (GR-41912) The builder now generated reports for internal errors, which users can share when creating issues. By default, error reports follow the `svm_err__pid.md` pattern and are created in the working directory. Use `-H:ErrorFile` to adjust the path or filename. ## 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/BuildArtifacts.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/BuildArtifacts.java index 053479a3d30c..51ce7ad3a75b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/BuildArtifacts.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/BuildArtifacts.java @@ -38,6 +38,7 @@ enum ArtifactType { HEADER, IMPORT_LIB, DEBUG_INFO, + BUILD_INFO, } static BuildArtifacts singleton() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VM.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VM.java index 800d246dc052..9d50baa95bb9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VM.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2022, 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 @@ -33,9 +33,11 @@ public final class VM { public final String version; + public final String supportURL; @Platforms(Platform.HOSTED_ONLY.class) - public VM(String config) { + public VM(String config, String supportURL) { + this.supportURL = supportURL; String versionStr = System.getProperty("org.graalvm.version"); VMError.guarantee(versionStr != null); versionStr = "GraalVM " + versionStr; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java index 138145a48470..4a6e08db071e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java @@ -283,6 +283,7 @@ private int buildImage(ImageClassLoader classLoader) { ForkJoinPool compilationExecutor = null; ProgressReporter reporter = new ProgressReporter(parsedHostedOptions); + Throwable vmError = null; boolean wasSuccessfulBuild = false; try (StopTimer ignored = totalTimer.start()) { Timer classlistTimer = timerCollection.get(TimerCollection.Registry.CLASSLIST); @@ -455,12 +456,10 @@ private int buildImage(ImageClassLoader classLoader) { } return 1; } catch (Throwable e) { - NativeImageGeneratorRunner.reportFatalError(e); + vmError = e; return 1; } finally { - if (imageName != null && generator != null) { - reporter.printEpilog(imageName, generator, wasSuccessfulBuild, parsedHostedOptions); - } + reporter.printEpilog(imageName, generator, classLoader, vmError, parsedHostedOptions); NativeImageGenerator.clearSystemPropertiesForImage(); ImageSingletonsSupportImpl.HostedManagement.clear(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java index 14c4a831967b..5c21ce7e0edc 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java @@ -27,19 +27,24 @@ import static org.graalvm.compiler.options.OptionType.Debug; import static org.graalvm.compiler.options.OptionType.User; +import java.nio.file.Path; import java.nio.file.Paths; +import java.text.SimpleDateFormat; import java.util.Arrays; +import java.util.Date; import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionValues; +import org.graalvm.compiler.serviceprovider.GraalServices; import com.oracle.graal.pointsto.reports.ReportUtils; import com.oracle.graal.pointsto.util.CompletionExecutor; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.option.APIOption; import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.option.LocatableMultiOptionValue; import com.oracle.svm.core.util.UserError; import com.oracle.svm.hosted.classinitialization.ClassInitializationOptions; @@ -180,6 +185,25 @@ public static CStandards getCStandard() { @Option(help = "Print unsafe operation offset warnings.)")// public static final HostedOptionKey UnsafeOffsetWarningsAreFatal = new HostedOptionKey<>(false); + private static final String DEFAULT_ERROR_FILE_NAME = "svm_err_%t_pid%p.md"; + + public static final Path getErrorFilePath() { + String errorFile = NativeImageOptions.ErrorFile.getValue(); + if (errorFile.isEmpty()) { + return NativeImageGenerator.generatedFiles(HostedOptionValues.singleton()).resolve(expandErrorFile(DEFAULT_ERROR_FILE_NAME)); + } else { + return Paths.get(expandErrorFile(errorFile)); + } + } + + private static String expandErrorFile(String errorFile) { + String timestamp = new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSS").format(new Date(GraalServices.getGlobalTimeStamp())); + return errorFile.replaceAll("%p", GraalServices.getExecutionID()).replaceAll("%t", timestamp); + } + + @Option(help = "If an error occurs, save a build error report to this file [default: ./" + DEFAULT_ERROR_FILE_NAME + "] (%p replaced with pid, %t with timestamp).)")// + public static final HostedOptionKey ErrorFile = new HostedOptionKey<>(""); + @Option(help = "Show exception stack traces for exceptions during image building.)")// public static final HostedOptionKey ReportExceptionStackTraces = new HostedOptionKey<>(areAssertionsEnabled()); 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 e51774403b9d..42af977f223d 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 @@ -74,6 +74,7 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.VM; import com.oracle.svm.core.code.CodeInfoTable; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.jdk.Resources; @@ -81,7 +82,6 @@ import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ProgressReporterJsonHelper.AnalysisResults; import com.oracle.svm.hosted.ProgressReporterJsonHelper.GeneralInfo; @@ -94,6 +94,7 @@ import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.reflect.ReflectionHostedSupport; +import com.oracle.svm.hosted.util.VMErrorReporter; import com.oracle.svm.util.ImageBuildStatistics; import com.oracle.svm.util.ReflectionUtil; @@ -612,18 +613,29 @@ private void printBreakdowns() { .a(String.format("%9s for %s more object types", Utils.bytesToHuman(totalHeapBytes - printedHeapBytes), numHeapItems - printedHeapItems)).flushln(); } - public void printEpilog(String imageName, NativeImageGenerator generator, boolean wasSuccessfulBuild, OptionValues parsedHostedOptions) { + public void printEpilog(String imageName, NativeImageGenerator generator, ImageClassLoader classLoader, Throwable error, OptionValues parsedHostedOptions) { + executor.shutdown(); + createAdditionalArtifacts(imageName, generator, classLoader, error, parsedHostedOptions); + + if (imageName == null || generator == null) { + printErrorMessage(error); + return; + } + l().printLineSeparator(); printResourceStatistics(); double totalSeconds = Utils.millisToSeconds(getTimer(TimerCollection.Registry.TOTAL).getTotalTime()); recordJsonMetric(ResourceUsageKey.TOTAL_SECS, totalSeconds); + if (jsonHelper != null) { + BuildArtifacts.singleton().add(ArtifactType.BUILD_INFO, jsonHelper.printToFile()); + } Map> artifacts = generator.getBuildArtifacts(); if (!artifacts.isEmpty()) { - l().printLineSeparator(); - printArtifacts(imageName, generator, parsedHostedOptions, artifacts, wasSuccessfulBuild); + BuildArtifacts.singleton().add(ArtifactType.BUILD_INFO, reportBuildArtifacts(NativeImageGenerator.generatedFiles(HostedOptionValues.singleton()), imageName, artifacts)); } + printArtifacts(artifacts); l().printHeadlineSeparator(); @@ -633,12 +645,51 @@ public void printEpilog(String imageName, NativeImageGenerator generator, boolea } else { timeStats = String.format("%dm %ds", (int) totalSeconds / 60, (int) totalSeconds % 60); } - l().a(wasSuccessfulBuild ? "Finished" : "Failed").a(" generating '").bold().a(imageName).reset().a("' ") - .a(wasSuccessfulBuild ? "in" : "after").a(" ").a(timeStats).a(".").println(); - executor.shutdown(); + l().a(error == null ? "Finished" : "Failed").a(" generating '").bold().a(imageName).reset().a("' ") + .a(error == null ? "in" : "after").a(" ").a(timeStats).a(".").println(); + + printErrorMessage(error); } - private void printArtifacts(String imageName, NativeImageGenerator generator, OptionValues parsedHostedOptions, Map> artifacts, boolean wasSuccessfulBuild) { + private static void createAdditionalArtifacts(String imageName, NativeImageGenerator generator, ImageClassLoader classLoader, Throwable error, OptionValues parsedHostedOptions) { + if (error != null) { + Path errorReportPath = NativeImageOptions.getErrorFilePath(); + ReportUtils.report("GraalVM Native Image Error Report", errorReportPath, p -> VMErrorReporter.generateErrorReport(p, classLoader, error), false); + BuildArtifacts.singleton().add(ArtifactType.BUILD_INFO, errorReportPath); + } + if (generator.getBigbang() != null && ImageBuildStatistics.Options.CollectImageBuildStatistics.getValue(parsedHostedOptions)) { + BuildArtifacts.singleton().add(ArtifactType.BUILD_INFO, reportImageBuildStatistics(imageName, generator.getBigbang())); + } + } + + private void printErrorMessage(Throwable error) { + if (error == null) { + return; + } + l().println(); + l().redBold().a("The build process encountered an unexpected error:").reset().println(); + if (NativeImageOptions.ReportExceptionStackTraces.getValue()) { + l().dim().println(); + error.printStackTrace(builderIO.getOut()); + l().reset().println(); + } else { + l().println(); + l().dim().a("> %s", error).reset().println(); + l().println(); + l().a("Please inspect the generated error report at:").println(); + l().link(NativeImageOptions.getErrorFilePath()).println(); + l().println(); + l().a("If you are unable to resolve this problem, please file an issue with the error report at:").println(); + var supportURL = ImageSingletons.lookup(VM.class).supportURL; + l().link(supportURL, supportURL).println(); + } + } + + private void printArtifacts(Map> artifacts) { + if (artifacts.isEmpty()) { + return; + } + l().printLineSeparator(); l().yellowBold().a("Produced artifacts:").reset().println(); // Use TreeMap to sort paths alphabetically. Map> pathToTypes = new TreeMap<>(); @@ -647,15 +698,6 @@ private void printArtifacts(String imageName, NativeImageGenerator generator, Op pathToTypes.computeIfAbsent(path, p -> new ArrayList<>()).add(artifactType.name().toLowerCase()); } }); - if (jsonHelper != null && wasSuccessfulBuild) { - Path jsonMetric = jsonHelper.printToFile(); - pathToTypes.computeIfAbsent(jsonMetric, p -> new ArrayList<>()).add("json"); - } - if (generator.getBigbang() != null && ImageBuildStatistics.Options.CollectImageBuildStatistics.getValue(parsedHostedOptions)) { - Path buildStatisticsPath = reportImageBuildStatistics(imageName, generator.getBigbang()); - pathToTypes.computeIfAbsent(buildStatisticsPath, p -> new ArrayList<>()).add("raw"); - } - pathToTypes.computeIfAbsent(reportBuildArtifacts(imageName, artifacts), p -> new ArrayList<>()).add("txt"); pathToTypes.forEach((path, typeNames) -> { l().a(" ").link(path).dim().a(" (").a(String.join(", ", typeNames)).a(")").reset().println(); }); @@ -674,9 +716,7 @@ private static Path reportImageBuildStatistics(String imageName, BigBang bb) { } } - private static Path reportBuildArtifacts(String imageName, Map> buildArtifacts) { - Path buildDir = NativeImageGenerator.generatedFiles(HostedOptionValues.singleton()); - + private static Path reportBuildArtifacts(Path buildDir, String imageName, Map> buildArtifacts) { Consumer writerConsumer = writer -> buildArtifacts.forEach((artifactType, paths) -> { writer.println("[" + artifactType + "]"); if (artifactType == BuildArtifacts.ArtifactType.JDK_LIB_SHIM) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/VMFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/VMFeature.java index b715c82e831f..39604a178bd5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/VMFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/VMFeature.java @@ -59,7 +59,7 @@ public void afterRegistration(AfterRegistrationAccess access) { protected VM createVMSingletonValue() { String config = System.getProperty("org.graalvm.config", "CE"); - return new VM(config); + return new VM(config, "https://graalvm.org/native-image/bug-report/"); } @Override 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 65d48c24611d..bc7076bf2ea7 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,11 +24,8 @@ */ 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; @@ -50,6 +47,7 @@ import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.ProgressReporter; import com.oracle.svm.hosted.image.sources.SourceManager; +import com.oracle.svm.hosted.util.DiagnosticUtils; @AutomaticallyRegisteredFeature @SuppressWarnings("unused") @@ -121,19 +119,10 @@ public boolean isLoadable() { }; 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)); + objectFile.newUserDefinedSection(".debug.svm.imagebuild.classpath", makeSectionImpl.apply(DiagnosticUtils.getClassPath(imageClassLoader))); + objectFile.newUserDefinedSection(".debug.svm.imagebuild.modulepath", makeSectionImpl.apply(DiagnosticUtils.getModulePath(imageClassLoader))); + objectFile.newUserDefinedSection(".debug.svm.imagebuild.arguments", makeSectionImpl.apply(DiagnosticUtils.getBuilderArguments(imageClassLoader))); + objectFile.newUserDefinedSection(".debug.svm.imagebuild.java.properties", makeSectionImpl.apply(DiagnosticUtils.getBuilderProperties())); } } ProgressReporter.singleton().setDebugInfoTimer(timer); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/util/DiagnosticUtils.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/util/DiagnosticUtils.java new file mode 100644 index 000000000000..33035a65b94a --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/util/DiagnosticUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022, 2022, 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.svm.hosted.util; + +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; + +import com.oracle.svm.hosted.ImageClassLoader; + +public class DiagnosticUtils { + public static List getClassPath(ImageClassLoader imageClassLoader) { + return imageClassLoader.classpath().stream().map(Path::toString).collect(Collectors.toList()); + } + + public static List getModulePath(ImageClassLoader imageClassLoader) { + return imageClassLoader.modulepath().stream().map(Path::toString).collect(Collectors.toList()); + } + + /* Get original arguments that got passed to the builder when it got started. */ + public static List getBuilderArguments(ImageClassLoader imageClassLoader) { + return imageClassLoader.classLoaderSupport.getHostedOptionParser().getArguments(); + } + + /* Get system properties that got passed to the VM that runs the builder. */ + public static List getBuilderProperties() { + return ManagementFactory.getRuntimeMXBean().getInputArguments().stream() + .filter(arg -> arg.startsWith("-D")) + .sorted().collect(Collectors.toList()); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/util/VMErrorReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/util/VMErrorReporter.java new file mode 100644 index 000000000000..5084c5a2b76a --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/util/VMErrorReporter.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2022, 2022, 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.svm.hosted.util; + +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.VM; +import com.oracle.svm.hosted.ImageClassLoader; +import com.oracle.svm.hosted.c.codegen.CCompilerInvoker; + +@SuppressWarnings("try") +public final class VMErrorReporter { + + public static void generateErrorReport(PrintWriter pw, ImageClassLoader classLoader, Throwable t) { + pw.println("# GraalVM Native Image Error Report"); + reportStackTrace(t, pw); + reportBuilderSetup(pw, classLoader); + reportGraalVMSetup(pw); + } + + private static void reportStackTrace(Throwable t, PrintWriter pw) { + pw.println("## Stack Trace"); + pw.println("```java"); + t.printStackTrace(pw); + pw.println("```"); + } + + private static void reportBuilderSetup(PrintWriter pw, ImageClassLoader classLoader) { + pw.println("## Builder Setup"); + try (DetailsPrinter p = new DetailsPrinter(pw, "Class path")) { + for (String entry : DiagnosticUtils.getClassPath(classLoader)) { + pw.println(entry); + } + } + try (DetailsPrinter p = new DetailsPrinter(pw, "Module path")) { + for (String entry : DiagnosticUtils.getModulePath(classLoader)) { + pw.println(entry); + } + } + try (DetailsPrinter p = new DetailsPrinter(pw, "Builder arguments")) { + for (String entry : DiagnosticUtils.getBuilderArguments(classLoader)) { + pw.println(entry); + } + } + try (DetailsPrinter p = new DetailsPrinter(pw, "Builder properties")) { + for (String entry : DiagnosticUtils.getBuilderProperties()) { + pw.println(entry); + } + } + } + + private static void reportGraalVMSetup(PrintWriter pw) { + pw.println("## GraalVM Setup"); + String version = ImageSingletons.lookup(VM.class).version; + String javaVersion = System.getProperty("java.runtime.version"); + pw.println("| Name | Value |"); + pw.println("| ---- | ----- |"); + pw.printf("| GraalVM version | `%s` |%n", version); + pw.printf("| Java version | `%s` |%n", javaVersion); + if (ImageSingletons.contains(CCompilerInvoker.class)) { + pw.printf("| C compiler | `%s` |%n", ImageSingletons.lookup(CCompilerInvoker.class).compilerInfo.getShortDescription()); + } + + String releaseContent = getReleaseFileContent(); + if (releaseContent != null) { + try (DetailsPrinter d = new DetailsPrinter(pw, "GraalVM release file")) { + pw.println(releaseContent); + } + } + } + + private static String getReleaseFileContent() { + Path releaseFile = Path.of(System.getProperty("java.home")).resolve("release"); + if (Files.exists(releaseFile)) { + try { + return Files.readString(releaseFile); + } catch (IOException e) { + return null; + } + } + return null; + } + + private static final class DetailsPrinter implements AutoCloseable { + private final PrintWriter pw; + + private DetailsPrinter(PrintWriter pw, String title) { + this.pw = pw; + pw.printf("%n
%n%s%n%n```%n", title); + } + + @Override + public void close() { + pw.printf("```%n%n
%n%n"); + } + } +} From 3fb4562ec31ca538dce716e67e5388c3813dceff Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Fri, 11 Nov 2022 11:16:35 +0100 Subject: [PATCH 2/2] Use `-H:+ReportExceptionStackTraces` internally. --- sdk/mx.sdk/mx_sdk_vm_impl.py | 1 + substratevm/mx.substratevm/mx_substratevm.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/mx.sdk/mx_sdk_vm_impl.py b/sdk/mx.sdk/mx_sdk_vm_impl.py index 6b252c01c05f..8fba371468b4 100644 --- a/sdk/mx.sdk/mx_sdk_vm_impl.py +++ b/sdk/mx.sdk/mx_sdk_vm_impl.py @@ -1277,6 +1277,7 @@ def contents(self): '--install-exit-handlers', '--enable-monitoring', '-H:+DumpRuntimeCompilationOnSignal', + '-H:+ReportExceptionStackTraces', ] if isinstance(image_config, (mx_sdk.LauncherConfig, mx_sdk.LanguageLibraryConfig)): diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 0bea4779dcc5..b3c00572c88b 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -249,7 +249,7 @@ def run_musl_basic_tests(): @contextmanager def native_image_context(common_args=None, hosted_assertions=True, native_image_cmd='', config=None, build_if_missing=False): common_args = [] if common_args is None else common_args - base_args = ['--no-fallback', '-H:+EnforceMaxRuntimeCompileMethods'] + base_args = ['--no-fallback', '-H:+EnforceMaxRuntimeCompileMethods', '-H:+ReportExceptionStackTraces'] base_args += ['-H:Path=' + svmbuild_dir()] if mx.get_opts().verbose: base_args += ['--verbose']