Skip to content

[GR-41912] More user-friendly reporting of internal errors. #5414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
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 sdk/mx.sdk/mx_sdk_vm_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)):
Expand Down
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_<timestamp>_pid<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.
Expand Down
2 changes: 1 addition & 1 deletion substratevm/mx.substratevm/mx_substratevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ enum ArtifactType {
HEADER,
IMPORT_LIB,
DEBUG_INFO,
BUILD_INFO,
}

static BuildArtifacts singleton() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -180,6 +185,25 @@ public static CStandards getCStandard() {
@Option(help = "Print unsafe operation offset warnings.)")//
public static final HostedOptionKey<Boolean> 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<String> ErrorFile = new HostedOptionKey<>("");

@Option(help = "Show exception stack traces for exceptions during image building.)")//
public static final HostedOptionKey<Boolean> ReportExceptionStackTraces = new HostedOptionKey<>(areAssertionsEnabled());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@
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;
import com.oracle.svm.core.jdk.resources.ResourceStorageEntry;
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;
Expand All @@ -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;

Expand Down Expand Up @@ -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<ArtifactType, List<Path>> 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();

Expand All @@ -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<ArtifactType, List<Path>> 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<ArtifactType, List<Path>> artifacts) {
if (artifacts.isEmpty()) {
return;
}
l().printLineSeparator();
l().yellowBold().a("Produced artifacts:").reset().println();
// Use TreeMap to sort paths alphabetically.
Map<Path, List<String>> pathToTypes = new TreeMap<>();
Expand All @@ -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();
});
Expand All @@ -674,9 +716,7 @@ private static Path reportImageBuildStatistics(String imageName, BigBang bb) {
}
}

private static Path reportBuildArtifacts(String imageName, Map<ArtifactType, List<Path>> buildArtifacts) {
Path buildDir = NativeImageGenerator.generatedFiles(HostedOptionValues.singleton());

private static Path reportBuildArtifacts(Path buildDir, String imageName, Map<ArtifactType, List<Path>> buildArtifacts) {
Consumer<PrintWriter> writerConsumer = writer -> buildArtifacts.forEach((artifactType, paths) -> {
writer.println("[" + artifactType + "]");
if (artifactType == BuildArtifacts.ArtifactType.JDK_LIB_SHIM) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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")
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> getClassPath(ImageClassLoader imageClassLoader) {
return imageClassLoader.classpath().stream().map(Path::toString).collect(Collectors.toList());
}

public static List<String> 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<String> getBuilderArguments(ImageClassLoader imageClassLoader) {
return imageClassLoader.classLoaderSupport.getHostedOptionParser().getArguments();
}

/* Get system properties that got passed to the VM that runs the builder. */
public static List<String> getBuilderProperties() {
return ManagementFactory.getRuntimeMXBean().getInputArguments().stream()
.filter(arg -> arg.startsWith("-D"))
.sorted().collect(Collectors.toList());
}
}
Loading