8308819: add JDWP and JDI virtual thread support for ThreadReference.ForceEarlyReturn
Reviewed-by: sspitsyn, alanb
This commit is contained in:
parent
e42a4b659a
commit
5531f6ba1b
@ -2104,10 +2104,10 @@ JDWP "Java(tm) Debug Wire Protocol"
|
||||
"command and resumption of thread execution, the "
|
||||
"state of the stack is undefined. "
|
||||
"<p>"
|
||||
"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 force a return from the current frame "
|
||||
"of a virtual thread when it is suspended at an event. "
|
||||
"An implementation may support forcing a return from the current frame "
|
||||
"of a suspended virtual thread in other cases."
|
||||
"<p>"
|
||||
"No further instructions are executed in the called "
|
||||
"method. Specifically, finally blocks are not executed. Note: "
|
||||
@ -2147,7 +2147,7 @@ JDWP "Java(tm) Debug Wire Protocol"
|
||||
(Error THREAD_NOT_SUSPENDED)
|
||||
(Error OPAQUE_FRAME "Attempted to return early from a frame "
|
||||
"corresponding to a native method, "
|
||||
"the thread is a virtual thread and the target "
|
||||
"the thread is a suspended virtual thread and the target "
|
||||
"VM is unable to force its current frame to return, "
|
||||
"or the implementation is unable to provide this "
|
||||
"functionality on this frame.")
|
||||
|
@ -436,10 +436,10 @@ public interface ThreadReference extends ObjectReference {
|
||||
* this method and resumption of thread execution, the
|
||||
* state of the stack is undefined.
|
||||
* <p>
|
||||
* The target VM may not support, or may only provide limited support,
|
||||
* for forcing a method to return 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 force a return from the current frame
|
||||
* of a virtual thread when it is suspended at an event.
|
||||
* An implementation may support forcing a return from the current frame
|
||||
* of a suspended virtual thread in other cases.
|
||||
* <p>
|
||||
* No further instructions are executed in the called
|
||||
* method. Specifically, finally blocks are not executed. Note:
|
||||
@ -484,7 +484,7 @@ public interface ThreadReference extends ObjectReference {
|
||||
* @throws IncompatibleThreadStateException if this
|
||||
* thread is not suspended.
|
||||
*
|
||||
* @throws OpaqueFrameException if this thread is a virtual thread and the
|
||||
* @throws OpaqueFrameException if this thread is a suspended virtual thread and the
|
||||
* target VM is unable to force the method to return.
|
||||
*
|
||||
* @throws NativeMethodException if the frame to be returned from
|
||||
|
@ -597,11 +597,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
} catch (JDWPException exc) {
|
||||
switch (exc.errorCode()) {
|
||||
case JDWP.Error.OPAQUE_FRAME:
|
||||
if (meth.isNative()) {
|
||||
throw new NativeMethodException();
|
||||
} else {
|
||||
assert isVirtual(); // can only happen with virtual threads
|
||||
if (isVirtual() && !meth.isNative()) {
|
||||
throw new OpaqueFrameException();
|
||||
} else {
|
||||
throw new NativeMethodException();
|
||||
}
|
||||
case JDWP.Error.THREAD_NOT_SUSPENDED:
|
||||
throw new IncompatibleThreadStateException(
|
||||
|
@ -105,16 +105,6 @@ vmTestbase/nsk/jdb/repeat/repeat001/repeat001.java 8300707 generic-all
|
||||
|
||||
vmTestbase/nsk/jdi/ExceptionEvent/catchLocation/location002/TestDescription.java 8278470 generic-all
|
||||
|
||||
####
|
||||
## JVMTI ForceEarlyReturn not supported for vthreads (JVMTI_ERROR_OPAQUE_FRAME)
|
||||
## Note forceEarlyReturn002 was converted to support vthreads. The rest were not
|
||||
## since there is no added value (JVMTI_ERROR_OPAQUE_FRAME is expected).
|
||||
|
||||
vmTestbase/nsk/jdi/ThreadReference/forceEarlyReturn/forceEarlyReturn002/forceEarlyReturn002.java
|
||||
vmTestbase/nsk/jdi/ThreadReference/forceEarlyReturn/forceEarlyReturn014/forceEarlyReturn014.java 8285415 generic-all
|
||||
vmTestbase/nsk/jdi/stress/serial/forceEarlyReturn001/TestDescription.java 8285415 generic-all
|
||||
vmTestbase/nsk/jdi/stress/serial/forceEarlyReturn002/TestDescription.java 8285415 generic-all
|
||||
|
||||
###
|
||||
# This test always times out on windows. This is due to the test forcing OOME in the
|
||||
# debuggee, which has the side affect of making the Read-Poller thread exit. Because
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2007, 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
|
||||
@ -147,30 +147,17 @@ public class forceEarlyReturn002 extends ForceEarlyReturnDebugger {
|
||||
|
||||
// get value for early return
|
||||
ObjectReference returnValue = (ObjectReference) referenceType.getValue(referenceType.fieldByName("expectedValue"));
|
||||
boolean vthreadMode = "Virtual".equals(System.getProperty("main.wrapper"));
|
||||
|
||||
try {
|
||||
// don't expect any exception, except for vthreads expect OpaqueFrameException
|
||||
// don't expect any exception
|
||||
threadReference.forceEarlyReturn(returnValue);
|
||||
if (vthreadMode) {
|
||||
setSuccess(false);
|
||||
log.complain("Expected OpaqueFrameException");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (vthreadMode && (e instanceof OpaqueFrameException)) {
|
||||
// pass
|
||||
} else {
|
||||
setSuccess(false);
|
||||
log.complain("Unexpected exception: " + e);
|
||||
e.printStackTrace(log.getOutStream());
|
||||
}
|
||||
setSuccess(false);
|
||||
log.complain("Unexpected exception: " + e);
|
||||
e.printStackTrace(log.getOutStream());
|
||||
}
|
||||
|
||||
if (vthreadMode) {
|
||||
// MethodExit event won't be as expected if using vthreads, so just resume
|
||||
threadReference.resume();
|
||||
} else {
|
||||
testMethodExitEvent(threadReference, ClassUsingTestClass.breakpointMethodName);
|
||||
}
|
||||
testMethodExitEvent(threadReference, ClassUsingTestClass.breakpointMethodName);
|
||||
|
||||
if (!isDebuggeeReady())
|
||||
return;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2007, 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
|
||||
@ -83,9 +83,7 @@ public class forceEarlyReturn002a extends AbstractJDIDebuggee {
|
||||
classUsingTestClass.createExpectedValue();
|
||||
Object value = classUsingTestClass.testClassMethod();
|
||||
|
||||
boolean vthreadMode = "Virtual".equals(System.getProperty("main.wrapper"));
|
||||
// expectedValue should be set as expected unless in vthread mode
|
||||
if (vthreadMode == (ClassUsingTestClass.expectedValue == value)) {
|
||||
if (ClassUsingTestClass.expectedValue != value) {
|
||||
setSuccess(false);
|
||||
log.complain("Unexpected result of testClassMethod: " + value);
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ com/sun/jdi/EATests.java#id0 8264699 generic-
|
||||
#
|
||||
|
||||
com/sun/jdi/DeferredStepTest.java 8285422 generic-all
|
||||
com/sun/jdi/EarlyReturnTest.java 8285422 generic-all
|
||||
com/sun/jdi/ExceptionEvents.java 8285422 generic-all
|
||||
com/sun/jdi/JdbMethodExitTest.java 8285422 generic-all
|
||||
com/sun/jdi/JdbStepTest.java 8285422 generic-all
|
||||
|
263
test/jdk/com/sun/jdi/ForceEarlyReturnTest.java
Normal file
263
test/jdk/com/sun/jdi/ForceEarlyReturnTest.java
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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 forceEarlyReturn() on threads in various states not covered
|
||||
* well by other tests. Most notably, this test includes a
|
||||
* test case for a suspended but unmounted virtual thread.
|
||||
*
|
||||
* @run build TestScaffold VMConnection TargetListener TargetAdapter
|
||||
* @run compile -g ForceEarlyReturnTest.java
|
||||
* @run driver ForceEarlyReturnTest NATIVE
|
||||
* @run driver ForceEarlyReturnTest LOOP
|
||||
* @run driver ForceEarlyReturnTest SLEEP
|
||||
*/
|
||||
import com.sun.jdi.*;
|
||||
import com.sun.jdi.event.*;
|
||||
import java.util.*;
|
||||
|
||||
/*
|
||||
* There are three test modes covered by this test:
|
||||
* NATIVE: the debuggee sits in a native method.
|
||||
* SLEEP: the debuggee blocks in Thread.sleep().
|
||||
* LOOP: the debuggee sits in a tight loop.
|
||||
*
|
||||
* In all cases the thread is suspended and errors such as IllegalArgumentException
|
||||
* and InvalidStackFrameException should not happen. The forceEarlyReturn() calls should
|
||||
* either pass, or produce OpaqueFrameException or NativeMethodException.
|
||||
*
|
||||
* Call stacks for each test mode (and expected result):
|
||||
*
|
||||
* NATIVE (NativeMethodException):
|
||||
* nativeMethod() <-- native method, which sleeps
|
||||
* loopOrSleep()
|
||||
* main()
|
||||
*
|
||||
* LOOP (no exception):
|
||||
* loopOrSleep() <-- tight loop
|
||||
* main()
|
||||
*
|
||||
* SLEEP (NativeMethodException for platform thread or OpaqueFrameException
|
||||
* for virtual thread. See explanation in runTests().):
|
||||
* Thread.sleep() + methods called by Thread.sleep()
|
||||
* loopOrSleep()
|
||||
* main()
|
||||
*/
|
||||
|
||||
class ForceEarlyReturnTestTarg {
|
||||
static TestMode mode;
|
||||
|
||||
static {
|
||||
System.loadLibrary("ForceEarlyReturnTestTarg");
|
||||
}
|
||||
|
||||
public static void loopOrSleep() {
|
||||
switch (mode) {
|
||||
case TestMode.LOOP:
|
||||
while (true);
|
||||
case TestMode.SLEEP:
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
break;
|
||||
case TestMode.NATIVE:
|
||||
nativeMethod();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static native void nativeMethod(); // native method that does a very long sleep
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(" debuggee: Howdy!");
|
||||
|
||||
// We expect just one argument, which is the test mode, such as SLEEP.
|
||||
if (args.length != 1) {
|
||||
throw new RuntimeException("Must pass 1 arguments to ForceEarlyReturnTestTarg");
|
||||
}
|
||||
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);
|
||||
|
||||
loopOrSleep();
|
||||
|
||||
System.out.println(" debuggee: Goodbye from ForceEarlyReturnTest!");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The different modes the test can be run in. See test description comment above.
|
||||
*/
|
||||
enum TestMode {
|
||||
NATIVE,
|
||||
SLEEP,
|
||||
LOOP;
|
||||
}
|
||||
|
||||
/********** test program **********/
|
||||
|
||||
public class ForceEarlyReturnTest extends TestScaffold {
|
||||
private static TestMode mode;
|
||||
|
||||
ForceEarlyReturnTest(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.
|
||||
if (args.length != 1) {
|
||||
throw new RuntimeException("Must pass one argument to ForceEarlyReturnTestTarg");
|
||||
}
|
||||
mode = Enum.valueOf(TestMode.class, args[0]); // convert test mode string to an enum
|
||||
|
||||
/*
|
||||
* The @run command looks something like:
|
||||
* @run driver ForceEarlyReturnTest SLEEP
|
||||
* We need to pass SLEEP 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 ForceEarlyReturnTest(newArgs).startTests();
|
||||
}
|
||||
|
||||
public void printStack(ThreadReference thread, String msg) throws Exception {
|
||||
System.out.println(msg);
|
||||
List<StackFrame> stack_frames = thread.frames();
|
||||
int i = 0;
|
||||
String sourceName;
|
||||
for (StackFrame f : stack_frames) {
|
||||
try {
|
||||
sourceName = f.location().sourceName();
|
||||
} catch (AbsentInformationException aie) {
|
||||
sourceName = "Unknown source";
|
||||
}
|
||||
System.out.println("frame[" + i++ +"]: " + f.location().method() +
|
||||
" (bci:"+ f.location().codeIndex() + ")" +
|
||||
" (" + sourceName + ":"+ f.location().lineNumber() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/********** test core **********/
|
||||
|
||||
protected void runTests() throws Exception {
|
||||
BreakpointEvent bpe = startTo("ForceEarlyReturnTestTarg", "loopOrSleep", "()V");
|
||||
ThreadReference mainThread = bpe.thread();
|
||||
boolean is_vthread_mode = "Virtual".equals(System.getProperty("main.wrapper"));
|
||||
|
||||
// Resume main thread until it is in Thread.sleep() or the infinite loop.
|
||||
mainThread.resume();
|
||||
try {
|
||||
Thread.sleep(1000); // give thread chance to get into Thread.sleep() or loop
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
mainThread.suspend(); // Suspend thread while in Thread.sleep() or loop
|
||||
printStack(mainThread, "Debuggee stack before forceEarlyReturn():");
|
||||
|
||||
/*
|
||||
* Figure out which exception forceEarlyReturn() should throw.
|
||||
*/
|
||||
Class expected_exception;
|
||||
switch(mode) {
|
||||
case NATIVE:
|
||||
/*
|
||||
* There is a native frame on the top of the stack, so we expect NativeMethodException.
|
||||
*/
|
||||
expected_exception = NativeMethodException.class;
|
||||
break;
|
||||
case LOOP:
|
||||
/*
|
||||
* There is a java frame on the top of the stack, so we expect no exception.
|
||||
*/
|
||||
expected_exception = null;
|
||||
break;
|
||||
case SLEEP:
|
||||
/*
|
||||
* For platform threads, Thread.sleep() results in the Thread.sleep0() native
|
||||
* frame on the stack, so the end result is NativeMethodException. For virtual
|
||||
* threads it is not quite so simple. If the thead is pinned (such as when
|
||||
* there is already a native method on the stack), you end up in
|
||||
* VirtualThread.parkOnCarrierThread(), which calls Unsafe.park(), which is a
|
||||
* native method, so again this results in NativeMethodException. However, for
|
||||
* a virtual thread that is not pinned (which is true for this test case), you
|
||||
* end up with no native methods on the stack due to how Continuation.yield()
|
||||
* works. So you have an unmounted virtual thread with no native frames, which
|
||||
* results in OpaqueFrameException being thrown.
|
||||
*/
|
||||
if (is_vthread_mode) {
|
||||
expected_exception = OpaqueFrameException.class;
|
||||
} else {
|
||||
expected_exception = NativeMethodException.class;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Bad test mode: " + mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call ThreadReference.forceEarlyReturn() and check for errors.
|
||||
*/
|
||||
try {
|
||||
if (is_vthread_mode && mode == TestMode.SLEEP) {
|
||||
// For this test case with virtual threads, the topmost frame is for
|
||||
// Continuation.yield0(), which returns a boolean.
|
||||
BooleanValue theValue = vm().mirrorOf(true);
|
||||
mainThread.forceEarlyReturn(theValue);
|
||||
} else {
|
||||
// For all other cases, the topmost frame will be one that returns void.
|
||||
VoidValue theValue = vm().mirrorOfVoid();
|
||||
mainThread.forceEarlyReturn(theValue);
|
||||
}
|
||||
if (expected_exception != null) {
|
||||
failure("failure: forceEarlyReturn() did not get expected exception: " + expected_exception);
|
||||
} else {
|
||||
System.out.println("success: no exception for forceEarlyReturn()");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
if (expected_exception == ex.getClass()) {
|
||||
System.out.println("success: forceEarlyReturn() got expected exception: " + ex);
|
||||
} else {
|
||||
failure("failure: forceEarlyReturn() got unexpected exception: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Most tests do a listenUntilVMDisconnect() here, but there is no real need for it
|
||||
* with this test, and doing so would require finding a way to get the debuggee
|
||||
* to exit the endless loop it might be in. When we return, TestScaffold will
|
||||
* call TestScaffold.shutdown(), causing the debuggee process to be terminated quickly.
|
||||
*/
|
||||
|
||||
if (testFailed) {
|
||||
throw new Exception("ForceEarlyReturnTest failed");
|
||||
}
|
||||
System.out.println("Passed:");
|
||||
}
|
||||
}
|
37
test/jdk/com/sun/jdi/libForceEarlyReturnTestTarg.c
Normal file
37
test/jdk/com/sun/jdi/libForceEarlyReturnTestTarg.c
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
/*
|
||||
* Class: NativeMethod
|
||||
* Method: nativeMethod
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_ForceEarlyReturnTestTarg_nativeMethod(JNIEnv *env, jobject obj) {
|
||||
/* Just spin. We don't need to ever return from here. */
|
||||
while(JNI_TRUE);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user