Skip to content

Commit

Permalink
Optimize stack trace information for implicit exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
christianwimmer committed Mar 29, 2024
1 parent 888fcaa commit 1d35087
Show file tree
Hide file tree
Showing 4 changed files with 386 additions and 5 deletions.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ 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);
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 @@ -1090,4 +1091,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);
}
Original file line number Diff line number Diff line change
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 1d35087

Please sign in to comment.