Skip to content

Commit 71fb023

Browse files
[GR-71950] Add better logging of interpreter frames during crash
PullRequest: graal/22822
2 parents ae145af + b029d3d commit 71fb023

File tree

7 files changed

+284
-58
lines changed

7 files changed

+284
-58
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.core.code;
2626

27+
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
28+
2729
import org.graalvm.nativeimage.c.function.CodePointer;
2830
import org.graalvm.nativeimage.c.struct.SizeOf;
2931
import org.graalvm.word.UnsignedWord;
@@ -46,8 +48,6 @@
4648
import jdk.graal.compiler.api.replacements.Fold;
4749
import jdk.graal.compiler.word.Word;
4850

49-
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
50-
5151
/**
5252
* Provides functionality to query information about a unit of compiled code from a {@link CodeInfo}
5353
* 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<Object> obj
365365
}
366366
}
367367

368-
public static Log log(CodeInfo info, Log log) {
369-
return info.isNull() ? log.string("null") : log.string("CodeInfo@").hex(info);
370-
}
371-
372368
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
373369
public static int getTier(CodeInfo info) {
374370
return cast(info).getTier();

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java

Lines changed: 137 additions & 46 deletions
Large diffs are not rendered by default.

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,12 @@ public static void visitObjectReferences(Pointer sp, CodePointer ip, CodeInfo in
167167

168168
@Uninterruptible(reason = "Not really uninterruptible, but we are about to fail.", calleeMustBe = false)
169169
public static RuntimeException fatalErrorNoReferenceMap(Pointer sp, CodePointer ip, CodeInfo info) {
170-
Log.log().string("ip: ").zhex(ip).string(", sp: ").zhex(sp).string(", ");
171-
CodeInfoAccess.log(info, Log.log()).newline();
170+
Log.log().string("ip: ").zhex(ip).string(", sp: ").zhex(sp).string(", code info: ");
171+
if (info.isNull()) {
172+
Log.log().string("null");
173+
} else {
174+
CodeInfoAccess.printCodeInfo(Log.log(), info, true);
175+
}
172176
throw VMError.shouldNotReachHere("No reference map information found");
173177
}
174178

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoQueryResult.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ public static final class ValueInfo {
114114
protected long data;
115115
protected JavaConstant value;
116116

117+
public ValueInfo() {
118+
clear();
119+
}
120+
117121
/**
118122
* Returns the type of the value, describing how to access the value.
119123
*/
@@ -186,6 +190,17 @@ public ValueInfo copyForElement(JavaKind javaKind, int offset) {
186190
copy.value = value;
187191
return copy;
188192
}
193+
194+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
195+
public void clear() {
196+
type = null;
197+
kind = null;
198+
isCompressedReference = false;
199+
isEliminatedMonitor = false;
200+
isAutoBoxedPrimitive = false;
201+
data = 0;
202+
value = null;
203+
}
189204
}
190205

191206
protected FrameInfoQueryResult caller;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/interpreter/InterpreterSupport.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
import com.oracle.svm.core.code.FrameInfoQueryResult;
4242
import com.oracle.svm.core.code.FrameSourceInfo;
4343
import com.oracle.svm.core.heap.ObjectReferenceVisitor;
44+
import com.oracle.svm.core.heap.RestrictHeapAccess;
4445
import com.oracle.svm.core.heap.UnknownPrimitiveField;
46+
import com.oracle.svm.core.log.Log;
4547

