Skip to content

Commit 7ca21b6

Browse files
author
Christian Wimmer
committed
Optimize stack trace information for implicit exceptions
1 parent ea3bb50 commit 7ca21b6

File tree

5 files changed

+409
-5
lines changed

5 files changed

+409
-5
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/gen/DebugInfoBuilder.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@
2828
import java.util.Arrays;
2929
import java.util.Queue;
3030

31-
import jdk.graal.compiler.debug.CounterKey;
32-
import jdk.graal.compiler.debug.DebugContext;
33-
import jdk.graal.compiler.debug.GraalError;
3431
import org.graalvm.collections.EconomicMap;
3532
import org.graalvm.collections.Equivalence;
33+
3634
import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider;
35+
import jdk.graal.compiler.debug.Assertions;
36+
import jdk.graal.compiler.debug.CounterKey;
37+
import jdk.graal.compiler.debug.DebugContext;
38+
import jdk.graal.compiler.debug.GraalError;
3739
import jdk.graal.compiler.lir.ConstantValue;
3840
import jdk.graal.compiler.lir.ImplicitLIRFrameState;
3941
import jdk.graal.compiler.lir.LIRFrameState;
@@ -49,8 +51,6 @@
4951
import jdk.graal.compiler.nodes.virtual.VirtualBoxingNode;
5052
import jdk.graal.compiler.nodes.virtual.VirtualObjectNode;
5153
import jdk.graal.compiler.nodes.virtual.VirtualObjectState;
52-
import jdk.graal.compiler.debug.Assertions;
53-
5454
import jdk.vm.ci.code.BytecodeFrame;
5555
import jdk.vm.ci.code.RegisterValue;
5656
import jdk.vm.ci.code.VirtualObject;
@@ -269,6 +269,7 @@ protected BytecodeFrame computeFrameForState(NodeWithState node, FrameState stat
269269
assert state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI || state.locksSize() == 0 : Assertions.errorMessageContext("node", node, "state", state);
270270

271271
assert !(state.getMethod().isSynchronized() && state.bci != BytecodeFrame.BEFORE_BCI && state.bci != BytecodeFrame.AFTER_BCI && state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI) ||
272+
!state.isValidForDeoptimization() ||
272273
state.locksSize() > 0 : Assertions.errorMessageContext("state", state, "node", node, "bci", state.bci);
273274
assert state.verify();
274275

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ This changelog summarizes major changes to GraalVM Native Image.
1616
* (GR-47832) Experimental support for upcalls from foreign code and other improvements to our implementation of the [Foreign Function & Memory API](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/ForeignInterface.md) (part of "Project Panama", [JEP 454](https://openjdk.org/jeps/454)) on AMD64. Must be enabled with `-H:+ForeignAPISupport` (requiring `-H:+UnlockExperimentalVMOptions`).
1717
* (GR-52314) `-XX:MissingRegistrationReportingMode` can now be used on program invocation instead of as a build option, to avoid a rebuild when debugging missing registration errors.
1818
* (GR-51086) Introduce a new `--static-nolibc` API option as a replacement for the experimental `-H:±StaticExecutableWithDynamicLibC` option.
19+
* (GR-52732) Introduce a new `ReduceImplicitExceptionStackTraceInformation` hosted option that reduces image size by reducing the runtime metadata for implicit exceptions, at the cost of stack trace precision. The option is diabled by default, but enabled with optimization level 3 and profile guided optimizations.
1920
* (GR-52534) Change the digest (used e.g. for symbol names) from SHA-1 encoded as a hex string (40 bytes) to 128-bit Murmur3 as a Base-62 string (22 bytes).
2021
* (GR-52578) Print information about embedded resources into `embedded-resources.json` using the `-H:+GenerateEmbeddedResourcesFile` option.
2122
* (GR-51172) Add support to catch OutOfMemoryError exceptions on native image if there is no memory left.

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,14 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, String ol
278278
SubstrateOptions.IncludeNodeSourcePositions.update(values, newLevel == OptimizationLevel.O0);
279279
SubstrateOptions.SourceLevelDebug.update(values, newLevel == OptimizationLevel.O0);
280280
SubstrateOptions.AOTTrivialInline.update(values, newLevel != OptimizationLevel.O0);
281+
282+
/*
283+
* We do not want to enable this optimization yet by default, because it reduces the
284+
* precision of implicit stack traces. But for optimized release builds, including pgo
285+
* builds, it is a valuable image size reduction.
286+
*/
287+
SubstrateOptions.ReduceImplicitExceptionStackTraceInformation.update(values, newLevel == OptimizationLevel.O3);
288+
281289
GraalOptions.OptimizeLongJumps.update(values, !newLevel.isOneOf(OptimizationLevel.O0, OptimizationLevel.BUILD_TIME));
282290
if (optimizeValueUpdateHandler != null) {
283291
optimizeValueUpdateHandler.onValueUpdate(values, newLevel);
@@ -1152,4 +1160,8 @@ public static class TruffleStableOptions {
11521160
"If there is no native-image-resources.filelist file in the language home directory or the file is empty, then no resources are copied.", type = User, stability = OptionStability.STABLE)//
11531161
public static final HostedOptionKey<Boolean> CopyLanguageResources = new HostedOptionKey<>(true);
11541162
}
1163+
1164+
@Option(help = "Reduce the amount of metadata in the image for implicit exceptions by removing inlining information from the stack trace. " +
1165+
"This makes the image smaller, but also the stack trace of implicit exceptions less precise.", type = OptionType.Expert)//
1166+
public static final HostedOptionKey<Boolean> ReduceImplicitExceptionStackTraceInformation = new HostedOptionKey<>(false);
11551167
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ImplicitExceptions.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.oracle.svm.core.heap.RestrictHeapAccess;
3636
import com.oracle.svm.core.jdk.InternalVMMethod;
3737
import com.oracle.svm.core.jdk.StackTraceUtils;
38+
import com.oracle.svm.core.option.SubstrateOptionsParser;
3839
import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor;
3940
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
4041
import com.oracle.svm.core.threadlocal.FastThreadLocalInt;
@@ -106,6 +107,20 @@ public class ImplicitExceptions {
106107
public static final SubstrateForeignCallDescriptor THROW_NEW_ASSERTION_ERROR_NULLARY = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwNewAssertionErrorNullary", NO_SIDE_EFFECT);
107108
public static final SubstrateForeignCallDescriptor THROW_NEW_ASSERTION_ERROR_OBJECT = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwNewAssertionErrorObject", NO_SIDE_EFFECT);
108109

110+
public static final SubstrateForeignCallDescriptor CREATE_OPT_NULL_POINTER_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "createOptNullPointerException", HAS_SIDE_EFFECT);
111+
public static final SubstrateForeignCallDescriptor CREATE_OPT_OUT_OF_BOUNDS_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "createOptOutOfBoundsException", HAS_SIDE_EFFECT);
112+
public static final SubstrateForeignCallDescriptor CREATE_OPT_CLASS_CAST_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "createOptClassCastException", HAS_SIDE_EFFECT);
113+
public static final SubstrateForeignCallDescriptor CREATE_OPT_ARRAY_STORE_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "createOptArrayStoreException", HAS_SIDE_EFFECT);
114+
public static final SubstrateForeignCallDescriptor CREATE_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "createOptIncompatibleClassChangeError",
115+
HAS_SIDE_EFFECT);
116+
117+
public static final SubstrateForeignCallDescriptor THROW_OPT_NULL_POINTER_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwOptNullPointerException", NO_SIDE_EFFECT);
118+
public static final SubstrateForeignCallDescriptor THROW_OPT_OUT_OF_BOUNDS_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwOptOutOfBoundsException", NO_SIDE_EFFECT);
119+
public static final SubstrateForeignCallDescriptor THROW_OPT_CLASS_CAST_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwOptClassCastException", NO_SIDE_EFFECT);
120+
public static final SubstrateForeignCallDescriptor THROW_OPT_ARRAY_STORE_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwOptArrayStoreException", NO_SIDE_EFFECT);
121+
public static final SubstrateForeignCallDescriptor THROW_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwOptIncompatibleClassChangeError",
122+
NO_SIDE_EFFECT);
123+
109124
public static final SubstrateForeignCallDescriptor GET_CACHED_NULL_POINTER_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "getCachedNullPointerException", HAS_SIDE_EFFECT);
110125
public static final SubstrateForeignCallDescriptor GET_CACHED_OUT_OF_BOUNDS_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "getCachedOutOfBoundsException", HAS_SIDE_EFFECT);
111126
public static final SubstrateForeignCallDescriptor GET_CACHED_CLASS_CAST_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "getCachedClassCastException", HAS_SIDE_EFFECT);
@@ -150,6 +165,9 @@ public class ImplicitExceptions {
150165
THROW_CACHED_NULL_POINTER_EXCEPTION, THROW_CACHED_OUT_OF_BOUNDS_EXCEPTION, THROW_CACHED_CLASS_CAST_EXCEPTION, THROW_CACHED_ARRAY_STORE_EXCEPTION,
151166
THROW_CACHED_INCOMPATIBLE_CLASS_CHANGE_ERROR,
152167
THROW_CACHED_ILLEGAL_ARGUMENT_EXCEPTION, THROW_CACHED_NEGATIVE_ARRAY_SIZE_EXCEPTION, THROW_CACHED_ARITHMETIC_EXCEPTION, THROW_CACHED_ASSERTION_ERROR,
168+
CREATE_OPT_NULL_POINTER_EXCEPTION, CREATE_OPT_OUT_OF_BOUNDS_EXCEPTION, CREATE_OPT_CLASS_CAST_EXCEPTION, CREATE_OPT_ARRAY_STORE_EXCEPTION,
169+
CREATE_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR,
170+
THROW_OPT_NULL_POINTER_EXCEPTION, THROW_OPT_OUT_OF_BOUNDS_EXCEPTION, THROW_OPT_CLASS_CAST_EXCEPTION, THROW_OPT_ARRAY_STORE_EXCEPTION, THROW_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR
153171
};
154172

