Skip to content

Commit e922d61

Browse files
committed
[GR-41912] More user-friendly reporting of internal errors.
PullRequest: graal/13151
2 parents f4e1fe1 + a8f2e25 commit e922d61

19 files changed

+347
-64
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
# This template is linked from https://graalvm.org/native-image/error-report
3+
name: "\U0001F6A8 Share Native Image error report"
4+
about: Share an error report from Native Image. To report a security vulnerability, please see below or the SECURITY.md file at the root of the repository. Do not open a GitHub issue.
5+
labels: bug, native-image
6+
7+
---
8+
9+
Replace this text with the error report (`svm_err_b_*.md`) generated by the Native Image build process.
10+
Please make sure the report does not contain any sensitive information.

.github/ISSUE_TEMPLATE/1_issues_native_image.md renamed to .github/ISSUE_TEMPLATE/2_issues_native_image_other.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
name: "\U0001F6A8 Native Image Issue Report"
3-
about: Create a report for a failure with Native Image. To report a security vulnerability, please see below or the SECURITY.md file at the root of the repository. Do not open a GitHub issue.
2+
name: "\U0001F6A8 Other Native Image Issues"
3+
about: File an issue for a problem with Native Image. To report a security vulnerability, please see below or the SECURITY.md file at the root of the repository. Do not open a GitHub issue.
44
title: ''
55
labels: bug, native-image
66
assignees: ''

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ blank_issues_enabled: false
22
contact_links:
33
- name: Need help or have a question?
44
url: https://www.graalvm.org/slack-invitation/
5-
about: Visit our slack channel.
5+
about: Join the GraalVM Slack channel.

sdk/mx.sdk/mx_sdk_vm_impl.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,6 +1273,7 @@ def contents(self):
12731273
'--install-exit-handlers',
12741274
'--enable-monitoring',
12751275
'-H:+DumpRuntimeCompilationOnSignal',
1276+
'-H:+ReportExceptionStackTraces',
12761277
]
12771278

