Skip to content

Commit

Permalink
[GR-52732] Optimize stack trace information for implicit exceptions.
Browse files Browse the repository at this point in the history
PullRequest: graal/17400
  • Loading branch information
christianwimmer committed Apr 29, 2024
2 parents ee40fc1 + 94d2796 commit d0b3d49
Show file tree
Hide file tree
Showing 5 changed files with 411 additions and 5 deletions.
Expand Up @@ -28,12 +28,14 @@
import java.util.Arrays;
import java.util.Queue;

import jdk.graal.compiler.debug.CounterKey;
import jdk.graal.compiler.debug.DebugContext;
import jdk.graal.compiler.debug.GraalError;
import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.Equivalence;

import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider;
import jdk.graal.compiler.debug.Assertions;
import jdk.graal.compiler.debug.CounterKey;
import jdk.graal.compiler.debug.DebugContext;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.lir.ConstantValue;
import jdk.graal.compiler.lir.ImplicitLIRFrameState;
import jdk.graal.compiler.lir.LIRFrameState;
Expand All @@ -49,8 +51,6 @@
import jdk.graal.compiler.nodes.virtual.VirtualBoxingNode;
import jdk.graal.compiler.nodes.virtual.VirtualObjectNode;
import jdk.graal.compiler.nodes.virtual.VirtualObjectState;
import jdk.graal.compiler.debug.Assertions;

