diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java index cd4a4df8af47..f5dd79922a2d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.core.code; +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.word.UnsignedWord; @@ -46,8 +48,6 @@ import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.word.Word; -import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; - /** * Provides functionality to query information about a unit of compiled code from a {@link CodeInfo} * object. This helper class is necessary to ensure that {@link CodeInfo} objects are used @@ -365,10 +365,6 @@ public static void setEncodings(CodeInfo info, NonmovableObjectArray obj } } - public static Log log(CodeInfo info, Log log) { - return info.isNull() ? log.string("null") : log.string("CodeInfo@").hex(info); - } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static int getTier(CodeInfo info) { return cast(info).getTier(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java index fd16011867dc..b1cb310cddb6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java @@ -86,7 +86,7 @@ public static class Options { private CodeInfoDecoder() { } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long lookupCodeInfoEntryOffset(CodeInfo info, long relativeIP) { long entryIP = lookupEntryIP(relativeIP); long entryOffset = loadEntryOffset(info, relativeIP); @@ -103,7 +103,7 @@ private static long lookupCodeInfoEntryOffset(CodeInfo info, long relativeIP) { return INVALID_FRAME_INFO_ENTRY_OFFSET; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long lookupCodeInfoEntryOffsetOrDefault(CodeInfo info, long relativeIP) { int chunksToSearch = 0; while (true) { @@ -166,7 +166,7 @@ static void lookupCodeInfo(CodeInfo info, long relativeIP, CodeInfoQueryResult c codeInfoQueryResult.frameInfo = CodeInfoQueryResult.NO_FRAME_INFO; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static void lookupCodeInfo(CodeInfo info, long relativeIP, SimpleCodeInfoQueryResult codeInfoQueryResult) { long sizeEncoding = INVALID_SIZE_ENCODING; long entryIP = lookupEntryIP(relativeIP); @@ -240,7 +240,7 @@ static long lookupDeoptimizationEntrypoint(CodeInfo info, long method, long enco return -1; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static long lookupStackReferenceMapIndex(CodeInfo info, long relativeIP) { long entryIP = lookupEntryIP(relativeIP); long entryOffset = loadEntryOffset(info, relativeIP); @@ -262,12 +262,12 @@ static long indexGranularity() { return Options.CodeInfoIndexGranularity.getValue(); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static long lookupEntryIP(long relativeIP) { return Long.divideUnsigned(relativeIP, indexGranularity()) * indexGranularity(); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long loadEntryOffset(CodeInfo info, long relativeIP) { counters().lookupEntryOffsetCount.inc(); long index = Long.divideUnsigned(relativeIP, indexGranularity()); @@ -275,7 +275,7 @@ private static long loadEntryOffset(CodeInfo info, long relativeIP) { } @AlwaysInline("Make IP-lookup loop call free") - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static int loadEntryFlags(CodeInfo info, long curOffset) { counters().loadEntryFlagsCount.inc(); return NonmovableByteArrayReader.getU1(CodeInfoAccess.getCodeInfoEncodings(info), curOffset); @@ -295,7 +295,7 @@ private static int loadDeoptReturnValueIsObject(CodeInfo info, long entryOffset, private static final int INVALID_FRAME_INFO_ENTRY_OFFSET = -1; @AlwaysInline("Make IP-lookup loop call free") - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long updateSizeEncoding(CodeInfo info, long entryOffset, int entryFlags, long sizeEncoding) { switch (extractFS(entryFlags)) { case FS_NO_CHANGE: @@ -311,7 +311,7 @@ private static long updateSizeEncoding(CodeInfo info, long entryOffset, int entr } } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long loadExceptionOffset(CodeInfo info, long entryOffset, int entryFlags) { switch (extractEX(entryFlags)) { case EX_NO_HANDLER: @@ -327,7 +327,7 @@ private static long loadExceptionOffset(CodeInfo info, long entryOffset, int ent } } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long loadReferenceMapIndex(CodeInfo info, long entryOffset, int entryFlags) { switch (extractRM(entryFlags)) { case RM_NO_MAP: @@ -349,19 +349,19 @@ private static long loadReferenceMapIndex(CodeInfo info, long entryOffset, int e public static final int FRAME_SIZE_STATUS_MASK = FRAME_SIZE_METHOD_START | FRAME_SIZE_ENTRY_POINT | FRAME_SIZE_HAS_CALLEE_SAVED_REGISTERS; - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public static boolean decodeIsEntryPoint(long sizeEncoding) { assert sizeEncoding != INVALID_SIZE_ENCODING; return (sizeEncoding & FRAME_SIZE_ENTRY_POINT) != 0; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static boolean decodeHasCalleeSavedRegisters(long sizeEncoding) { assert sizeEncoding != INVALID_SIZE_ENCODING; return (sizeEncoding & FRAME_SIZE_HAS_CALLEE_SAVED_REGISTERS) != 0; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static long decodeTotalFrameSize(long sizeEncoding) { assert sizeEncoding != INVALID_SIZE_ENCODING; return sizeEncoding & ~FRAME_SIZE_STATUS_MASK; @@ -432,7 +432,7 @@ private static FrameInfoQueryResult loadFrameInfo(CodeInfo info, long entryOffse * * @see CodeInfoEncoder.Encoders#encodeMethodTable */ - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static void fillSourceFields(FrameInfoQueryResult result) { int methodId = result.sourceMethodId; CodeInfo info; @@ -489,7 +489,7 @@ private static int readIndex(Pointer p, boolean isShort, int offset) { } @AlwaysInline("Make IP-lookup loop call free") - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long advanceIP(CodeInfo info, long entryOffset, long entryIP) { int deltaIP = NonmovableByteArrayReader.getU1(CodeInfoAccess.getCodeInfoEncodings(info), offsetIP(entryOffset)); if (deltaIP == DELTA_END_OF_TABLE) { @@ -588,62 +588,62 @@ private static boolean endOfTable(long entryIP) { } } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static int extractFS(int entryFlags) { return (entryFlags & FS_MASK_IN_PLACE) >> FS_SHIFT; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static int extractEX(int entryFlags) { return (entryFlags & EX_MASK_IN_PLACE) >> EX_SHIFT; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static int extractRM(int entryFlags) { return (entryFlags & RM_MASK_IN_PLACE) >> RM_SHIFT; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) static int extractFI(int entryFlags) { return (entryFlags & FI_MASK_IN_PLACE) >> FI_SHIFT; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long offsetIP(long entryOffset) { return entryOffset + IP_OFFSET; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long offsetFS(long entryOffset, int entryFlags) { assert extractFS(entryFlags) != FS_NO_CHANGE; return entryOffset + FS_OFFSET; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long getU1(byte[] data, long byteIndex) { return data[(int) byteIndex] & 0xFF; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long offsetEX(long entryOffset, int entryFlags) { assert extractEX(entryFlags) != EX_NO_HANDLER; return entryOffset + getU1(EX_OFFSET, entryFlags); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long offsetRM(long entryOffset, int entryFlags) { assert extractRM(entryFlags) != RM_NO_MAP && extractRM(entryFlags) != RM_EMPTY_MAP; return entryOffset + getU1(RM_OFFSET, entryFlags); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long offsetFI(long entryOffset, int entryFlags) { assert extractFI(entryFlags) != FI_NO_DEOPT; return entryOffset + getU1(FI_OFFSET, entryFlags); } @AlwaysInline("Make IP-lookup loop call free") - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long advanceOffset(long entryOffset, int entryFlags) { counters().advanceOffset.inc(); long returnValueIsObjectSize = 0; @@ -668,15 +668,21 @@ public static class FrameInfoCursor { private final ReusableTypeReader frameInfoReader = new ReusableTypeReader(); private final SingleShotFrameInfoQueryResultAllocator singleShotFrameInfoQueryResultAllocator = new SingleShotFrameInfoQueryResultAllocator(); private final FrameInfoState state = new FrameInfoState(); + private final FrameInfoDecoder.ValueInfoAllocator valueInfoAllocator; private CodeInfo info; private FrameInfoQueryResult result; private boolean canDecode; public FrameInfoCursor() { + this(DummyValueInfoAllocator.SINGLETON); + } + + public FrameInfoCursor(FrameInfoDecoder.ValueInfoAllocator valueInfoAllocator) { + this.valueInfoAllocator = valueInfoAllocator; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SuppressWarnings("hiding") public void initialize(CodeInfo info, CodePointer ip, boolean exactIPMatch) { this.info = info; @@ -691,13 +697,13 @@ public void initialize(CodeInfo info, CodePointer ip, boolean exactIPMatch) { * invalidates the data of all {@link FrameInfoQueryResult} objects that were previously * returned by {@link FrameInfoCursor#get}. */ - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public boolean advance() { decodeNextEntry(); return result != null; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public boolean hasCaller() { assert result != null; return !state.isDone; @@ -706,21 +712,25 @@ public boolean hasCaller() { /** * Returns the information for the current frame. * - * Please note there is no caller and no value information present in the + * Please note there is no caller and (almost) no value information present in the * {@link FrameInfoQueryResult} object (i.e., the methods - * {@link FrameInfoQueryResult#getCaller()}, {@link FrameInfoQueryResult#getValueInfos()}, - * and {@link FrameInfoQueryResult#getVirtualObjects()} will return {@code null}). + * {@link FrameInfoQueryResult#getCaller()} and + * {@link FrameInfoQueryResult#getVirtualObjects()} will return {@code null}, + * {@link FrameInfoQueryResult#getValueInfos()} might return {@code null} and will not + * contain decoded constants). * * Every {@link FrameInfoCursor} object uses only a single {@link FrameInfoQueryResult} * object internally. Therefore, the values of that object are overwritten when - * {@link #advance()} is called to move to the next frame. + * {@link #advance()} is called to move to the next frame. This is also true for + * {@link FrameInfoQueryResult.ValueInfo} array and objects from + * {@link FrameInfoQueryResult#getValueInfos()}. */ - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public FrameInfoQueryResult get() { return result; } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private void decodeNextEntry() { if (!canDecode) { return; @@ -729,7 +739,7 @@ private void decodeNextEntry() { singleShotFrameInfoQueryResultAllocator.reload(); int entryFlags = loadEntryFlags(info, state.entryOffset); boolean isDeoptEntry = extractFI(entryFlags) == FI_DEOPT_ENTRY_INDEX_S4; - result = FrameInfoDecoder.decodeFrameInfo(isDeoptEntry, frameInfoReader, info, singleShotFrameInfoQueryResultAllocator, DummyValueInfoAllocator.SINGLETON, + result = FrameInfoDecoder.decodeFrameInfo(isDeoptEntry, frameInfoReader, info, singleShotFrameInfoQueryResultAllocator, valueInfoAllocator, FrameInfoDecoder.SubstrateConstantAccess, state); if (result == null) { /* No more entries. */ @@ -737,7 +747,7 @@ private void decodeNextEntry() { } } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private boolean initFrameInfoReader(CodePointer ip, boolean exactIPMatch) { long relativeIP = CodeInfoAccess.relativeIP(info, ip); long entryOffset = exactIPMatch ? lookupCodeInfoEntryOffset(info, relativeIP) : lookupCodeInfoEntryOffsetOrDefault(info, relativeIP); @@ -771,7 +781,7 @@ public FrameInfoState() { reset(); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public FrameInfoState reset() { entryOffset = INVALID_FRAME_INFO_ENTRY_OFFSET; isFirstFrame = true; @@ -786,14 +796,14 @@ private static final class SingleShotFrameInfoQueryResultAllocator implements Fr private final FrameInfoQueryResult frameInfoQueryResult = new FrameInfoQueryResult(); private boolean fired; - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public SingleShotFrameInfoQueryResultAllocator reload() { fired = false; return this; } @Override - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public FrameInfoQueryResult newFrameInfoQueryResult() { if (fired) { return null; @@ -804,33 +814,114 @@ public FrameInfoQueryResult newFrameInfoQueryResult() { } } - private static final class DummyValueInfoAllocator implements FrameInfoDecoder.ValueInfoAllocator { - static final DummyValueInfoAllocator SINGLETON = new DummyValueInfoAllocator(); + /** + * This limited implementation of {@code FrameInfoDecoder.ValueInfoAllocator} doesn't need to + * allocate anything at runtime but only supports a limited number of + * {@link FrameInfoQueryResult.ValueInfo} objects (20). + *

+ * Those objects are re-used so they should not be held onto cross calls to + * {@link #newValueInfoArray}. + *

+ * Constants are not {@linkplain #decodeConstant decoded} so + * {@link FrameInfoQueryResult.ValueInfo#getValue()} will return {@code null}. + *

+ * {@link #newValueInfoArrayArray} always returns null (this is currently only used for virtual + * objects). + */ + public static final class SingleShotValueInfoAllocator implements FrameInfoDecoder.ValueInfoAllocator { + private static final int NUM_PREALLOCATED_VALUE_INFO = 20; + /** + * The pre-allocated array to be returned by {@code #newValueInfoArray}. + */ + private final FrameInfoQueryResult.ValueInfo[] valueInfos; + /** + * The pre-allocated objects to be returned by {@code #newValueInfo}. + */ + private final FrameInfoQueryResult.ValueInfo[] preAllocatedValueInfos; + private int nextPreAllocatedValueInfo; + + @Platforms(Platform.HOSTED_ONLY.class) + public SingleShotValueInfoAllocator() { + valueInfos = new FrameInfoQueryResult.ValueInfo[NUM_PREALLOCATED_VALUE_INFO]; + preAllocatedValueInfos = new FrameInfoQueryResult.ValueInfo[valueInfos.length]; + for (int i = 0; i < preAllocatedValueInfos.length; i++) { + preAllocatedValueInfos[i] = new FrameInfoQueryResult.ValueInfo(); + } + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private void reset() { + for (int i = 0, len = valueInfos.length; i < len; i++) { + valueInfos[i] = null; + } + nextPreAllocatedValueInfo = 0; + /* + * The is no need to eagerly clear the elements of preAllocatedValueInfos: they do not + * hold onto objects since constants are not decoded. They are cleared when returned by + * newValueInfo. + */ + } + + @Override + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public FrameInfoQueryResult.ValueInfo[] newValueInfoArray(int len) { + if (len > valueInfos.length) { + return null; + } + reset(); + return valueInfos; + } + + @Override + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public FrameInfoQueryResult.ValueInfo newValueInfo() { + if (nextPreAllocatedValueInfo < preAllocatedValueInfos.length) { + FrameInfoQueryResult.ValueInfo valueInfo = preAllocatedValueInfos[nextPreAllocatedValueInfo++]; + valueInfo.clear(); + return valueInfo; + } + return null; + } + + @Override + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public FrameInfoQueryResult.ValueInfo[][] newValueInfoArrayArray(int len) { + return null; + } + + @Override + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void decodeConstant(FrameInfoQueryResult.ValueInfo valueInfo, NonmovableObjectArray frameInfoObjectConstants, ConstantAccess constantAccess) { + } + } + + public static final class DummyValueInfoAllocator implements FrameInfoDecoder.ValueInfoAllocator { + public static final DummyValueInfoAllocator SINGLETON = new DummyValueInfoAllocator(); @Platforms(Platform.HOSTED_ONLY.class) private DummyValueInfoAllocator() { } @Override - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public FrameInfoQueryResult.ValueInfo newValueInfo() { return null; } @Override - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public FrameInfoQueryResult.ValueInfo[] newValueInfoArray(int len) { return null; } @Override - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public FrameInfoQueryResult.ValueInfo[][] newValueInfoArrayArray(int len) { return null; } @Override - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public void decodeConstant(FrameInfoQueryResult.ValueInfo valueInfo, NonmovableObjectArray frameInfoObjectConstants, ConstantAccess constantAccess) { } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java index 169bda2276b5..b5988db22308 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java @@ -167,8 +167,12 @@ public static void visitObjectReferences(Pointer sp, CodePointer ip, CodeInfo in @Uninterruptible(reason = "Not really uninterruptible, but we are about to fail.", calleeMustBe = false) public static RuntimeException fatalErrorNoReferenceMap(Pointer sp, CodePointer ip, CodeInfo info) { - Log.log().string("ip: ").zhex(ip).string(", sp: ").zhex(sp).string(", "); - CodeInfoAccess.log(info, Log.log()).newline(); + Log.log().string("ip: ").zhex(ip).string(", sp: ").zhex(sp).string(", code info: "); + if (info.isNull()) { + Log.log().string("null"); + } else { + CodeInfoAccess.printCodeInfo(Log.log(), info, true); + } throw VMError.shouldNotReachHere("No reference map information found"); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoQueryResult.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoQueryResult.java index f6a09e5d236d..d5b8c26320eb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoQueryResult.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoQueryResult.java @@ -114,6 +114,10 @@ public static final class ValueInfo { protected long data; protected JavaConstant value; + public ValueInfo() { + clear(); + } + /** * Returns the type of the value, describing how to access the value. */ @@ -186,6 +190,17 @@ public ValueInfo copyForElement(JavaKind javaKind, int offset) { copy.value = value; return copy; } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void clear() { + type = null; + kind = null; + isCompressedReference = false; + isEliminatedMonitor = false; + isAutoBoxedPrimitive = false; + data = 0; + value = null; + } } protected FrameInfoQueryResult caller; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/interpreter/InterpreterSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/interpreter/InterpreterSupport.java index 91245348e278..7e4b98553511 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/interpreter/InterpreterSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/interpreter/InterpreterSupport.java @@ -41,7 +41,9 @@ import com.oracle.svm.core.code.FrameInfoQueryResult; import com.oracle.svm.core.code.FrameSourceInfo; import com.oracle.svm.core.heap.ObjectReferenceVisitor; +import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.heap.UnknownPrimitiveField; +import com.oracle.svm.core.log.Log; import jdk.graal.compiler.api.replacements.Fold; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -79,6 +81,14 @@ public static InterpreterSupport singleton() { */ public abstract FrameSourceInfo getInterpretedMethodFrameInfo(FrameInfoQueryResult frameInfo, Pointer sp); + /** + * Make a best-effort attempt at logging helpful information about the + * {@linkplain #isInterpreterRoot interpreter frame}. Avoiding allocations or anything risky + * during crash logging. + */ + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Used for crash log") + public abstract void logInterpreterFrame(Log log, FrameInfoQueryResult frameInfo, Pointer sp); + @Platforms(Platform.HOSTED_ONLY.class) public static void setLeaveStubPointer(CFunctionPointer leaveStubPointer, int length) { assert singleton().leaveStubPointer == null : "multiple leave stub methods registered"; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java index 96212221ce82..be7dda206b91 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java @@ -34,6 +34,7 @@ import com.oracle.svm.core.code.CodeInfoAccess; import com.oracle.svm.core.code.CodeInfoDecoder; import com.oracle.svm.core.code.CodeInfoTable; +import com.oracle.svm.core.code.FrameInfoDecoder; import com.oracle.svm.core.code.FrameInfoQueryResult; import com.oracle.svm.core.code.ImageCodeInfo; import com.oracle.svm.core.code.UntetheredCodeInfo; @@ -41,6 +42,7 @@ import com.oracle.svm.core.deopt.DeoptimizedFrame; import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.interpreter.InterpreterSupport; import com.oracle.svm.core.log.Log; import jdk.graal.compiler.word.Word; @@ -87,12 +89,23 @@ private static void logFrame(Log log, Pointer sp, CodePointer ip) { public static class StackFramePrintVisitor extends ParameterizedStackFrameVisitor { private static final int MAX_STACK_FRAMES_PER_THREAD_TO_PRINT = 100_000; - private final CodeInfoDecoder.FrameInfoCursor frameInfoCursor = new CodeInfoDecoder.FrameInfoCursor(); + private final CodeInfoDecoder.FrameInfoCursor frameInfoCursor; private int invocationCount; private int printedFrames; private Pointer expectedSP; public StackFramePrintVisitor() { + FrameInfoDecoder.ValueInfoAllocator valueInfoAllocator; + if (InterpreterSupport.isEnabled()) { + /* + * This helps print interpreter frames: InterpreterSupportImpl needs value info for + * the method and bci. + */ + valueInfoAllocator = new CodeInfoDecoder.SingleShotValueInfoAllocator(); + } else { + valueInfoAllocator = CodeInfoDecoder.DummyValueInfoAllocator.SINGLETON; + } + frameInfoCursor = new CodeInfoDecoder.FrameInfoCursor(valueInfoAllocator); } @SuppressWarnings("hiding") @@ -160,7 +173,7 @@ private void logDeoptimizedJavaFrame(Log log, Pointer sp, CodePointer ip, Deopti boolean isCompilationRoot = frame.getCaller() == null; printFrameIdentifier(log, Word.nullPointer(), deoptFrame, isCompilationRoot, false); logFrameRaw(log, sp, ip, deoptFrame.getSourceTotalFrameSize()); - logFrameInfo(log, frame.getFrameInfo(), ImageCodeInfo.CODE_INFO_NAME + ", deopt"); + logFrameInfo(log, frame.getFrameInfo(), ImageCodeInfo.CODE_INFO_NAME + ", deopt", sp); if (!isCompilationRoot) { log.newline(); } @@ -202,7 +215,7 @@ private void logVirtualFrame(Log log, Pointer sp, CodePointer ip, CodeInfo codeI logJavaFrameMinimalInfo(log, sp, ip, codeInfo, null, isCompilationRoot); String codeInfoName = DeoptimizationSupport.enabled() ? CodeInfoAccess.getName(codeInfo) : null; - logFrameInfo(log, frameInfo, codeInfoName); + logFrameInfo(log, frameInfo, codeInfoName, sp); } private void logJavaFrameMinimalInfo(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo, DeoptimizedFrame deoptFrame, boolean isCompilationRoot) { @@ -261,11 +274,14 @@ private static void logSPAndIP(Log log, Pointer sp, CodePointer ip) { log.string("IP ").zhex(ip); } - private static void logFrameInfo(Log log, FrameInfoQueryResult frameInfo, String runtimeMethodInfoName) { + private void logFrameInfo(Log log, FrameInfoQueryResult frameInfo, String runtimeMethodInfoName, Pointer sp) { if (runtimeMethodInfoName != null) { log.string("[").string(runtimeMethodInfoName).string("] "); } frameInfo.log(log); + if (InterpreterSupport.isEnabled() && invocationCount == 1 && InterpreterSupport.singleton().isInterpreterRoot(frameInfo)) { + InterpreterSupport.singleton().logInterpreterFrame(log, frameInfo, sp); + } } private static void printFrameIdentifier(Log log, CodeInfo codeInfo, DeoptimizedFrame deoptFrame, boolean isCompilationRoot, boolean isNative) { diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterSupportImpl.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterSupportImpl.java index 044c509411b2..f4e78bcf43f1 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterSupportImpl.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterSupportImpl.java @@ -36,9 +36,14 @@ import com.oracle.svm.core.code.FrameInfoQueryResult; import com.oracle.svm.core.code.FrameSourceInfo; import com.oracle.svm.core.heap.ReferenceAccess; +import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.interpreter.InterpreterFrameSourceInfo; import com.oracle.svm.core.interpreter.InterpreterSupport; +import com.oracle.svm.core.log.Log; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.espresso.classfile.descriptors.ByteSequence; +import com.oracle.svm.espresso.classfile.descriptors.Name; +import com.oracle.svm.espresso.classfile.descriptors.Symbol; import com.oracle.svm.interpreter.metadata.InterpreterResolvedJavaMethod; import jdk.graal.compiler.word.Word; @@ -46,6 +51,8 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; public final class InterpreterSupportImpl extends InterpreterSupport { + private static final int MAX_SYMBOL_LOG_LENGTH = 255; + private final int bciSlot; private final int interpretedMethodSlot; private final int interpretedFrameSlot; @@ -135,6 +142,93 @@ public FrameSourceInfo getInterpretedMethodFrameInfo(FrameInfoQueryResult frameI throw VMError.shouldNotReachHereAtRuntime(); } + @Override + @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Used for crash log") + public void logInterpreterFrame(Log log, FrameInfoQueryResult frameInfo, Pointer sp) { + if (isInterpreterIntrinsicRoot(frameInfo)) { + logInterpreterIntrinsicFrame(log, frameInfo, sp); + } else if (isInterpreterBytecodeRoot(frameInfo)) { + logInterpreterBytecodeFrame(log, frameInfo, sp); + } else { + throw VMError.shouldNotReachHereAtRuntime(); + } + } + + private void logInterpreterBytecodeFrame(Log log, FrameInfoQueryResult frameInfo, Pointer sp) { + if (!frameInfo.hasLocalValueInfo()) { + log.string(" missing local value info (bytecode)"); + return; + } + InterpreterResolvedJavaMethod interpretedMethod = readInterpretedMethod(frameInfo, sp); + if (interpretedMethod == null) { + log.string(" no interpreter method (bytecode)"); + return; + } + int bci = readBCI(frameInfo, sp); + logInterpreterMethod(log, interpretedMethod, bci); + } + + private void logInterpreterIntrinsicFrame(Log log, FrameInfoQueryResult frameInfo, Pointer sp) { + if (!frameInfo.hasLocalValueInfo()) { + log.string(" missing local value info (intrinsic)"); + return; + } + InterpreterResolvedJavaMethod intrinsicMethod = readIntrinsicMethod(frameInfo, sp); + if (intrinsicMethod == null) { + log.string(" no interpreter method (intrinsic)"); + return; + } + logInterpreterMethod(log, intrinsicMethod, -1); + } + + private static void logInterpreterMethod(Log log, InterpreterResolvedJavaMethod interpretedMethod, int bci) { + String sourceHolderName = interpretedMethod.getDeclaringClass().getJavaClass().getName(); + Symbol sourceMethodName = interpretedMethod.getSymbolicName(); + LineNumberTable lineNumberTable = interpretedMethod.getLineNumberTable(); + int sourceLineNumber = -1; // unknown + if (lineNumberTable != null && bci >= 0) { + sourceLineNumber = lineNumberTable.getLineNumber(bci); + } + log.spaces(2); + log.string(sourceHolderName); + log.character('.'); + logSymbol(log, sourceMethodName); + String sourceFileName = interpretedMethod.getDeclaringClass().getSourceFileName(); + if (sourceFileName == null && sourceLineNumber >= 0) { + sourceFileName = "Unknown Source"; + } + if (sourceFileName != null) { + log.character('('); + log.string(sourceFileName); + if (sourceLineNumber >= 0) { + log.string(":"); + log.signed(sourceLineNumber); + } + log.character(')'); + } + if (bci >= 0) { + log.spaces(1); + log.string("@bci "); + log.signed(bci); + } + } + + private static void logSymbol(Log log, ByteSequence byteSequence) { + int length = Math.min(byteSequence.length(), MAX_SYMBOL_LOG_LENGTH); + for (int i = 0; i < length; i++) { + int b = byteSequence.unsignedByteAt(i); + if (0x20 <= b && b <= 0x7e) { + // only log printable ascii + log.character((char) b); + } else { + log.character('?'); + } + } + if (byteSequence.length() > MAX_SYMBOL_LOG_LENGTH) { + log.string("..."); + } + } + @Platforms(Platform.HOSTED_ONLY.class) @Override public void buildMethodIdMapping(ResolvedJavaMethod[] encodedMethods) {