diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 6b77c0e266a3..2c885923aa66 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -8,6 +8,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-39497) Add `-H:BuildOutputJSONFile=` option for [JSON build output](https://github.com/oracle/graal/edit/master/docs/reference-manual/native-image/BuildOutput.md#machine-readable-build-output). Please feel free to provide feedback so that we can stabilize the schema/API. * (GR-40170) Add `--silent` option to silence the build output. * (GR-39475) Add initial support for jvmstat. +* (GR-39563) Add support for JDK 19 and Project Loom Virtual Threads (JEP 425) for high-throughput lightweight concurrency. Enable on JDK 19 with `native-image -J--enable-preview`. ## Version 22.2.0 * (GR-20653) Re-enable the usage of all CPU features for JIT compilation on AMD64. diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java index d9d2b1ab8b31..1561c2f5a21f 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java @@ -80,7 +80,7 @@ import com.oracle.svm.core.option.RuntimeOptionKey; import com.oracle.svm.core.os.CommittedMemoryProvider; import com.oracle.svm.core.snippets.KnownIntrinsics; -import com.oracle.svm.core.thread.JavaThreads; +import com.oracle.svm.core.thread.PlatformThreads; import com.oracle.svm.core.thread.ThreadStatus; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.thread.VMThreads; @@ -561,12 +561,12 @@ public void waitForReferencePendingList() throws InterruptedException { private static boolean waitForPendingReferenceList(long initialOffers, long initialWakeUps) { Thread currentThread = Thread.currentThread(); - int oldThreadStatus = JavaThreads.getThreadStatus(currentThread); - JavaThreads.setThreadStatus(currentThread, ThreadStatus.PARKED); + int oldThreadStatus = PlatformThreads.getThreadStatus(currentThread); + PlatformThreads.setThreadStatus(currentThread, ThreadStatus.PARKED); try { return transitionToNativeThenAwaitPendingRefs(initialOffers, initialWakeUps); } finally { - JavaThreads.setThreadStatus(currentThread, oldThreadStatus); + PlatformThreads.setThreadStatus(currentThread, oldThreadStatus); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index d125b205cf10..8352a579abea 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -134,16 +134,6 @@ protected void onValueUpdate(EconomicMap, Object> values, Boolean o @Option(help = "Support continuations (without requiring a Project Loom JDK)") // public static final HostedOptionKey SupportContinuations = new HostedOptionKey<>(false); - @Option(help = "Build with Project Loom JDK") // - public static final HostedOptionKey UseLoom = new HostedOptionKey<>(false) { - @Override - protected void onValueUpdate(EconomicMap, Object> values, Boolean oldValue, Boolean newValue) { - if (newValue) { - SupportContinuations.update(values, true); - } - } - }; - public static final int ForceFallback = 10; public static final int Automatic = 5; public static final int NoFallback = 0; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuationAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuationAccess.java index cecb07f06040..27f3e263eb6a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuationAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuationAccess.java @@ -125,7 +125,7 @@ private static int allocateFromStack(Continuation cont, Pointer baseSp, Pointer if (!yield) { PreemptVisitor visitor = new PreemptVisitor(baseSp); JavaStackWalker.walkThread(targetThread, visitor); - if (visitor.preemptStatus != Continuation.YIELD_SUCCESS) { + if (visitor.preemptStatus != Continuation.FREEZE_OK) { return visitor.preemptStatus; } startSp = visitor.leafSP; @@ -138,7 +138,7 @@ private static int allocateFromStack(Continuation cont, Pointer baseSp, Pointer StoredContinuation instance = allocate(framesSize); fillUninterruptibly(instance, startIp, startSp, framesSize); cont.stored = instance; - return Continuation.YIELD_SUCCESS; + return Continuation.FREEZE_OK; } @Uninterruptible(reason = "Prevent modifications to the stack while initializing instance and copying frames.") @@ -187,39 +187,18 @@ public static boolean walkReferences(Pointer baseAddress, ObjectReferenceVisitor StoredContinuation s = (StoredContinuation) holderObject; assert baseAddress.equal(Word.objectToUntrackedPointer(holderObject)); - CodePointer startIp = getIP(s); - if (startIp.isNull()) { + JavaStackWalk walk = StackValue.get(JavaStackWalk.class); + if (!initWalk(s, walk)) { return true; // uninitialized, ignore } - Pointer startSp = getFramesStart(s); - Pointer endSp = arrayAddress(s).add(getSizeInBytes(s)); - - JavaStackWalk walk = StackValue.get(JavaStackWalk.class); - JavaStackWalker.initWalk(walk, startSp, endSp, startIp); - SimpleCodeInfoQueryResult queryResult = StackValue.get(SimpleCodeInfoQueryResult.class); do { - Pointer sp = walk.getSP(); - CodePointer ip = walk.getPossiblyStaleIP(); - UntetheredCodeInfo untetheredCodeInfo = walk.getIPCodeInfo(); Object tether = CodeInfoAccess.acquireTether(untetheredCodeInfo); try { - CodeInfo codeInfo = CodeInfoAccess.convert(untetheredCodeInfo); - VMError.guarantee(codeInfo.equal(CodeInfoTable.getImageCodeInfo())); - VMError.guarantee(Deoptimizer.checkDeoptimized(sp) == null); - if (codeInfo.isNull()) { - throw JavaStackWalker.reportUnknownFrameEncountered(sp, ip, null); - } - - CodeInfoAccess.lookupCodeInfo(codeInfo, CodeInfoAccess.relativeIP(codeInfo, ip), queryResult); - - NonmovableArray referenceMapEncoding = CodeInfoAccess.getStackReferenceMapEncoding(codeInfo); - long referenceMapIndex = queryResult.getReferenceMapIndex(); - if (referenceMapIndex != ReferenceMapIndex.NO_REFERENCE_MAP) { - CodeReferenceMapDecoder.walkOffsetsFromPointer(sp, referenceMapEncoding, referenceMapIndex, visitor, holderObject); - } + CodeInfo codeInfo = CodeInfoAccess.convert(untetheredCodeInfo, tether); + walkFrameReferences(walk, codeInfo, queryResult, visitor, holderObject); } finally { CodeInfoAccess.releaseTether(untetheredCodeInfo, tether); } @@ -228,13 +207,52 @@ public static boolean walkReferences(Pointer baseAddress, ObjectReferenceVisitor return true; } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static boolean initWalk(StoredContinuation s, JavaStackWalk walk) { + CodePointer startIp = getIP(s); + if (startIp.isNull()) { + return false; // uninitialized + } + + initWalk(s, walk, startIp); + return true; + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static void initWalk(StoredContinuation s, JavaStackWalk walk, CodePointer startIp) { + Pointer startSp = getFramesStart(s); + Pointer endSp = arrayAddress(s).add(getSizeInBytes(s)); + + JavaStackWalker.initWalk(walk, startSp, endSp, startIp); + walk.setAnchor(WordFactory.nullPointer()); // never use an anchor of this platform thread + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static void walkFrameReferences(JavaStackWalk walk, CodeInfo codeInfo, SimpleCodeInfoQueryResult queryResult, ObjectReferenceVisitor visitor, Object holderObject) { + Pointer sp = walk.getSP(); + CodePointer ip = walk.getPossiblyStaleIP(); + if (codeInfo.isNull()) { + throw JavaStackWalker.reportUnknownFrameEncountered(sp, ip, null); + } + VMError.guarantee(codeInfo.equal(CodeInfoTable.getImageCodeInfo())); + VMError.guarantee(Deoptimizer.checkDeoptimized(sp) == null); + + CodeInfoAccess.lookupCodeInfo(codeInfo, CodeInfoAccess.relativeIP(codeInfo, ip), queryResult); + + NonmovableArray referenceMapEncoding = CodeInfoAccess.getStackReferenceMapEncoding(codeInfo); + long referenceMapIndex = queryResult.getReferenceMapIndex(); + if (referenceMapIndex != ReferenceMapIndex.NO_REFERENCE_MAP) { + CodeReferenceMapDecoder.walkOffsetsFromPointer(sp, referenceMapEncoding, referenceMapIndex, visitor, holderObject); + } + } + private static final class PreemptVisitor extends StackFrameVisitor { private final Pointer endSP; private boolean startFromNextFrame = false; Pointer leafSP; CodePointer leafIP; - int preemptStatus = Continuation.YIELD_SUCCESS; + int preemptStatus = Continuation.FREEZE_OK; PreemptVisitor(Pointer endSP) { this.endSP = endSP; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/DisallowedImageHeapObjects.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/DisallowedImageHeapObjects.java index 8f7c0574bb11..e86e9131a3ea 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/DisallowedImageHeapObjects.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/image/DisallowedImageHeapObjects.java @@ -26,14 +26,15 @@ import java.io.FileDescriptor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.MappedByteBuffer; import java.util.Random; import java.util.SplittableRandom; import java.util.concurrent.ThreadLocalRandom; -import com.oracle.svm.core.thread.LoomSupport; -import com.oracle.svm.core.thread.Target_java_lang_Continuation; +import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.thread.VirtualThreads; import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ReflectionUtil; @@ -48,12 +49,14 @@ public interface DisallowedObjectReporter { } private static final Class CANCELLABLE_CLASS; + private static final Class JDK_VIRTUAL_THREAD_CLASS; + private static final Class CONTINUATION_CLASS; + private static final Method CONTINUATION_IS_STARTED_METHOD; static { - try { - CANCELLABLE_CLASS = Class.forName("sun.nio.fs.Cancellable"); - } catch (ClassNotFoundException ex) { - throw VMError.shouldNotReachHere(ex); - } + CANCELLABLE_CLASS = ReflectionUtil.lookupClass(false, "sun.nio.fs.Cancellable"); + JDK_VIRTUAL_THREAD_CLASS = ReflectionUtil.lookupClass(true, "java.lang.VirtualThread"); + CONTINUATION_CLASS = ReflectionUtil.lookupClass(true, "jdk.internal.vm.Continuation"); + CONTINUATION_IS_STARTED_METHOD = (CONTINUATION_CLASS == null) ? null : ReflectionUtil.lookupMethod(false, CONTINUATION_CLASS, "isStarted"); } public static void check(Object obj, DisallowedObjectReporter reporter) { @@ -64,24 +67,29 @@ public static void check(Object obj, DisallowedObjectReporter reporter) { obj, "Try avoiding to initialize the class that caused initialization of the object."); } - /* Started Threads can not be in the image heap. */ + /* Started platform threads can not be in the image heap. */ if (obj instanceof Thread) { final Thread asThread = (Thread) obj; - if (VirtualThreads.isSupported() && VirtualThreads.singleton().isVirtual(asThread)) { + if (VirtualThreads.isSupported() && (VirtualThreads.singleton().isVirtual(asThread) || (JDK_VIRTUAL_THREAD_CLASS != null && JDK_VIRTUAL_THREAD_CLASS.isInstance(asThread)))) { // allowed unless the thread is mounted, in which case it references its carrier // thread and fails } else if (asThread.getState() != Thread.State.NEW && asThread.getState() != Thread.State.TERMINATED) { throw reporter.raise("Detected a started Thread in the image heap. " + "Threads running in the image generator are no longer running at image runtime.", - asThread, "Try avoiding to initialize the class that caused initialization of the Thread."); + asThread, "Prevent threads from starting during image generation, or a started thread from being included in the image."); } } - if (obj instanceof Target_java_lang_Continuation) { - final Target_java_lang_Continuation asCont = (Target_java_lang_Continuation) obj; - if (LoomSupport.isStarted(asCont)) { + if (SubstrateUtil.HOSTED && CONTINUATION_CLASS != null && CONTINUATION_CLASS.isInstance(obj)) { + boolean isStarted; + try { + isStarted = (Boolean) CONTINUATION_IS_STARTED_METHOD.invoke(obj); + } catch (IllegalAccessException | InvocationTargetException ignored) { + isStarted = false; + } + if (isStarted) { throw reporter.raise("Detected a started Continuation in the image heap. " + - "Continuations running in the image generator are no longer running at image runtime.", - asCont, "Try avoiding to initialize the class that caused initialization of the Continuation."); + "Continuation state from the image generator cannot be used at image runtime.", + obj, "Prevent continuations from starting during image generation, or started continuations from being included in the image."); } } /* FileDescriptors can not be in the image heap. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java index b9512f79b146..828d0a366ce7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java @@ -69,8 +69,8 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.JavaLangSubstitutions.ClassValueSupport; import com.oracle.svm.core.monitor.MonitorSupport; -import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.snippets.SubstrateForeignCallTarget; +import com.oracle.svm.core.thread.JavaThreads; import com.oracle.svm.core.util.VMError; import jdk.vm.ci.meta.MetaAccessProvider; @@ -99,16 +99,21 @@ private int hashCodeSubst() { } @Substitute - @TargetElement(name = "wait", onlyWith = JDK17OrEarlier.class) + @TargetElement(name = "wait") private void waitSubst(long timeoutMillis) throws InterruptedException { + /* + * JDK 19 and later: our monitor implementation does not pin virtual threads, so avoid + * jdk.internal.misc.Blocker which expects and asserts that a virtual thread is pinned. + * Also, we get interrupted on the virtual thread instead of the carrier thread, which + * clears the carrier thread's interrupt status too, so we don't have to intercept an + * InterruptedException from the carrier thread to clear the virtual thread interrupt. + */ MonitorSupport.singleton().wait(this, timeoutMillis); } - @Substitute - @TargetElement(name = "wait0", onlyWith = JDK19OrLater.class) - private void waitSubstLoom(long timeoutMillis) throws InterruptedException { - MonitorSupport.singleton().wait(this, timeoutMillis); - } + @Delete + @TargetElement(onlyWith = JDK19OrLater.class) + private native void wait0(long timeoutMillis); @Substitute @TargetElement(name = "notify") @@ -211,7 +216,7 @@ final class Target_java_lang_Throwable { @Substitute @NeverInline("Starting a stack walk in the caller frame") private Object fillInStackTrace() { - stackTrace = StackTraceUtils.getStackTrace(true, KnownIntrinsics.readCallerStackPointer()); + stackTrace = JavaThreads.getStackTrace(true, Thread.currentThread()); return this; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java index 49cf2b91cf3e..5f0f6751133b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java @@ -48,6 +48,7 @@ import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport; +import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.AutomaticFeature; @@ -332,6 +333,19 @@ public static int getCommonPoolParallelism() { @TargetElement(onlyWith = JDK17OrEarlier.class)// static int COMMON_PARALLELISM; + @Alias @TargetElement(onlyWith = JDK19OrLater.class) // + private static Unsafe U; + + @Alias @TargetElement(onlyWith = JDK19OrLater.class) // + private static long POOLIDS; + + @Substitute + @TargetElement(onlyWith = JDK19OrLater.class) // + private static int getAndAddPoolIds(int x) { + // Original method wrongly uses ForkJoinPool.class instead of calling U.staticFieldBase() + return U.getAndAddInt(StaticFieldsSupport.getStaticPrimitiveFields(), POOLIDS, x); + } + @Alias // Target_java_util_concurrent_ForkJoinPool(byte forCommonPoolOnly) { } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StackTraceUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StackTraceUtils.java index e770d2fab397..7b67f977b8b5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StackTraceUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StackTraceUtils.java @@ -24,24 +24,34 @@ */ package com.oracle.svm.core.jdk; +import static com.oracle.svm.core.snippets.KnownIntrinsics.readCallerStackPointer; + import java.security.AccessControlContext; import java.security.AccessController; import java.security.ProtectionDomain; import java.util.ArrayList; import org.graalvm.nativeimage.IsolateThread; -import com.oracle.svm.util.DirectAnnotationAccess; +import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.word.Pointer; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.code.FrameInfoQueryResult; +import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.stack.JavaStackFrameVisitor; import com.oracle.svm.core.stack.JavaStackWalker; +import com.oracle.svm.core.thread.JavaThreads; +import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.thread.LoomSupport; -import com.oracle.svm.core.thread.Target_java_lang_Continuation; +import com.oracle.svm.core.thread.PlatformThreads; +import com.oracle.svm.core.thread.Target_java_lang_Thread; +import com.oracle.svm.core.thread.Target_jdk_internal_vm_Continuation; +import com.oracle.svm.core.thread.VMOperation; +import com.oracle.svm.core.thread.VirtualThreads; +import com.oracle.svm.util.DirectAnnotationAccess; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -53,28 +63,45 @@ public class StackTraceUtils { private static final StackTraceElement[] NO_ELEMENTS = new StackTraceElement[0]; /** - * Captures the stack trace of the current thread. Used by {@link Throwable#fillInStackTrace()}, - * {@link Thread#getStackTrace()}, and {@link Thread#getAllStackTraces()}. + * Captures the stack trace of the current thread. In almost any context, calling + * {@link JavaThreads#getStackTrace} for {@link Thread#currentThread()} is preferable. * * Captures at most {@link SubstrateOptions#MaxJavaStackTraceDepth} stack trace elements if max * depth > 0, or all if max depth <= 0. */ - public static StackTraceElement[] getStackTrace(boolean filterExceptions, Pointer startSP) { + public static StackTraceElement[] getStackTrace(boolean filterExceptions, Pointer startSP, Pointer endSP) { BuildStackTraceVisitor visitor = new BuildStackTraceVisitor(filterExceptions, SubstrateOptions.MaxJavaStackTraceDepth.getValue()); - JavaStackWalker.walkCurrentThread(startSP, visitor); + JavaStackWalker.walkCurrentThread(startSP, endSP, visitor); return visitor.trace.toArray(NO_ELEMENTS); } /** - * Captures the stack trace of another thread. Used by {@link Thread#getStackTrace()} and - * {@link Thread#getAllStackTraces()}. + * Captures the stack trace of a thread (potentially the current thread) while stopped at a + * safepoint. Used by {@link Thread#getStackTrace()} and {@link Thread#getAllStackTraces()}. * * Captures at most {@link SubstrateOptions#MaxJavaStackTraceDepth} stack trace elements if max * depth > 0, or all if max depth <= 0. */ - public static StackTraceElement[] getStackTrace(boolean filterExceptions, IsolateThread thread) { - BuildStackTraceVisitor visitor = new BuildStackTraceVisitor(filterExceptions, SubstrateOptions.MaxJavaStackTraceDepth.getValue()); - JavaStackWalker.walkThread(thread, visitor); + @NeverInline("Potentially starting a stack walk in the caller frame") + public static StackTraceElement[] getStackTraceAtSafepoint(Thread thread) { + assert VMOperation.isInProgressAtSafepoint(); + if (VirtualThreads.isSupported()) { // NOTE: also for platform threads! + return VirtualThreads.singleton().getVirtualOrPlatformThreadStackTraceAtSafepoint(thread, readCallerStackPointer()); + } + return PlatformThreads.getStackTraceAtSafepoint(thread, readCallerStackPointer()); + } + + public static StackTraceElement[] getThreadStackTraceAtSafepoint(IsolateThread isolateThread, Pointer endSP) { + assert VMOperation.isInProgressAtSafepoint(); + BuildStackTraceVisitor visitor = new BuildStackTraceVisitor(false, SubstrateOptions.MaxJavaStackTraceDepth.getValue()); + JavaStackWalker.walkThread(isolateThread, endSP, visitor, null); + return visitor.trace.toArray(NO_ELEMENTS); + } + + public static StackTraceElement[] getThreadStackTraceAtSafepoint(Pointer startSP, Pointer endSP, CodePointer startIP) { + assert VMOperation.isInProgressAtSafepoint(); + BuildStackTraceVisitor visitor = new BuildStackTraceVisitor(false, SubstrateOptions.MaxJavaStackTraceDepth.getValue()); + JavaStackWalker.walkThreadAtSafepoint(startSP, endSP, startIP, visitor); return visitor.trace.toArray(NO_ELEMENTS); } @@ -137,11 +164,9 @@ public static boolean shouldShowFrame(FrameInfoQueryResult frameInfo, boolean sh return false; } - if (LoomSupport.isEnabled() && clazz == Target_java_lang_Continuation.class) { - // Skip intrinsics in JDK - if ("enterSpecial".equals(frameInfo.getSourceMethodName())) { - return false; - } else if ("doYield".equals(frameInfo.getSourceMethodName())) { + if (LoomSupport.isEnabled() && clazz == Target_jdk_internal_vm_Continuation.class) { + String name = frameInfo.getSourceMethodName(); + if ("enter0".equals(name) || "enterSpecial".equals(name)) { return false; } } @@ -185,6 +210,31 @@ public static ClassLoader latestUserDefinedClassLoader(Pointer startSP) { JavaStackWalker.walkCurrentThread(startSP, visitor); return visitor.result; } + + public static StackTraceElement[] asyncGetStackTrace(Thread thread) { + GetStackTraceOperation vmOp = new GetStackTraceOperation(thread); + vmOp.enqueue(); + return vmOp.result; + } + + private static class GetStackTraceOperation extends JavaVMOperation { + private final Thread thread; + StackTraceElement[] result; + + GetStackTraceOperation(Thread thread) { + super(VMOperationInfos.get(GetStackTraceOperation.class, "Get stack trace", SystemEffect.SAFEPOINT)); + this.thread = thread; + } + + @Override + protected void operate() { + if (thread.isAlive()) { + result = getStackTraceAtSafepoint(thread); + } else { + result = Target_java_lang_Thread.EMPTY_STACK_TRACE; + } + } + } } class BuildStackTraceVisitor extends JavaStackFrameVisitor { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_StackWalker.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_StackWalker.java index f96d4ef2b020..850742ea25dd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_StackWalker.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_StackWalker.java @@ -37,6 +37,7 @@ import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.word.Pointer; +import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; import com.oracle.svm.core.FrameAccess; @@ -51,17 +52,21 @@ import com.oracle.svm.core.code.CodeInfoAccess; import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.code.FrameInfoQueryResult; +import com.oracle.svm.core.code.SimpleCodeInfoQueryResult; import com.oracle.svm.core.code.UntetheredCodeInfo; import com.oracle.svm.core.deopt.DeoptimizedFrame; import com.oracle.svm.core.deopt.Deoptimizer; +import com.oracle.svm.core.heap.StoredContinuation; +import com.oracle.svm.core.heap.StoredContinuationAccess; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.stack.JavaStackFrameVisitor; import com.oracle.svm.core.stack.JavaStackWalk; import com.oracle.svm.core.stack.JavaStackWalker; +import com.oracle.svm.core.thread.Continuation; import com.oracle.svm.core.thread.LoomSupport; -import com.oracle.svm.core.thread.Target_java_lang_Continuation; -import com.oracle.svm.core.thread.Target_java_lang_ContinuationScope; import com.oracle.svm.core.thread.Target_java_lang_VirtualThread; +import com.oracle.svm.core.thread.Target_jdk_internal_vm_Continuation; +import com.oracle.svm.core.thread.Target_jdk_internal_vm_ContinuationScope; import com.oracle.svm.core.thread.VirtualThreads; import com.oracle.svm.core.util.VMError; @@ -72,13 +77,13 @@ final class Target_java_lang_StackWalker { * Current continuation that the stack walker is on. */ @Alias @TargetElement(onlyWith = LoomJDK.class)// - Target_java_lang_Continuation continuation; + Target_jdk_internal_vm_Continuation continuation; /** - * Target continuation scope if we're iterating a {@link Target_java_lang_Continuation}. + * Target continuation scope if we're iterating a {@link Target_jdk_internal_vm_Continuation}. */ @Alias @TargetElement(onlyWith = LoomJDK.class)// - Target_java_lang_ContinuationScope contScope; + Target_jdk_internal_vm_ContinuationScope contScope; @Alias Set