155173
private static final FastThreadLocalInt implicitExceptionsAreFatal = FastThreadLocalFactory.createInt("ImplicitExceptions.implicitExceptionsAreFatal");
@@ -406,6 +424,79 @@ private static void throwNewAssertionErrorObject(Object detailMessage) {
406424
throw new AssertionError(detailMessage);
407425
}
408426

427+
private static final String IMPRECISE_STACK_MSG = "Stack trace is imprecise, the top frames are missing and/or have wrong line numbers. To get precise stack traces, build the image with option " +
428+
SubstrateOptionsParser.commandArgument(SubstrateOptions.ReduceImplicitExceptionStackTraceInformation, "-");
429+
430+
/** Foreign call: {@link #CREATE_OPT_NULL_POINTER_EXCEPTION}. */
431+
@SubstrateForeignCallTarget(stubCallingConvention = true)
432+
private static NullPointerException createOptNullPointerException() {
433+
vmErrorIfImplicitExceptionsAreFatal(false);
434+
return new NullPointerException(IMPRECISE_STACK_MSG);
435+
}
436+
437+
/** Foreign call: {@link #CREATE_OPT_OUT_OF_BOUNDS_EXCEPTION}. */
438+
@SubstrateForeignCallTarget(stubCallingConvention = true)
439+
private static ArrayIndexOutOfBoundsException createOptOutOfBoundsException() {
440+
vmErrorIfImplicitExceptionsAreFatal(false);
441+
return new ArrayIndexOutOfBoundsException(IMPRECISE_STACK_MSG);
442+
}
443+
444+
/** Foreign call: {@link #CREATE_OPT_CLASS_CAST_EXCEPTION}. */
445+
@SubstrateForeignCallTarget(stubCallingConvention = true)
446+
private static ClassCastException createOptClassCastException() {
447+
vmErrorIfImplicitExceptionsAreFatal(false);
448+
return new ClassCastException(IMPRECISE_STACK_MSG);
449+
}
450+
451+
/** Foreign call: {@link #CREATE_OPT_ARRAY_STORE_EXCEPTION}. */
452+
@SubstrateForeignCallTarget(stubCallingConvention = true)
453+
private static ArrayStoreException createOptArrayStoreException() {
454+
vmErrorIfImplicitExceptionsAreFatal(false);
455+
return new ArrayStoreException(IMPRECISE_STACK_MSG);
456+
}
457+
458+
/** Foreign call: {@link #CREATE_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR}. */
459+
@SubstrateForeignCallTarget(stubCallingConvention = true)
460+
private static IncompatibleClassChangeError createOptIncompatibleClassChangeError() {
461+
vmErrorIfImplicitExceptionsAreFatal(false);
462+
return new IncompatibleClassChangeError(IMPRECISE_STACK_MSG);
463+
}
464+
465+
/** Foreign call: {@link #THROW_OPT_NULL_POINTER_EXCEPTION}. */
466+
@SubstrateForeignCallTarget(stubCallingConvention = true)
467+
private static void throwOptNullPointerException() {
468+
vmErrorIfImplicitExceptionsAreFatal(false);
469+
throw new NullPointerException(IMPRECISE_STACK_MSG);
470+
}
471+
472+
/** Foreign call: {@link #THROW_OPT_OUT_OF_BOUNDS_EXCEPTION}. */
473+
@SubstrateForeignCallTarget(stubCallingConvention = true)
474+
private static void throwOptOutOfBoundsException() {
475+
vmErrorIfImplicitExceptionsAreFatal(false);
476+
throw new ArrayIndexOutOfBoundsException(IMPRECISE_STACK_MSG);
477+
}
478+
479+
/** Foreign call: {@link #THROW_OPT_CLASS_CAST_EXCEPTION}. */
480+
@SubstrateForeignCallTarget(stubCallingConvention = true)
481+
private static void throwOptClassCastException() {
482+
vmErrorIfImplicitExceptionsAreFatal(false);
483+
throw new ClassCastException(IMPRECISE_STACK_MSG);
484+
}
485+
486+
/** Foreign call: {@link #THROW_OPT_ARRAY_STORE_EXCEPTION}. */
487+
@SubstrateForeignCallTarget(stubCallingConvention = true)
488+
private static void throwOptArrayStoreException() {
489+
vmErrorIfImplicitExceptionsAreFatal(false);
490+
throw new ArrayStoreException(IMPRECISE_STACK_MSG);
491+
}
492+
493+
/** Foreign call: {@link #THROW_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR}. */
494+
@SubstrateForeignCallTarget(stubCallingConvention = true)
495+
private static void throwOptIncompatibleClassChangeError() {
496+
vmErrorIfImplicitExceptionsAreFatal(false);
497+
throw new IncompatibleClassChangeError(IMPRECISE_STACK_MSG);
498+
}
499+
409500
/** Foreign call: {@link #GET_CACHED_NULL_POINTER_EXCEPTION}. */
410501
@RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.")
411502
@SubstrateForeignCallTarget(stubCallingConvention = true)

0 commit comments

Comments
 (0)