diff --git a/src/java.se/share/data/jdwp/jdwp.spec b/src/java.se/share/data/jdwp/jdwp.spec
index a7d489b2813..0c4c5ed5c24 100644
--- a/src/java.se/share/data/jdwp/jdwp.spec
+++ b/src/java.se/share/data/jdwp/jdwp.spec
@@ -2692,10 +2692,9 @@ JDWP "Java(tm) Debug Wire Protocol"
"objectref
is added back as well. The Java virtual machine "
"program counter is restored to the opcode of the invoke instruction."
"
" - "The target VM may not support, or may only provide limited support, for this " - "command when the thread is a virtual thread. It may, for example, only support " - "this command when the virtual thread is suspended at a breakpoint or singlestep " - "event." + "This command may be used to pop frames of a virtual thread when " + "it is suspended at an event. An implementation may support popping " + "the frames of a suspended virtual thread in other cases." "
" "Since JDWP version 1.4. Requires canPopFrames capability - see " "CapabilitiesNew." @@ -2713,8 +2712,8 @@ JDWP "Java(tm) Debug Wire Protocol" (Error NO_MORE_FRAMES) (Error OPAQUE_FRAME "If one or more of the frames to pop is a native " "method or its caller is a native method, or the " - "thread is a virtual thread and the implementation " - "is unable to pop the frames.") + "thread is a suspended virtual thread and the implementation " + "was unable to pop the frames.") (Error NOT_IMPLEMENTED) (Error VM_DEAD) ) diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/ThreadReference.java b/src/jdk.jdi/share/classes/com/sun/jdi/ThreadReference.java index a201e7ed137..3cf0cb96960 100644 --- a/src/jdk.jdi/share/classes/com/sun/jdi/ThreadReference.java +++ b/src/jdk.jdi/share/classes/com/sun/jdi/ThreadReference.java @@ -370,15 +370,14 @@ public interface ThreadReference extends ObjectReference { * Thus the target program may * proceed differently than the user would expect. *
- * The specified thread must be suspended. + * This thread must be suspended. *
* All StackFrame
objects for this thread are
* invalidated.
*
- * The target VM may not support, or may only provide limited support, - * for popping stack frames when the thread is a virtual thread. - * It may, for example, only support this operation when the virtual - * thread is suspended at a breakpoint or singlestep event. + * This method may be used to pop frames of a virtual thread when + * it is suspended at an event. An implementation may support popping + * the frames of a suspended virtual thread in other cases. *
* No events are generated by this method. *
@@ -403,8 +402,8 @@ public interface ThreadReference extends ObjectReference {
* @throws java.lang.IllegalArgumentException if frame
* is not on this thread's call stack.
*
- * @throws OpaqueFrameException if this thread is a virtual thread and the
- * target VM is unable to pop the frames.
+ * @throws OpaqueFrameException if this thread is a suspended virtual thread and the
+ * target VM was unable to pop the frames.
*
* @throws NativeMethodException if one of the frames that would be
* popped is that of a native method or if the frame previous to
diff --git a/src/jdk.jdi/share/classes/com/sun/tools/jdi/StackFrameImpl.java b/src/jdk.jdi/share/classes/com/sun/tools/jdi/StackFrameImpl.java
index 0ad0b08e72f..e242bd7313b 100644
--- a/src/jdk.jdi/share/classes/com/sun/tools/jdi/StackFrameImpl.java
+++ b/src/jdk.jdi/share/classes/com/sun/tools/jdi/StackFrameImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -396,9 +396,27 @@ public class StackFrameImpl extends MirrorImpl
switch (exc.errorCode()) {
case JDWP.Error.OPAQUE_FRAME:
if (thread.isVirtual()) {
- throw new OpaqueFrameException(); // can only happen with virtual threads
+ // We first need to find out if the current frame is native, or if the
+ // previous frame is native, in which case we throw NativeMethodException
+ for (int i = 0; i < 2; i++) {
+ StackFrameImpl sf;
+ try {
+ sf = (StackFrameImpl)thread.frame(i);
+ } catch (IndexOutOfBoundsException e) {
+ // This should never happen, but we need to check for it.
+ break;
+ }
+ sf.validateStackFrame();
+ MethodImpl meth = (MethodImpl)sf.location().method();
+ if (meth.isNative()) {
+ throw new NativeMethodException();
+ }
+ }
+ // No native frames involved. Must have been due to thread
+ // not being mounted.
+ throw new OpaqueFrameException();
} else {
- throw new NativeMethodException(); // can only happen with platform threads
+ throw new NativeMethodException();
}
case JDWP.Error.THREAD_NOT_SUSPENDED:
throw new IncompatibleThreadStateException(
diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt
index 66ef137957b..efe88a86045 100644
--- a/test/hotspot/jtreg/ProblemList-Virtual.txt
+++ b/test/hotspot/jtreg/ProblemList-Virtual.txt
@@ -94,32 +94,6 @@ vmTestbase/nsk/jdb/repeat/repeat001/repeat001.java 8300707 generic-all
vmTestbase/nsk/jdi/ExceptionEvent/catchLocation/location002/TestDescription.java 8278470 generic-all
-####
-## JVMTI PopFrame() is returning OPAQUE_FRAME because vthreads are not supported.
-## Note: vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes001 was converted
-## to support vthreads and expect the OPAQUE_FRAME error. The others were
-## not because they don't add any additional value.
-
-vmTestbase/nsk/jdb/pop_exception/pop_exception001/pop_exception001.java 8285414 generic-all
-
-vmTestbase/nsk/jdi/VirtualMachine/redefineClasses/redefineclasses002/TestDescription.java 8285414 generic-all
-
-vmTestbase/nsk/jdi/Scenarios/invokeMethod/popframes001/TestDescription.java 8285414 generic-all
-vmTestbase/nsk/jdi/BScenarios/hotswap/tc01x002/TestDescription.java 8285414 generic-all
-vmTestbase/nsk/jdi/BScenarios/hotswap/tc02x001/TestDescription.java 8285414 generic-all
-vmTestbase/nsk/jdi/BScenarios/hotswap/tc02x002/TestDescription.java 8285414 generic-all
-vmTestbase/nsk/jdi/BScenarios/hotswap/tc04x001/TestDescription.java 8285414 generic-all
-vmTestbase/nsk/jdi/BScenarios/hotswap/tc04x002/TestDescription.java 8285414 generic-all
-vmTestbase/nsk/jdi/BScenarios/hotswap/tc06x001/TestDescription.java 8285414 generic-all
-vmTestbase/nsk/jdi/BScenarios/hotswap/tc08x001/TestDescription.java 8285414 generic-all
-vmTestbase/nsk/jdi/BScenarios/hotswap/tc10x002/TestDescription.java 8285414 generic-all
-
-vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes002/TestDescription.java 8285414 generic-all
-vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes003/TestDescription.java 8285414 generic-all
-vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes004/TestDescription.java 8285414 generic-all
-
-vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes001/TestDescription.java 8308237 generic-all
-
####
## JVMTI ForceEarlyReturn not supported for vthreads (JVMTI_ERROR_OPAQUE_FRAME)
## Note forceEarlyReturn002 was converted to support vthreads. The rest were not
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes001.java
index 5a0461ad085..51f51da29ef 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes001.java
@@ -93,8 +93,6 @@ import java.io.*;
public class popframes001 extends JDIBase {
- static boolean vthreadMode = "Virtual".equals(System.getProperty("main.wrapper"));
-
public static void main (String argv[]) {
int result = run(argv, System.out);
@@ -221,10 +219,6 @@ public class popframes001 extends JDIBase {
try {
testRun();
- if (vthreadMode) {
- return 0; // just exit. we already got the expected OpaqueFrameException
- }
-
log2("waiting for VMDeathEvent");
getEventSet();
if (eventIterator.nextEvent() instanceof VMDeathEvent)
@@ -356,24 +350,10 @@ public class popframes001 extends JDIBase {
log2("......thread2Ref.popFrames(stackFrame);");
try {
thread2Ref.popFrames(stackFrame);
- if (vthreadMode) {
- log3("ERROR: Expected OpaqueFrameException");
- testExitCode = FAILED;
- }
- } catch ( Exception e ) {
- if (vthreadMode && (e instanceof OpaqueFrameException)) {
- // pass. resume thread and exit
- log2("......got expected OpaqueFrameException");
- log2("......thread2Ref.resume();");
- thread2Ref.resume();
- breakpointForCommunication();
- vm.resume();
- break;
- } else {
- log3("ERROR: " + e.getClass().getSimpleName());
- testExitCode = FAILED;
- throw e;
- }
+ } catch ( IncompatibleThreadStateException e ) {
+ log3("ERROR: IncompatibleThreadStateException");
+ testExitCode = FAILED;
+ break;
}
log2("......thread2Ref.resume();");
diff --git a/test/jdk/ProblemList-Virtual.txt b/test/jdk/ProblemList-Virtual.txt
index d5b9009a430..35fbc3870eb 100644
--- a/test/jdk/ProblemList-Virtual.txt
+++ b/test/jdk/ProblemList-Virtual.txt
@@ -36,9 +36,6 @@ com/sun/jdi/JdbStopThreadTest.java 8285422 generic-all
com/sun/jdi/JdbStopThreadidTest.java 8285422 generic-all
com/sun/jdi/MethodEntryExitEvents.java 8285422 generic-all
com/sun/jdi/MultiBreakpointsTest.java 8285422 generic-all
-com/sun/jdi/PopAndStepTest.java 8285422 generic-all
-com/sun/jdi/PopAsynchronousTest.java 8285422 generic-all
-com/sun/jdi/PopSynchronousTest.java 8285422 generic-all
com/sun/jdi/RedefineCrossStart.java 8285422 generic-all
com/sun/jdi/RedefineG.java 8285422 generic-all
com/sun/jdi/RedefineNestmateAttr/TestNestmateAttr.java 8285422 generic-all
@@ -46,7 +43,6 @@ com/sun/jdi/RedefineTTYLineNumber.java 8285422 generic-all
com/sun/jdi/ReferrersTest.java 8285422 generic-all
com/sun/jdi/SetLocalWhileThreadInNative.java 8285422 generic-all
com/sun/jdi/StepTest.java 8285422 generic-all
-com/sun/jdi/PopAndInvokeTest.java 8305632 generic-all
com/sun/jdi/cds/CDSBreakpointTest.java 8307778 generic-all
com/sun/jdi/cds/CDSDeleteAllBkptsTest.java 8307778 generic-all
com/sun/jdi/cds/CDSFieldWatchpoints.java 8307778 generic-all
diff --git a/test/jdk/com/sun/jdi/PopFramesTest.java b/test/jdk/com/sun/jdi/PopFramesTest.java
new file mode 100644
index 00000000000..1fc45e2a191
--- /dev/null
+++ b/test/jdk/com/sun/jdi/PopFramesTest.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @summary Call popFrames() on threads in various states not covered
+ * well by other tests. Most notably, this test includes
+ * test cases for a suspended but unmounted virtual thread.
+ * It is mostly for testing for OpaqueFrameException and
+ * NativeMethodException.
+ *
+ * @run build TestScaffold VMConnection TargetListener TargetAdapter
+ * @run compile -g PopFramesTest.java
+ * @run driver PopFramesTest SLEEP_NATIVE
+ * @run driver PopFramesTest LOOP_NATIVE
+ * @run driver PopFramesTest SLEEP_PRENATIVE
+ * @run driver PopFramesTest LOOP_PRENATIVE
+ * @run driver PopFramesTest SLEEP_NONATIVE
+ * @run driver PopFramesTest LOOP_NONATIVE
+ */
+import com.sun.jdi.*;
+import com.sun.jdi.event.*;
+import java.util.*;
+
+/*
+ * There are six test modes covered by this test:
+ * SLEEP_NATIVE
+ * LOOP_NATIVE
+ * SLEEP_PRENATIVE
+ * LOOP_PRENATIVE
+ * SLEEP_NONATIVE
+ * LOOP_NONATIVE
+ *
+ * SLEEP: the debuggee blocks in Thread.sleep().
+ * LOOP: the debuggee sits in a tight loop.
+ * NATIVE: there is a native frame within the set of frames to pop.
+ * PRENATIVE: there is a native frame before the set of frames to pop.
+ * NONATIVE: there is no native frame (purposefully) present in the stack.
+ *
+ * In all cases the thread is suspended and errors such as IllegalArgumentException
+ * and InvalidStackFrameException should not happen. The popFrames() calls should
+ * either pass, or produce OpaqueFrameException or NativeMethodException.
+ *
+ * Call stacks for each test mode (and expected result):
+ * - Note in all cases the popMethod() frame is the frame passed to popFrames().
+ * - Note that Thread.sleep() usually results in the native Thread.sleep0() frame
+ * being at the top of the stack. However, for a mounted virtual thread
+ * it does not result in any native frames due to how the VM parks virtual threads.
+ *
+ * SLEEP_NATIVE (NativeMethodException):
+ * Thread.sleep() + methods called by Thread.sleep()
+ * loopOrSleep()
+ * upcallMethod()
+ * doUpcall() <-- native method
+ * popMethod()
+ * main()
+ *
+ * LOOP_NATIVE (NativeMethodException):
+ * loopOrSleep() <-- tight loop
+ * upcallMethod()
+ * doUpcall() <-- native method
+ * popMethod()
+ * main()
+ *
+ * SLEEP_PRENATIVE (NativeMethodException due to Thread.sleep() blocking in a native method):
+ * Thread.sleep() + methods called by Thread.sleep()
+ * loopOrSleep()
+ * popMethod()
+ * upcallMethod()
+ * doUpcall() <-- native method
+ * main()
+ *
+ * LOOP_PRENATIVE (no exception):
+ * loopOrSleep() <-- tight loop
+ * popMethod()
+ * upcallMethod()
+ * doUpcall() <-- native method
+ * main()
+ *
+ * SLEEP_NONATIVE (NativeMethodException for platform thread or OpaqueFrameException
+ * for virtual thread. See explanation in runTests().):
+ * Thread.sleep() + methods called by Thread.sleep()
+ * loopOrSleep()
+ * popMethod()
+ * main()
+ *
+ * LOOP_NONATIVE (no exception):
+ * loopOrSleep() <-- tight loop
+ * popMethod()
+ * main()
+ */
+
+class PopFramesTestTarg {
+ static TestMode mode;
+
+ static {
+ System.loadLibrary("PopFramesTestTarg");
+ }
+
+ /*
+ * This is the method whose frame (and all those after it) will be popped.
+ */
+ public static void popMethod() {
+ System.out.println(" debuggee: in popMethod");
+ if (mode.isCallNative()) {
+ doUpcall();
+ } else {
+ loopOrSleep();
+ }
+ }
+
+ public static void loopOrSleep() {
+ if (mode.isDoLoop()) {
+ while (true);
+ } else {
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public static native void doUpcall(); // native method that will call upcallMethod()
+
+ public static void upcallMethod() {
+ if (mode.isCallPrenative()) {
+ popMethod();
+ } else {
+ loopOrSleep();
+ }
+ }
+
+ public static void main(String[] args) {
+ System.out.println(" debuggee: Howdy!");
+
+ // We expect just one argument, which is the test mode, such as SLEEP_NONATIVE.
+ if (args.length != 1) {
+ throw new RuntimeException("Must pass 1 arguments to PopFramesTestTarg");
+ }
+ System.out.println(" debuggee: args[0]: " + args[0]);
+ mode = Enum.valueOf(TestMode.class, args[0]); // convert test mode string to an enum
+ System.out.println(" debuggee: test mode: " + mode);
+
+ if (mode.isCallNative()) {
+ popMethod(); // call popMethod() directly, and it will call out to native
+ } else if (mode.isCallPrenative()) {
+ doUpcall(); // call native method that will call back into java to call popMethod()
+ } else {
+ popMethod(); // call popMethod() directly
+ }
+
+ System.out.println(" debuggee: Goodbye from PopFramesTest!");
+ }
+}
+
+/*
+ * The different modes the test can be run in. See test description comment above.
+ */
+enum TestMode {
+ SLEEP_NATIVE,
+ LOOP_NATIVE,
+ SLEEP_PRENATIVE,
+ LOOP_PRENATIVE,
+ SLEEP_NONATIVE,
+ LOOP_NONATIVE;
+
+ // Returns true if debuggee should block in an infinite loop. Otherwise it calls Thread.sleep().
+ boolean isDoLoop() {
+ return this == LOOP_NATIVE || this == LOOP_PRENATIVE || this == LOOP_NONATIVE;
+ }
+
+ // Returns true if debuggee should introduce a native frame within the set of frames to pop.
+ boolean isCallNative() {
+ return this == LOOP_NATIVE || this == SLEEP_NATIVE;
+ }
+
+ // Returns true if debuggee should introduce a native frame before the set of frames to pop.
+ // The purpose is to cause the virtual thread to be pinned.
+ boolean isCallPrenative() {
+ return this == LOOP_PRENATIVE || this == SLEEP_PRENATIVE;
+ }
+}
+
+/********** test program **********/
+
+public class PopFramesTest extends TestScaffold {
+ private static TestMode mode;
+
+ PopFramesTest(String args[]) {
+ super(args);
+ }
+
+ public static void main(String[] args) throws Exception {
+ // We should get one argument that indicates the test mode, such as SLEEP_NONATIVE.
+ if (args.length != 1) {
+ throw new RuntimeException("Must pass one argument to PopFramesTestTarg");
+ }
+ mode = Enum.valueOf(TestMode.class, args[0]); // convert test mode string to an enum
+
+ /*
+ * The @run command looks something like:
+ * @run driver PopFramesTest SLEEP_NONATIVE
+ * We need to pass SLEEP_NONATIVE to the debuggee. We also need to insert
+ * -Djava.library.path so the native method can be accessed if called.
+ */
+ String nativePath = "-Djava.library.path=" + System.getProperty("java.library.path");
+ String[] newArgs = new String[2];
+ newArgs[0] = nativePath;
+ newArgs[1] = args[0]; // pass test mode, such as SLEEP_NONATIVE
+
+ new PopFramesTest(newArgs).startTests();
+ }
+
+ StackFrame frameFor(ThreadReference thread, String methodName) throws Exception {
+ Iterator it = thread.frames().iterator();
+
+ while (it.hasNext()) {
+ StackFrame frame = (StackFrame)it.next();
+ if (frame.location().method().name().equals(methodName)) {
+ return frame;
+ }
+ }
+ failure("FAIL: " + methodName + " not on stack");
+ return null;
+ }
+
+ public void printStack(ThreadReference thread, String msg) throws Exception {
+ System.out.println(msg);
+ List