On Sat, 11 Oct 2025 05:09:31 GMT, Chris Plummer <[email protected]> wrote:
> > @plummercj Did you run `jhsdb jstack --mixed` ? StubRoutine would appear in
> > mixed mode only I think (in Linux, at least).
>
> I did not, and unfortunately --mixed is not support on OSX.
>
Ok, that’s why I wasn’t seeing `<StubRoutines (continuation stubs)>` either. I
removed `—mixed` because it wasn’t working for me, only the top native frame
was shown in the stacktrace (on linux-x64). I could still reproduce the assert
without `—mixed` so I continued testing that way assuming the output for the
`enterSpecial` frame would be the same in both modes.
Now, testing on linux-aarch64, `—mixed` mode works and I can see `<StubRoutines
(continuation stubs)>` for the `enterSpecial` frame. Looking at the code, I see
the difference with mixed mode is the way we walk the stack. Even for Java
frames the sender is always obtained through this method [1], relying basically
on the FP. Then once we get to the frame with pc equal to the return barrier,
here [2] we will just branch to the else case and print it as a continuation
stub instead of the `enterSpecial` frame.
I was also curious how we can walk compiled frames this way, since the FP will
not contain in general a valid link unless `-XX:+PreserveFramePointer` is set.
So I added `-Xcomp` to `LingeredApp.startApp` and I see that we indeed fail to
walk the stack:
----------------- 2943661 -----------------
"ForkJoinPool-1-worker-2" #30 daemon prio=5 tid=0x0000ffff08007f30 nid=2943661
runnable [0x0000ffff695e1000]
java.lang.Thread.State: RUNNABLE
JavaThread state: _thread_in_native
0x0000ffff9bb40b24 __clock_nanosleep + 0x84
0x0000ffff9bb45c0c __GI___nanosleep + 0x1c
0x0000ffff9bb45acc __sleep + 0x4c
0x0000ffff836fb338 <nep_invoker_blob>
0x0000ffff7c802ef8 *
java.lang.invoke.LambdaForm$MH+0x0000000601047800.invoke(java.lang.Object,
long, int) bci:10 (Compiled frame)
*
java.lang.invoke.LambdaForm$MH+0x000000060104d400.invokeExact_MT(java.lang.Object,
long, int, java.lang.Object) bci:21 (Compiled frame)
0x910023e12a0003e2 ????????
----------------- 2943659 -----------------
When setting` -XX:+PreserveFramePointer` we can walk them again (also tweaked
the code to print the correct `enterSpecial` frame):
----------------- 2947348 -----------------
"ForkJoinPool-1-worker-2" #30 daemon prio=5 tid=0x0000ffff1c008040 nid=2947348
runnable [0x0000ffff7c8dd000]
java.lang.Thread.State: RUNNABLE
JavaThread state: _thread_in_native
0x0000ffffaee18b24 __clock_nanosleep + 0x84
0x0000ffffaee1dc0c __GI___nanosleep + 0x1c
0x0000ffffaee1dacc __sleep + 0x4c
0x0000ffff976faa38 <nep_invoker_blob>
0x0000ffff90806f3c *
java.lang.invoke.LambdaForm$MH+0x000007e001047800.invoke(java.lang.Object,
long, int) bci:10 (Compiled frame)
0x0000ffff90bad354 *
java.lang.invoke.LambdaForm$MH+0x000007e00104d400.invokeExact_MT(java.lang.Object,
long, int, java.lang.Object) bci:21 (Compiled frame)
0x0000ffff97597d60 *
jdk.internal.foreign.abi.DowncallStub+0x000007e001048800.invoke(java.lang.foreign.SegmentAllocator,
java.lang.foreign.MemorySegment, int) bci:44 (Interpreted frame)
0x0000ffff908b7a70 *
java.lang.invoke.LambdaForm$DMH+0x000007e001048c00.invokeStaticInit(java.lang.Object,
java.lang.Object, java.lang.Object, int) bci:14 (Compiled frame)
0x0000ffff90b9b710 *
java.lang.invoke.LambdaForm$MH+0x000007e00104c800.invoke(java.lang.Object, int)
bci:44 (Compiled frame)
0x0000ffff90b9041c *
java.lang.invoke.LambdaForm$MH+0x000007e00104c400.invoke_MT(java.lang.Object,
int, java.lang.Object) bci:18 (Compiled frame)
0x0000ffff97597f30 * LingeredAppWithVirtualThread.run() bci:15 line:65
(Interpreted frame)
0x0000ffff975963b8 *
jdk.internal.vm.Continuation.enterSpecial(jdk.internal.vm.Continuation,
boolean, boolean) bci:0 (Compiled frame)
0x0000ffff97fa5fbc * jdk.internal.vm.Continuation.run() bci:152 line:251
(Compiled frame)
0x0000ffff90b85888 * java.lang.VirtualThread.runContinuation() bci:100
line:293 (Compiled frame)
0x0000ffff97fa3eb0 *
java.lang.VirtualThread$$Lambda+0x000007e001034c88.run() bci:4 (Compiled frame)
0x0000ffff90b8445c *
java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec() bci:4 line:1596
(Compiled frame)
0x0000ffff90b83298 * java.util.concurrent.ForkJoinTask.doExec() bci:10
line:511 (Compiled frame)
0x0000ffff90b81e2c *
java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(java.util.concurrent.ForkJoinTask,
int) bci:5 line:1450 (Compiled frame)
*
java.util.concurrent.ForkJoinPool.runWorker(java.util.concurrent.ForkJoinPool$WorkQueue)
bci:364 line:2019 (Compiled frame)
0x0000ffff97fa03a8 * java.util.concurrent.ForkJoinWorkerThread.run()
bci:31 line:187 (Compiled frame)
0x0000ffff9759349c <StubRoutines (initial stubs)>
0x0000ffffad95020c JavaCalls::call_helper(JavaValue*, methodHandle const&,
JavaCallArguments*, JavaThread*) + 0x45c
0x0000ffffad950880 JavaCalls::call_virtual(JavaValue*, Klass*, Symbol*,
Symbol*, JavaCallArguments*, JavaThread*) + 0x278
0x0000ffffad950e14 JavaCalls::call_virtual(JavaValue*, Handle, Klass*,
Symbol*, Symbol*, JavaThread*) + 0x8c
0x0000ffffadaee66c thread_entry(JavaThread*, JavaThread*) + 0xc4
0x0000ffffad98de68 JavaThread::thread_main_inner() + 0x108
0x0000ffffae301efc Thread::call_run() + 0xac
0x0000ffffadfe105c thread_native_entry(Thread*) + 0x12c
0x0000ffffaede3b50 start_thread + 0x300
I would have expected that `mixed` mode walked the stack similar to how we do
it in the VM with `NativeStackPrinter` using `frame::next_frame()`, i.e we
change how we get the sender based on the frame.
Anyways, I was just curious about the difference in output with `mixed` mode.
[1]
https://github.com/openjdk/jdk/blob/d6537c6d3ee6d7a59d609b277f0538da0afb0fbf/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java#L56
[2]
https://github.com/openjdk/jdk/blob/d6537c6d3ee6d7a59d609b277f0538da0afb0fbf/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java#L148
-------------
PR Comment: https://git.openjdk.org/jdk/pull/27728#issuecomment-3403057882