import jdk.vm.ci.code.BytecodeFrame;
import jdk.vm.ci.code.RegisterValue;
import jdk.vm.ci.code.VirtualObject;
Expand Down Expand Up @@ -269,6 +269,7 @@ protected BytecodeFrame computeFrameForState(NodeWithState node, FrameState stat
assert state.bci != BytecodeFrame.AFTER_EXCEPTION_BCI || state.locksSize() == 0 : Assertions.errorMessageContext("node", node, "state", state);

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

Expand Down
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Expand Up @@ -16,6 +16,7 @@ This changelog summarizes major changes to GraalVM Native Image.
* (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`).
* (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.
* (GR-51086) Introduce a new `--static-nolibc` API option as a replacement for the experimental `-H:卤StaticExecutableWithDynamicLibC` option.
* (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.
* (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).
* (GR-52578) Print information about embedded resources into `embedded-resources.json` using the `-H:+GenerateEmbeddedResourcesFile` option.
* (GR-51172) Add support to catch OutOfMemoryError exceptions on native image if there is no memory left.
Expand Down
Expand Up @@ -285,6 +285,14 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, String ol
SubstrateOptions.IncludeNodeSourcePositions.update(values, newLevel == OptimizationLevel.O0);
SubstrateOptions.SourceLevelDebug.update(values, newLevel == OptimizationLevel.O0);
SubstrateOptions.AOTTrivialInline.update(values, newLevel != OptimizationLevel.O0);

/*
* We do not want to enable this optimization yet by default, because it reduces the
* precision of implicit stack traces. But for optimized release builds, including pgo
* builds, it is a valuable image size reduction.
*/
SubstrateOptions.ReduceImplicitExceptionStackTraceInformation.update(values, newLevel == OptimizationLevel.O3);

GraalOptions.OptimizeLongJumps.update(values, !newLevel.isOneOf(OptimizationLevel.O0, OptimizationLevel.BUILD_TIME));
if (optimizeValueUpdateHandler != null) {
optimizeValueUpdateHandler.onValueUpdate(values, newLevel);
Expand Down Expand Up @@ -1167,4 +1175,8 @@ public static class TruffleStableOptions {
"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)//
public static final HostedOptionKey<Boolean> CopyLanguageResources = new HostedOptionKey<>(true);
}

@Option(help = "Reduce the amount of metadata in the image for implicit exceptions by removing inlining information from the stack trace. " +
"This makes the image smaller, but also the stack trace of implicit exceptions less precise.", type = OptionType.Expert)//
public static final HostedOptionKey<Boolean> ReduceImplicitExceptionStackTraceInformation = new HostedOptionKey<>(false);
}
Expand Up @@ -35,6 +35,7 @@
import com.oracle.svm.core.heap.RestrictHeapAccess;
import com.oracle.svm.core.jdk.InternalVMMethod;
import com.oracle.svm.core.jdk.StackTraceUtils;
import com.oracle.svm.core.option.SubstrateOptionsParser;
import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor;
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
import com.oracle.svm.core.threadlocal.FastThreadLocalInt;
Expand Down Expand Up @@ -106,6 +107,20 @@ public class ImplicitExceptions {
public static final SubstrateForeignCallDescriptor THROW_NEW_ASSERTION_ERROR_NULLARY = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwNewAssertionErrorNullary", NO_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor THROW_NEW_ASSERTION_ERROR_OBJECT = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwNewAssertionErrorObject", NO_SIDE_EFFECT);

public static final SubstrateForeignCallDescriptor CREATE_OPT_NULL_POINTER_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "createOptNullPointerException", HAS_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor CREATE_OPT_OUT_OF_BOUNDS_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "createOptOutOfBoundsException", HAS_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor CREATE_OPT_CLASS_CAST_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "createOptClassCastException", HAS_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor CREATE_OPT_ARRAY_STORE_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "createOptArrayStoreException", HAS_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor CREATE_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "createOptIncompatibleClassChangeError",
HAS_SIDE_EFFECT);

public static final SubstrateForeignCallDescriptor THROW_OPT_NULL_POINTER_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwOptNullPointerException", NO_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor THROW_OPT_OUT_OF_BOUNDS_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwOptOutOfBoundsException", NO_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor THROW_OPT_CLASS_CAST_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwOptClassCastException", NO_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor THROW_OPT_ARRAY_STORE_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwOptArrayStoreException", NO_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor THROW_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "throwOptIncompatibleClassChangeError",
NO_SIDE_EFFECT);

public static final SubstrateForeignCallDescriptor GET_CACHED_NULL_POINTER_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "getCachedNullPointerException", HAS_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor GET_CACHED_OUT_OF_BOUNDS_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "getCachedOutOfBoundsException", HAS_SIDE_EFFECT);
public static final SubstrateForeignCallDescriptor GET_CACHED_CLASS_CAST_EXCEPTION = SnippetRuntime.findForeignCall(ImplicitExceptions.class, "getCachedClassCastException", HAS_SIDE_EFFECT);
Expand Down Expand Up @@ -150,6 +165,9 @@ public class ImplicitExceptions {
THROW_CACHED_NULL_POINTER_EXCEPTION, THROW_CACHED_OUT_OF_BOUNDS_EXCEPTION, THROW_CACHED_CLASS_CAST_EXCEPTION, THROW_CACHED_ARRAY_STORE_EXCEPTION,
THROW_CACHED_INCOMPATIBLE_CLASS_CHANGE_ERROR,
THROW_CACHED_ILLEGAL_ARGUMENT_EXCEPTION, THROW_CACHED_NEGATIVE_ARRAY_SIZE_EXCEPTION, THROW_CACHED_ARITHMETIC_EXCEPTION, THROW_CACHED_ASSERTION_ERROR,
CREATE_OPT_NULL_POINTER_EXCEPTION, CREATE_OPT_OUT_OF_BOUNDS_EXCEPTION, CREATE_OPT_CLASS_CAST_EXCEPTION, CREATE_OPT_ARRAY_STORE_EXCEPTION,
CREATE_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR,
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
};

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

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 " +
SubstrateOptionsParser.commandArgument(SubstrateOptions.ReduceImplicitExceptionStackTraceInformation, "-");

/** Foreign call: {@link #CREATE_OPT_NULL_POINTER_EXCEPTION}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
private static NullPointerException createOptNullPointerException() {
vmErrorIfImplicitExceptionsAreFatal(false);
return new NullPointerException(IMPRECISE_STACK_MSG);
}

/** Foreign call: {@link #CREATE_OPT_OUT_OF_BOUNDS_EXCEPTION}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
private static ArrayIndexOutOfBoundsException createOptOutOfBoundsException() {
vmErrorIfImplicitExceptionsAreFatal(false);
return new ArrayIndexOutOfBoundsException(IMPRECISE_STACK_MSG);
}

/** Foreign call: {@link #CREATE_OPT_CLASS_CAST_EXCEPTION}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
private static ClassCastException createOptClassCastException() {
vmErrorIfImplicitExceptionsAreFatal(false);
return new ClassCastException(IMPRECISE_STACK_MSG);
}

/** Foreign call: {@link #CREATE_OPT_ARRAY_STORE_EXCEPTION}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
private static ArrayStoreException createOptArrayStoreException() {
vmErrorIfImplicitExceptionsAreFatal(false);
return new ArrayStoreException(IMPRECISE_STACK_MSG);
}

/** Foreign call: {@link #CREATE_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
private static IncompatibleClassChangeError createOptIncompatibleClassChangeError() {
vmErrorIfImplicitExceptionsAreFatal(false);
return new IncompatibleClassChangeError(IMPRECISE_STACK_MSG);
}

/** Foreign call: {@link #THROW_OPT_NULL_POINTER_EXCEPTION}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
private static void throwOptNullPointerException() {
vmErrorIfImplicitExceptionsAreFatal(false);
throw new NullPointerException(IMPRECISE_STACK_MSG);
}

/** Foreign call: {@link #THROW_OPT_OUT_OF_BOUNDS_EXCEPTION}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
private static void throwOptOutOfBoundsException() {
vmErrorIfImplicitExceptionsAreFatal(false);
throw new ArrayIndexOutOfBoundsException(IMPRECISE_STACK_MSG);
}

/** Foreign call: {@link #THROW_OPT_CLASS_CAST_EXCEPTION}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
private static void throwOptClassCastException() {
vmErrorIfImplicitExceptionsAreFatal(false);
throw new ClassCastException(IMPRECISE_STACK_MSG);
}

/** Foreign call: {@link #THROW_OPT_ARRAY_STORE_EXCEPTION}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
private static void throwOptArrayStoreException() {
vmErrorIfImplicitExceptionsAreFatal(false);
throw new ArrayStoreException(IMPRECISE_STACK_MSG);
}

/** Foreign call: {@link #THROW_OPT_INCOMPATIBLE_CLASS_CHANGE_ERROR}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
private static void throwOptIncompatibleClassChangeError() {
vmErrorIfImplicitExceptionsAreFatal(false);
throw new IncompatibleClassChangeError(IMPRECISE_STACK_MSG);
}

/** Foreign call: {@link #GET_CACHED_NULL_POINTER_EXCEPTION}. */
@RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.")
@SubstrateForeignCallTarget(stubCallingConvention = true)
Expand Down

0 comments on commit d0b3d49

Please sign in to comment.