Skip to content

Commit d05f810

Browse files
committed
Implement virtual thread stack traces.
1 parent 5f3d445 commit d05f810

16 files changed

+478
-135
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuationAccess.java

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -187,39 +187,18 @@ public static boolean walkReferences(Pointer baseAddress, ObjectReferenceVisitor
187187
StoredContinuation s = (StoredContinuation) holderObject;
188188
assert baseAddress.equal(Word.objectToUntrackedPointer(holderObject));
189189

190-
CodePointer startIp = getIP(s);
191-
if (startIp.isNull()) {
190+
JavaStackWalk walk = StackValue.get(JavaStackWalk.class);
191+
if (!initWalk(s, walk)) {
192192
return true; // uninitialized, ignore
193193
}
194194

195-
Pointer startSp = getFramesStart(s);
196-
Pointer endSp = arrayAddress(s).add(getSizeInBytes(s));
197-
198-
JavaStackWalk walk = StackValue.get(JavaStackWalk.class);
199-
JavaStackWalker.initWalk(walk, startSp, endSp, startIp);
200-
201195
SimpleCodeInfoQueryResult queryResult = StackValue.get(SimpleCodeInfoQueryResult.class);
202196
do {
203-
Pointer sp = walk.getSP();
204-
CodePointer ip = walk.getPossiblyStaleIP();
205-
206197
UntetheredCodeInfo untetheredCodeInfo = walk.getIPCodeInfo();
207198
Object tether = CodeInfoAccess.acquireTether(untetheredCodeInfo);
208199
try {
209-
CodeInfo codeInfo = CodeInfoAccess.convert(untetheredCodeInfo);
210-
VMError.guarantee(codeInfo.equal(CodeInfoTable.getImageCodeInfo()));
211-
VMError.guarantee(Deoptimizer.checkDeoptimized(sp) == null);
212-
if (codeInfo.isNull()) {
213-
throw JavaStackWalker.reportUnknownFrameEncountered(sp, ip, null);
214-
}
215-
216-
CodeInfoAccess.lookupCodeInfo(codeInfo, CodeInfoAccess.relativeIP(codeInfo, ip), queryResult);
217-
218-
NonmovableArray<Byte> referenceMapEncoding = CodeInfoAccess.getStackReferenceMapEncoding(codeInfo);
219-
long referenceMapIndex = queryResult.getReferenceMapIndex();
220-
if (referenceMapIndex != ReferenceMapIndex.NO_REFERENCE_MAP) {
221-
CodeReferenceMapDecoder.walkOffsetsFromPointer(sp, referenceMapEncoding, referenceMapIndex, visitor, holderObject);
222-
}
200+
CodeInfo codeInfo = CodeInfoAccess.convert(untetheredCodeInfo, tether);
201+
walkFrameReferences(walk, codeInfo, queryResult, visitor, holderObject);
223202
} finally {
224203
CodeInfoAccess.releaseTether(untetheredCodeInfo, tether);
225204
}
@@ -228,6 +207,45 @@ public static boolean walkReferences(Pointer baseAddress, ObjectReferenceVisitor
228207
return true;
229208
}
230209

210+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
211+
public static boolean initWalk(StoredContinuation s, JavaStackWalk walk) {
212+
CodePointer startIp = getIP(s);
213+
if (startIp.isNull()) {
214+
return false; // uninitialized
215+
}
216+
217+
initWalk(s, walk, startIp);
218+
return true;
219+
}
220+
221+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
222+
public static void initWalk(StoredContinuation s, JavaStackWalk walk, CodePointer startIp) {
223+
Pointer startSp = getFramesStart(s);
224+
Pointer endSp = arrayAddress(s).add(getSizeInBytes(s));
225+
226+
JavaStackWalker.initWalk(walk, startSp, endSp, startIp);
227+
walk.setAnchor(WordFactory.nullPointer()); // never use an anchor of this platform thread
228+
}
229+
230+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
231+
public static void walkFrameReferences(JavaStackWalk walk, CodeInfo codeInfo, SimpleCodeInfoQueryResult queryResult, ObjectReferenceVisitor visitor, Object holderObject) {
232+
Pointer sp = walk.getSP();
233+
CodePointer ip = walk.getPossiblyStaleIP();
234+
if (codeInfo.isNull()) {
235+
throw JavaStackWalker.reportUnknownFrameEncountered(sp, ip, null);
236+
}
237+
VMError.guarantee(codeInfo.equal(CodeInfoTable.getImageCodeInfo()));
238+
VMError.guarantee(Deoptimizer.checkDeoptimized(sp) == null);
239+
240+
CodeInfoAccess.lookupCodeInfo(codeInfo, CodeInfoAccess.relativeIP(codeInfo, ip), queryResult);
241+
242+
NonmovableArray<Byte> referenceMapEncoding = CodeInfoAccess.getStackReferenceMapEncoding(codeInfo);
243+
long referenceMapIndex = queryResult.getReferenceMapIndex();
244+
if (referenceMapIndex != ReferenceMapIndex.NO_REFERENCE_MAP) {
245+
CodeReferenceMapDecoder.walkOffsetsFromPointer(sp, referenceMapEncoding, referenceMapIndex, visitor, holderObject);
246+
}
247+
}
248+
231249
private static final class PreemptVisitor extends StackFrameVisitor {
232250
private final Pointer endSP;
233251
private boolean startFromNextFrame = false;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@
6969
import com.oracle.svm.core.hub.DynamicHub;
7070
import com.oracle.svm.core.jdk.JavaLangSubstitutions.ClassValueSupport;
7171
import com.oracle.svm.core.monitor.MonitorSupport;
72-
import com.oracle.svm.core.snippets.KnownIntrinsics;
7372
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
73+
import com.oracle.svm.core.thread.JavaThreads;
7474
import com.oracle.svm.core.thread.VirtualThreads;
7575
import com.oracle.svm.core.util.VMError;
7676

@@ -228,7 +228,7 @@ final class Target_java_lang_Throwable {
228228
@Substitute
229229
@NeverInline("Starting a stack walk in the caller frame")
230230
private Object fillInStackTrace() {
231-
stackTrace = StackTraceUtils.getStackTrace(true, KnownIntrinsics.readCallerStackPointer());
231+
stackTrace = JavaThreads.getStackTrace(true, Thread.currentThread());
232232
return this;
233233
}
234234

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StackTraceUtils.java

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,33 @@
2424
*/
2525
package com.oracle.svm.core.jdk;
2626

27+
import static com.oracle.svm.core.snippets.KnownIntrinsics.readCallerStackPointer;
28+
2729
import java.security.AccessControlContext;
2830
import java.security.AccessController;
2931
import java.security.ProtectionDomain;
3032
import java.util.ArrayList;
3133

3234
import org.graalvm.nativeimage.IsolateThread;
35+
import org.graalvm.nativeimage.c.function.CodePointer;
3336
import org.graalvm.word.Pointer;
3437

3538
import com.oracle.svm.core.SubstrateOptions;
3639
import com.oracle.svm.core.SubstrateUtil;
3740
import com.oracle.svm.core.annotate.NeverInline;
3841
import com.oracle.svm.core.code.FrameInfoQueryResult;
42+
import com.oracle.svm.core.heap.VMOperationInfos;
3943
import com.oracle.svm.core.snippets.KnownIntrinsics;
4044
import com.oracle.svm.core.stack.JavaStackFrameVisitor;
4145
import com.oracle.svm.core.stack.JavaStackWalker;
46+
import com.oracle.svm.core.thread.JavaThreads;
47+
import com.oracle.svm.core.thread.JavaVMOperation;
4248
import com.oracle.svm.core.thread.LoomSupport;
49+
import com.oracle.svm.core.thread.PlatformThreads;
50+
import com.oracle.svm.core.thread.Target_java_lang_Thread;
4351
import com.oracle.svm.core.thread.Target_jdk_internal_vm_Continuation;
52+
import com.oracle.svm.core.thread.VMOperation;
53+
import com.oracle.svm.core.thread.VirtualThreads;
4454
import com.oracle.svm.util.DirectAnnotationAccess;
4555

4656
import jdk.vm.ci.meta.MetaAccessProvider;
@@ -53,28 +63,45 @@ public class StackTraceUtils {
5363
private static final StackTraceElement[] NO_ELEMENTS = new StackTraceElement[0];
5464

5565
/**
56-
* Captures the stack trace of the current thread. Used by {@link Throwable#fillInStackTrace()},
57-
* {@link Thread#getStackTrace()}, and {@link Thread#getAllStackTraces()}.
66+
* Captures the stack trace of the current thread. In almost any context, calling
67+
* {@link JavaThreads#getStackTrace} for {@link Thread#currentThread()} is preferable.
5868
*
5969
* Captures at most {@link SubstrateOptions#MaxJavaStackTraceDepth} stack trace elements if max
6070
* depth > 0, or all if max depth <= 0.
6171
*/
62-
public static StackTraceElement[] getStackTrace(boolean filterExceptions, Pointer startSP) {
72+
public static StackTraceElement[] getStackTrace(boolean filterExceptions, Pointer startSP, Pointer endSP) {
6373
BuildStackTraceVisitor visitor = new BuildStackTraceVisitor(filterExceptions, SubstrateOptions.MaxJavaStackTraceDepth.getValue());
64-
JavaStackWalker.walkCurrentThread(startSP, visitor);
74+
JavaStackWalker.walkCurrentThread(startSP, endSP, visitor);
6575
return visitor.trace.toArray(NO_ELEMENTS);
6676
}
6777

6878
/**
69-
* Captures the stack trace of another thread. Used by {@link Thread#getStackTrace()} and
70-
* {@link Thread#getAllStackTraces()}.
79+
* Captures the stack trace of a thread (potentially the current thread) while stopped at a
80+
* safepoint. Used by {@link Thread#getStackTrace()} and {@link Thread#getAllStackTraces()}.
7181
*
7282
* Captures at most {@link SubstrateOptions#MaxJavaStackTraceDepth} stack trace elements if max
7383
* depth > 0, or all if max depth <= 0.
7484
*/
75-
public static StackTraceElement[] getStackTrace(boolean filterExceptions, IsolateThread thread) {
76-
BuildStackTraceVisitor visitor = new BuildStackTraceVisitor(filterExceptions, SubstrateOptions.MaxJavaStackTraceDepth.getValue());
77-
JavaStackWalker.walkThread(thread, visitor);
85+
@NeverInline("Potentially starting a stack walk in the caller frame")
86+
public static StackTraceElement[] getStackTraceAtSafepoint(Thread thread) {
87+
assert VMOperation.isInProgressAtSafepoint();
88+
if (VirtualThreads.isSupported()) { // NOTE: also for platform threads!
89+
return VirtualThreads.singleton().getVirtualOrPlatformThreadStackTraceAtSafepoint(thread, readCallerStackPointer());
90+
}
91+
return PlatformThreads.getStackTraceAtSafepoint(thread, readCallerStackPointer());
92+
}
93+
94+
public static StackTraceElement[] getThreadStackTraceAtSafepoint(IsolateThread isolateThread, Pointer endSP) {
95+
assert VMOperation.isInProgressAtSafepoint();
96+
BuildStackTraceVisitor visitor = new BuildStackTraceVisitor(false, SubstrateOptions.MaxJavaStackTraceDepth.getValue());
97+
JavaStackWalker.walkThread(isolateThread, endSP, visitor);
98+
return visitor.trace.toArray(NO_ELEMENTS);
99+
}
100+
101+
public static StackTraceElement[] getThreadStackTraceAtSafepoint(Pointer startSP, Pointer endSP, CodePointer startIP) {
102+
assert VMOperation.isInProgressAtSafepoint();
103+
BuildStackTraceVisitor visitor = new BuildStackTraceVisitor(false, SubstrateOptions.MaxJavaStackTraceDepth.getValue());
104+
JavaStackWalker.walkThreadAtSafepoint(startSP, endSP, startIP, visitor);
78105
return visitor.trace.toArray(NO_ELEMENTS);
79106
}
80107

@@ -183,6 +210,31 @@ public static ClassLoader latestUserDefinedClassLoader(Pointer startSP) {
183210
JavaStackWalker.walkCurrentThread(startSP, visitor);
184211
return visitor.result;
185212
}
213+
214+
public static StackTraceElement[] asyncGetStackTrace(Thread thread) {
215+
GetStackTraceOperation vmOp = new GetStackTraceOperation(thread);
216+
vmOp.enqueue();
217+
return vmOp.result;
218+
}
219+
220+
private static class GetStackTraceOperation extends JavaVMOperation {
221+
private final Thread thread;
222+
StackTraceElement[] result;
223+
224+
GetStackTraceOperation(Thread thread) {
225+
super(VMOperationInfos.get(GetStackTraceOperation.class, "Get stack trace", SystemEffect.SAFEPOINT));
226+
this.thread = thread;
227+
}
228+
229+
@Override
230+
protected void operate() {
231+
if (thread.isAlive()) {
232+
result = getStackTraceAtSafepoint(thread);
233+
} else {
234+
result = Target_java_lang_Thread.EMPTY_STACK_TRACE;
235+
}
236+
}
237+
}
186238
}
187239

188240
class BuildStackTraceVisitor extends JavaStackFrameVisitor {

0 commit comments

Comments
 (0)