12781279
if isinstance(image_config, (mx_sdk.LauncherConfig, mx_sdk.LanguageLibraryConfig)):

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This changelog summarizes major changes to GraalVM Native Image.
1212
* (GR-42375) Add `-H:±GenerateBuildArtifactsFile` option, which generates a `build-artifacts.json` file with a list of all artifacts produced by Native Image. `.build_artifacts.txt` files are now deprecated, disabled (can be re-enabled with env setting `NATIVE_IMAGE_DEPRECATED_BUILD_ARTIFACTS_TXT=true`), and will be removed in a future release.
1313
* (GR-34179) Improved debugging support on Windows: Debug information now includes information about Java types (contributed by Red Hat).
1414
* (GR-41096) Support services loaded through the `java.util.ServiceLoader.ModuleServicesLookupIterator`. An example of such service is the `com.sun.jndi.rmi.registry.RegistryContextFactory`.
15+
* (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_b_<timestamp>_pid<pid>.md` pattern and are created in the working directory. Use `-H:ErrorFile` to adjust the path or filename.
1516

1617
## Version 22.3.0
1718
* (GR-35721) Remove old build output style and the `-H:±BuildOutputUseNewStyle` option.

substratevm/mx.substratevm/mx_substratevm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def vm_executable_path(executable, config=None):
236236
@contextmanager
237237
def native_image_context(common_args=None, hosted_assertions=True, native_image_cmd='', config=None, build_if_missing=False):
238238
common_args = [] if common_args is None else common_args
239-
base_args = ['--no-fallback', '-H:+EnforceMaxRuntimeCompileMethods']
239+
base_args = ['--no-fallback', '-H:+EnforceMaxRuntimeCompileMethods', '-H:+ReportExceptionStackTraces']
240240
base_args += ['-H:Path=' + svmbuild_dir()]
241241
if mx.get_opts().verbose:
242242
base_args += ['--verbose']

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VM.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -27,25 +27,22 @@
2727
import org.graalvm.nativeimage.Platform;
2828
import org.graalvm.nativeimage.Platforms;
2929

30-
import com.oracle.svm.core.util.VMError;
31-
3230
public final class VM {
3331

3432
public final String version;
3533
public final String vendor;
3634
public final String vendorUrl;
3735
public final String runtimeName;
36+
public final String supportURL;
3837

3938
@Platforms(Platform.HOSTED_ONLY.class)
40-
public VM(String config) {
41-
String versionStr = System.getProperty("org.graalvm.version");
42-
VMError.guarantee(versionStr != null);
43-
versionStr = "GraalVM " + versionStr;
44-
versionStr += " Java " + Runtime.version().toString();
45-
versionStr += " " + config;
46-
version = versionStr;
39+
public VM() {
40+
String versionStr = System.getProperty("org.graalvm.version", "Unknown Version");
41+
String edition = System.getProperty("org.graalvm.config", "CE");
42+
version = String.format("GraalVM %s Java %s %s", versionStr, Runtime.version(), edition);
4743
vendor = System.getProperty("org.graalvm.vendor", "Oracle Corporation");
4844
vendorUrl = System.getProperty("org.graalvm.vendorurl", "https://www.graalvm.org/");
4945
runtimeName = System.getProperty("java.runtime.name", "Unknown Runtime Environment");
46+
supportURL = System.getProperty("org.graalvm.supporturl", "https://graalvm.org/native-image/error-report/");
5047
}
5148
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
*/
2525
package com.oracle.svm.hosted;
2626

27+
import java.io.PrintWriter;
2728
import java.util.ArrayList;
29+
import java.util.Comparator;
2830
import java.util.HashMap;
2931
import java.util.HashSet;
3032
import java.util.LinkedHashSet;
@@ -178,14 +180,7 @@ public void registerFeatures(ImageClassLoader loader, DebugContext debug) {
178180
if (NativeImageOptions.PrintFeatures.getValue()) {
179181
ReportUtils.report("feature information", SubstrateOptions.reportsPath(), "feature_info", "csv", out -> {
180182
out.println("Feature, Required Features");
181-
for (Feature featureInstance : featureInstances) {
182-
out.print(featureInstance.getClass().getTypeName());
183-
String requiredFeaturesString = featureInstance.getRequiredFeatures().stream()
184-
.map(Class::getTypeName)
185-
.collect(Collectors.joining(" ", "[", "]"));
186-
out.print(", ");
187-
out.println(requiredFeaturesString);
188-
}
183+
dumpAllFeatures(out);
189184
});
190185
}
191186
}
@@ -248,4 +243,15 @@ public List<Feature> getUserSpecificFeatures() {
248243
(classLoaderSupport.isNativeImageClassLoader(f.getClass().getClassLoader()) || userEnabledFeatures.contains(f.getClass().getName())))
249244
.collect(Collectors.toList());
250245
}
246+
247+
public void dumpAllFeatures(PrintWriter out) {
248+
featureInstances.stream().sorted(Comparator.comparing(f -> f.getClass().getTypeName())).forEachOrdered(f -> {
249+
out.print(f.getClass().getTypeName());
250+
String requiredFeaturesString = f.getRequiredFeatures().stream()
251+
.map(Class::getTypeName)
252+
.collect(Collectors.joining(" ", "[", "]"));
253+
out.print(", ");
254+
out.println(requiredFeaturesString);
255+
});
256+
}
251257
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ private int buildImage(ImageClassLoader classLoader) {
287287
ForkJoinPool compilationExecutor = null;
288288

289289
ProgressReporter reporter = new ProgressReporter(parsedHostedOptions);
290+
Throwable vmError = null;
290291
boolean wasSuccessfulBuild = false;
291292
try (StopTimer ignored = totalTimer.start()) {
292293
Timer classlistTimer = timerCollection.get(TimerCollection.Registry.CLASSLIST);
@@ -452,20 +453,18 @@ private int buildImage(ImageClassLoader classLoader) {
452453
}
453454
return ExitStatus.BUILDER_ERROR.getValue();
454455
} catch (Throwable e) {
455-
NativeImageGeneratorRunner.reportFatalError(e);
456+
vmError = e;
456457
return ExitStatus.BUILDER_ERROR.getValue();
457458
} finally {
458-
if (imageName != null && generator != null) {
459-
reportEpilog(imageName, reporter, wasSuccessfulBuild, parsedHostedOptions);
460-
}
459+
reportEpilog(imageName, reporter, classLoader, vmError, parsedHostedOptions);
461460
NativeImageGenerator.clearSystemPropertiesForImage();
462461
ImageSingletonsSupportImpl.HostedManagement.clear();
463462
}
464463
return ExitStatus.OK.getValue();
465464
}
466465

467-
protected void reportEpilog(String imageName, ProgressReporter reporter, boolean wasSuccessfulBuild, OptionValues parsedHostedOptions) {
468-
reporter.printEpilog(imageName, generator, wasSuccessfulBuild, parsedHostedOptions);
466+
protected void reportEpilog(String imageName, ProgressReporter reporter, ImageClassLoader classLoader, Throwable vmError, OptionValues parsedHostedOptions) {
467+
reporter.printEpilog(imageName, generator, generator.featureHandler, classLoader, vmError, parsedHostedOptions);
469468
}
470469

471470
protected NativeImageGenerator createImageGenerator(ImageClassLoader classLoader, HostedOptionParser optionParser, Pair<Method, CEntryPointData> mainEntryPointData, ProgressReporter reporter) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,24 @@
2727
import static org.graalvm.compiler.options.OptionType.Debug;
2828
import static org.graalvm.compiler.options.OptionType.User;
2929

30+
import java.nio.file.Path;
3031
import java.nio.file.Paths;
32+
import java.text.SimpleDateFormat;
3133
import java.util.Arrays;
34+
import java.util.Date;
3235

3336
import org.graalvm.collections.EconomicMap;
3437
import org.graalvm.compiler.options.Option;
3538
import org.graalvm.compiler.options.OptionKey;
3639
import org.graalvm.compiler.options.OptionValues;
40+
import org.graalvm.compiler.serviceprovider.GraalServices;
3741

3842
import com.oracle.graal.pointsto.reports.ReportUtils;
3943
import com.oracle.graal.pointsto.util.CompletionExecutor;
4044
import com.oracle.svm.core.SubstrateOptions;
4145
import com.oracle.svm.core.option.APIOption;
4246
import com.oracle.svm.core.option.HostedOptionKey;
47+
import com.oracle.svm.core.option.HostedOptionValues;
4348
import com.oracle.svm.core.option.LocatableMultiOptionValue;
4449
import com.oracle.svm.core.util.UserError;
4550
import com.oracle.svm.hosted.classinitialization.ClassInitializationOptions;
@@ -180,6 +185,26 @@ public static CStandards getCStandard() {
180185
@Option(help = "Print unsafe operation offset warnings.)")//
181186
public static final HostedOptionKey<Boolean> UnsafeOffsetWarningsAreFatal = new HostedOptionKey<>(false);
182187

188+
// Inspired by HotSpot's hs_err_<pid>.log files and for build-time errors (err_b).
189+
private static final String DEFAULT_ERROR_FILE_NAME = "svm_err_b_%t_pid%p.md";
190+
191+
public static final Path getErrorFilePath() {
192+
String errorFile = NativeImageOptions.ErrorFile.getValue();
193+
if (errorFile.isEmpty()) {
194+
return NativeImageGenerator.generatedFiles(HostedOptionValues.singleton()).resolve(expandErrorFile(DEFAULT_ERROR_FILE_NAME));
195+
} else {
196+
return Paths.get(expandErrorFile(errorFile));
197+
}
198+
}
199+
200+
private static String expandErrorFile(String errorFile) {
201+
String timestamp = new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSS").format(new Date(GraalServices.getGlobalTimeStamp()));
202+
return errorFile.replaceAll("%p", GraalServices.getExecutionID()).replaceAll("%t", timestamp);
203+
}
204+
205+
@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).)")//
206+
public static final HostedOptionKey<String> ErrorFile = new HostedOptionKey<>("");
207+
183208
@Option(help = "Show exception stack traces for exceptions during image building.)")//
184209
public static final HostedOptionKey<Boolean> ReportExceptionStackTraces = new HostedOptionKey<>(areAssertionsEnabled());
185210

@@ -199,6 +224,7 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean o
199224
SubstitutionReportFeature.Options.ReportPerformedSubstitutions.update(values, true);
200225
SubstrateOptions.DumpTargetInfo.update(values, true);
201226
PrintFeatures.update(values, true);
227+
ReportExceptionStackTraces.update(values, true);
202228
}
203229
}
204230
};

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@
7474
import com.oracle.svm.core.SubstrateOptions;
7575
import com.oracle.svm.core.VM;
7676
import com.oracle.svm.core.code.CodeInfoTable;
77+
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
7778
import com.oracle.svm.core.feature.InternalFeature;
7879
import com.oracle.svm.core.heap.Heap;
7980
import com.oracle.svm.core.jdk.Resources;
8081
import com.oracle.svm.core.jdk.resources.ResourceStorageEntry;
8182
import com.oracle.svm.core.meta.SubstrateObjectConstant;
8283
import com.oracle.svm.core.option.HostedOptionValues;
8384
import com.oracle.svm.core.reflect.ReflectionMetadataDecoder;
84-
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
8585
import com.oracle.svm.core.util.VMError;
8686
import com.oracle.svm.hosted.ProgressReporterJsonHelper.AnalysisResults;
8787
import com.oracle.svm.hosted.ProgressReporterJsonHelper.GeneralInfo;
@@ -94,6 +94,7 @@
9494
import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo;
9595
import com.oracle.svm.hosted.meta.HostedMetaAccess;
9696
import com.oracle.svm.hosted.reflect.ReflectionHostedSupport;
97+
import com.oracle.svm.hosted.util.VMErrorReporter;
9798
import com.oracle.svm.util.ImageBuildStatistics;
9899
import com.oracle.svm.util.ReflectionUtil;
99100

@@ -117,6 +118,7 @@ public class ProgressReporter {
117118

118119
private final ProgressReporterJsonHelper jsonHelper;
119120
private final DirectPrinter linePrinter = new DirectPrinter();
121+
private final StringBuilder buildOutputLog = new StringBuilder();
120122
private final StagePrinter<?> stagePrinter;
121123
private final ColorStrategy colorStrategy;
122124
private final LinkStrategy linkStrategy;
@@ -595,19 +597,28 @@ private void printBreakdowns() {
595597
.a(String.format("%9s for %s more object types", Utils.bytesToHuman(totalHeapBytes - printedHeapBytes), numHeapItems - printedHeapItems)).flushln();
596598
}
597599

598-
public void printEpilog(String imageName, NativeImageGenerator generator, boolean wasSuccessfulBuild, OptionValues parsedHostedOptions) {
600+
public void printEpilog(String imageName, NativeImageGenerator generator, FeatureHandler featureHandler, ImageClassLoader classLoader, Throwable error, OptionValues parsedHostedOptions) {
601+
executor.shutdown();
602+
603+
if (error != null) {
604+
Path errorReportPath = NativeImageOptions.getErrorFilePath();
605+
ReportUtils.report("GraalVM Native Image Error Report", errorReportPath, p -> VMErrorReporter.generateErrorReport(p, buildOutputLog, classLoader, featureHandler, error), false);
606+
BuildArtifacts.singleton().add(ArtifactType.BUILD_INFO, errorReportPath);
607+
}
608+
609+
if (imageName == null || generator == null) {
610+
printErrorMessage(error);
611+
return;
612+
}
613+
599614
l().printLineSeparator();
600615
printResourceStatistics();
601616

602617
double totalSeconds = Utils.millisToSeconds(getTimer(TimerCollection.Registry.TOTAL).getTotalTime());
603618
recordJsonMetric(ResourceUsageKey.TOTAL_SECS, totalSeconds);
604619

605-
createArtifacts(imageName, generator, parsedHostedOptions, wasSuccessfulBuild);
606-
Map<ArtifactType, List<Path>> artifacts = generator.getBuildArtifacts();
607-
if (!artifacts.isEmpty()) {
608-
l().printLineSeparator();
609-
printArtifacts(artifacts);
610-
}
620+
createAdditionalArtifacts(imageName, generator, error, parsedHostedOptions);
621+
printArtifacts(generator.getBuildArtifacts());
611622

612623
l().printHeadlineSeparator();
613624

@@ -617,14 +628,38 @@ public void printEpilog(String imageName, NativeImageGenerator generator, boolea
617628
} else {
618629
timeStats = String.format("%dm %ds", (int) totalSeconds / 60, (int) totalSeconds % 60);
619630
}
620-
l().a(wasSuccessfulBuild ? "Finished" : "Failed").a(" generating '").bold().a(imageName).reset().a("' ")
621-
.a(wasSuccessfulBuild ? "in" : "after").a(" ").a(timeStats).a(".").println();
622-
executor.shutdown();
631+
l().a(error == null ? "Finished" : "Failed").a(" generating '").bold().a(imageName).reset().a("' ")
632+
.a(error == null ? "in" : "after").a(" ").a(timeStats).a(".").println();
633+
634+
printErrorMessage(error);
623635
}
624636

625-
private void createArtifacts(String imageName, NativeImageGenerator generator, OptionValues parsedHostedOptions, boolean wasSuccessfulBuild) {
637+
private void printErrorMessage(Throwable error) {
638+
if (error == null) {
639+
return;
640+
}
641+
l().println();
642+
l().redBold().a("The build process encountered an unexpected error:").reset().println();
643+
if (NativeImageOptions.ReportExceptionStackTraces.getValue()) {
644+
l().dim().println();
645+
error.printStackTrace(builderIO.getOut());
646+
l().reset().println();
647+
} else {
648+
l().println();
649+
l().dim().a("> %s", error).reset().println();
650+
l().println();
651+
l().a("Please inspect the generated error report at:").println();
652+
l().link(NativeImageOptions.getErrorFilePath()).println();
653+
l().println();
654+
l().a("If you are unable to resolve this problem, please file an issue with the error report at:").println();
655+
var supportURL = ImageSingletons.lookup(VM.class).supportURL;
656+
l().link(supportURL, supportURL).println();
657+
}
658+
}
659+
660+
private void createAdditionalArtifacts(String imageName, NativeImageGenerator generator, Throwable error, OptionValues parsedHostedOptions) {
626661
BuildArtifacts artifacts = BuildArtifacts.singleton();
627-
if (jsonHelper != null && wasSuccessfulBuild) {
662+
if (error == null && jsonHelper != null) {
628663
artifacts.add(ArtifactType.BUILD_INFO, jsonHelper.printToFile());
629664
}
630665
if (generator.getBigbang() != null && ImageBuildStatistics.Options.CollectImageBuildStatistics.getValue(parsedHostedOptions)) {
@@ -634,6 +669,10 @@ private void createArtifacts(String imageName, NativeImageGenerator generator, O
634669
}
635670

636671
private void printArtifacts(Map<ArtifactType, List<Path>> artifacts) {
672+
if (artifacts.isEmpty()) {
673+
return;
674+
}
675+
l().printLineSeparator();
637676
l().yellowBold().a("Produced artifacts:").reset().println();
638677
// Use TreeMap to sort paths alphabetically.
639678
Map<Path, List<String>> pathToTypes = new TreeMap<>();
@@ -857,14 +896,17 @@ public void close() {
857896

858897
private void print(char text) {
859898
builderIO.getOut().print(text);
899+
buildOutputLog.append(text);
860900
}
861901

862902
private void print(String text) {
863903
builderIO.getOut().print(text);
904+
buildOutputLog.append(text);
864905
}
865906

866907
private void println() {
867908
builderIO.getOut().println();
909+
buildOutputLog.append(System.lineSeparator());
868910
}
869911

870912
/*
@@ -1361,23 +1403,30 @@ public String asDocLink(String text, String htmlAnchor) {
13611403
}
13621404
}
13631405

1364-
private static class ANSI {
1406+
public static class ANSI {
13651407
static final String ESCAPE = "\033";
13661408
static final String RESET = ESCAPE + "[0m";
13671409
static final String BOLD = ESCAPE + "[1m";
13681410
static final String DIM = ESCAPE + "[2m";
1411+
static final String STRIP_COLORS = "\033\\[[;\\d]*m";
13691412

13701413
static final String LINK_START = ESCAPE + "]8;;";
13711414
static final String LINK_TEXT = ESCAPE + "\\";
13721415
static final String LINK_END = LINK_START + LINK_TEXT;
13731416
static final String LINK_FORMAT = LINK_START + "%s" + LINK_TEXT + "%s" + LINK_END;
1417+
static final String STRIP_LINKS = "\033]8;;https://\\S+\033\\\\([^\033]*)\033]8;;\033\\\\";
13741418

13751419
static final String BLUE = ESCAPE + "[0;34m";
13761420

13771421
static final String RED_BOLD = ESCAPE + "[1;31m";
13781422
static final String YELLOW_BOLD = ESCAPE + "[1;33m";
13791423
static final String BLUE_BOLD = ESCAPE + "[1;34m";
13801424
static final String MAGENTA_BOLD = ESCAPE + "[1;35m";
1425+
1426+
/* Strip all ANSI codes emitted by this class. */
1427+
public static String strip(String string) {
1428+
return string.replaceAll(STRIP_COLORS, "").replaceAll(STRIP_LINKS, "$1");
1429+
}
13811430
}
13821431
}
13831432

0 commit comments

Comments
 (0)