4648
import jdk.graal.compiler.api.replacements.Fold;
4749
import jdk.vm.ci.meta.ResolvedJavaMethod;
@@ -79,6 +81,14 @@ public static InterpreterSupport singleton() {
7981
*/
8082
public abstract FrameSourceInfo getInterpretedMethodFrameInfo(FrameInfoQueryResult frameInfo, Pointer sp);
8183

84+
/**
85+
* Make a best-effort attempt at logging helpful information about the
86+
* {@linkplain #isInterpreterRoot interpreter frame}. Avoiding allocations or anything risky
87+
* during crash logging.
88+
*/
89+
@RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Used for crash log")
90+
public abstract void logInterpreterFrame(Log log, FrameInfoQueryResult frameInfo, Pointer sp);
91+
8292
@Platforms(Platform.HOSTED_ONLY.class)
8393
public static void setLeaveStubPointer(CFunctionPointer leaveStubPointer, int length) {
8494
assert singleton().leaveStubPointer == null : "multiple leave stub methods registered";

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@
3434
import com.oracle.svm.core.code.CodeInfoAccess;
3535
import com.oracle.svm.core.code.CodeInfoDecoder;
3636
import com.oracle.svm.core.code.CodeInfoTable;
37+
import com.oracle.svm.core.code.FrameInfoDecoder;
3738
import com.oracle.svm.core.code.FrameInfoQueryResult;
3839
import com.oracle.svm.core.code.ImageCodeInfo;
3940
import com.oracle.svm.core.code.UntetheredCodeInfo;
4041
import com.oracle.svm.core.deopt.DeoptimizationSupport;
4142
import com.oracle.svm.core.deopt.DeoptimizedFrame;
4243
import com.oracle.svm.core.heap.RestrictHeapAccess;
4344
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
45+
import com.oracle.svm.core.interpreter.InterpreterSupport;
4446
import com.oracle.svm.core.log.Log;
4547

4648
import jdk.graal.compiler.word.Word;
@@ -87,12 +89,23 @@ private static void logFrame(Log log, Pointer sp, CodePointer ip) {
8789
public static class StackFramePrintVisitor extends ParameterizedStackFrameVisitor {
8890
private static final int MAX_STACK_FRAMES_PER_THREAD_TO_PRINT = 100_000;
8991

90-
private final CodeInfoDecoder.FrameInfoCursor frameInfoCursor = new CodeInfoDecoder.FrameInfoCursor();
92+
private final CodeInfoDecoder.FrameInfoCursor frameInfoCursor;
9193
private int invocationCount;
9294
private int printedFrames;
9395
private Pointer expectedSP;
9496

9597
public StackFramePrintVisitor() {
98+
FrameInfoDecoder.ValueInfoAllocator valueInfoAllocator;
99+
if (InterpreterSupport.isEnabled()) {
100+
/*
101+
* This helps print interpreter frames: InterpreterSupportImpl needs value info for
102+
* the method and bci.
103+
*/
104+
valueInfoAllocator = new CodeInfoDecoder.SingleShotValueInfoAllocator();
105+
} else {
106+
valueInfoAllocator = CodeInfoDecoder.DummyValueInfoAllocator.SINGLETON;
107+
}
108+
frameInfoCursor = new CodeInfoDecoder.FrameInfoCursor(valueInfoAllocator);
96109
}
97110

98111
@SuppressWarnings("hiding")
@@ -160,7 +173,7 @@ private void logDeoptimizedJavaFrame(Log log, Pointer sp, CodePointer ip, Deopti
160173
boolean isCompilationRoot = frame.getCaller() == null;
161174
printFrameIdentifier(log, Word.nullPointer(), deoptFrame, isCompilationRoot, false);
162175
logFrameRaw(log, sp, ip, deoptFrame.getSourceTotalFrameSize());
163-
logFrameInfo(log, frame.getFrameInfo(), ImageCodeInfo.CODE_INFO_NAME + ", deopt");
176+
logFrameInfo(log, frame.getFrameInfo(), ImageCodeInfo.CODE_INFO_NAME + ", deopt", sp);
164177
if (!isCompilationRoot) {
165178
log.newline();
166179
}
@@ -202,7 +215,7 @@ private void logVirtualFrame(Log log, Pointer sp, CodePointer ip, CodeInfo codeI
202215
logJavaFrameMinimalInfo(log, sp, ip, codeInfo, null, isCompilationRoot);
203216

204217
String codeInfoName = DeoptimizationSupport.enabled() ? CodeInfoAccess.getName(codeInfo) : null;
205-
logFrameInfo(log, frameInfo, codeInfoName);
218+
logFrameInfo(log, frameInfo, codeInfoName, sp);
206219
}
207220

208221
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) {
261274
log.string("IP ").zhex(ip);
262275
}
263276

264-
private static void logFrameInfo(Log log, FrameInfoQueryResult frameInfo, String runtimeMethodInfoName) {
277+
private void logFrameInfo(Log log, FrameInfoQueryResult frameInfo, String runtimeMethodInfoName, Pointer sp) {
265278
if (runtimeMethodInfoName != null) {
266279
log.string("[").string(runtimeMethodInfoName).string("] ");
267280
}
268281
frameInfo.log(log);
282+
if (InterpreterSupport.isEnabled() && invocationCount == 1 && InterpreterSupport.singleton().isInterpreterRoot(frameInfo)) {
283+
InterpreterSupport.singleton().logInterpreterFrame(log, frameInfo, sp);
284+
}
269285
}
270286

271287
private static void printFrameIdentifier(Log log, CodeInfo codeInfo, DeoptimizedFrame deoptFrame, boolean isCompilationRoot, boolean isNative) {

substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterSupportImpl.java

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,23 @@
3636
import com.oracle.svm.core.code.FrameInfoQueryResult;
3737
import com.oracle.svm.core.code.FrameSourceInfo;
3838
import com.oracle.svm.core.heap.ReferenceAccess;
39+
import com.oracle.svm.core.heap.RestrictHeapAccess;
3940
import com.oracle.svm.core.interpreter.InterpreterFrameSourceInfo;
4041
import com.oracle.svm.core.interpreter.InterpreterSupport;
42+
import com.oracle.svm.core.log.Log;
4143
import com.oracle.svm.core.util.VMError;
44+
import com.oracle.svm.espresso.classfile.descriptors.ByteSequence;
45+
import com.oracle.svm.espresso.classfile.descriptors.Name;
46+
import com.oracle.svm.espresso.classfile.descriptors.Symbol;
4247
import com.oracle.svm.interpreter.metadata.InterpreterResolvedJavaMethod;
4348

4449
import jdk.graal.compiler.word.Word;
4550
import jdk.vm.ci.meta.LineNumberTable;
4651
import jdk.vm.ci.meta.ResolvedJavaMethod;
4752

4853
public final class InterpreterSupportImpl extends InterpreterSupport {
54+
private static final int MAX_SYMBOL_LOG_LENGTH = 255;
55+
4956
private final int bciSlot;
5057
private final int interpretedMethodSlot;
5158
private final int interpretedFrameSlot;
@@ -135,6 +142,93 @@ public FrameSourceInfo getInterpretedMethodFrameInfo(FrameInfoQueryResult frameI
135142
throw VMError.shouldNotReachHereAtRuntime();
136143
}
137144

145+
@Override
146+
@RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Used for crash log")
147+
public void logInterpreterFrame(Log log, FrameInfoQueryResult frameInfo, Pointer sp) {
148+
if (isInterpreterIntrinsicRoot(frameInfo)) {
149+
logInterpreterIntrinsicFrame(log, frameInfo, sp);
150+
} else if (isInterpreterBytecodeRoot(frameInfo)) {
151+
logInterpreterBytecodeFrame(log, frameInfo, sp);
152+
} else {
153+
throw VMError.shouldNotReachHereAtRuntime();
154+
}
155+
}
156+
157+
private void logInterpreterBytecodeFrame(Log log, FrameInfoQueryResult frameInfo, Pointer sp) {
158+
if (!frameInfo.hasLocalValueInfo()) {
159+
log.string(" missing local value info (bytecode)");
160+
return;
161+
}
162+
InterpreterResolvedJavaMethod interpretedMethod = readInterpretedMethod(frameInfo, sp);
163+
if (interpretedMethod == null) {
164+
log.string(" no interpreter method (bytecode)");
165+
return;
166+
}
167+
int bci = readBCI(frameInfo, sp);
168+
logInterpreterMethod(log, interpretedMethod, bci);
169+
}
170+
171+
private void logInterpreterIntrinsicFrame(Log log, FrameInfoQueryResult frameInfo, Pointer sp) {
172+
if (!frameInfo.hasLocalValueInfo()) {
173+
log.string(" missing local value info (intrinsic)");
174+
return;
175+
}
176+
InterpreterResolvedJavaMethod intrinsicMethod = readIntrinsicMethod(frameInfo, sp);
177+
if (intrinsicMethod == null) {
178+
log.string(" no interpreter method (intrinsic)");
179+
return;
180+
}
181+
logInterpreterMethod(log, intrinsicMethod, -1);
182+
}
183+
184+
private static void logInterpreterMethod(Log log, InterpreterResolvedJavaMethod interpretedMethod, int bci) {
185+
String sourceHolderName = interpretedMethod.getDeclaringClass().getJavaClass().getName();
186+
Symbol<Name> sourceMethodName = interpretedMethod.getSymbolicName();
187+
LineNumberTable lineNumberTable = interpretedMethod.getLineNumberTable();
188+
int sourceLineNumber = -1; // unknown
189+
if (lineNumberTable != null && bci >= 0) {
190+
sourceLineNumber = lineNumberTable.getLineNumber(bci);
191+
}
192+
log.spaces(2);
193+
log.string(sourceHolderName);
194+
log.character('.');
195+
logSymbol(log, sourceMethodName);
196+
String sourceFileName = interpretedMethod.getDeclaringClass().getSourceFileName();
197+
if (sourceFileName == null && sourceLineNumber >= 0) {
198+
sourceFileName = "Unknown Source";
199+
}
200+
if (sourceFileName != null) {
201+
log.character('(');
202+
log.string(sourceFileName);
203+
if (sourceLineNumber >= 0) {
204+
log.string(":");
205+
log.signed(sourceLineNumber);
206+
}
207+
log.character(')');
208+
}
209+
if (bci >= 0) {
210+
log.spaces(1);
211+
log.string("@bci ");
212+
log.signed(bci);
213+
}
214+
}
215+
216+
private static void logSymbol(Log log, ByteSequence byteSequence) {
217+
int length = Math.min(byteSequence.length(), MAX_SYMBOL_LOG_LENGTH);
218+
for (int i = 0; i < length; i++) {
219+
int b = byteSequence.unsignedByteAt(i);
220+
if (0x20 <= b && b <= 0x7e) {
221+
// only log printable ascii
222+
log.character((char) b);
223+
} else {
224+
log.character('?');
225+
}
226+
}
227+
if (byteSequence.length() > MAX_SYMBOL_LOG_LENGTH) {
228+
log.string("...");
229+
}
230+
}
231+
138232
@Platforms(Platform.HOSTED_ONLY.class)
139233
@Override
140234
public void buildMethodIdMapping(ResolvedJavaMethod[] encodedMethods) {

0 commit comments

Comments
 (0)