8306471: Add virtual threads support to JDWP ThreadReference.Stop and JDI ThreadReference.stop()
Reviewed-by: sspitsyn, alanb
This commit is contained in:
parent
4441a2306f
commit
d809823fe4
@ -2004,10 +2004,11 @@ JDWP "Java(tm) Debug Wire Protocol"
|
|||||||
(Command Stop=10
|
(Command Stop=10
|
||||||
"Stops the thread with an asynchronous exception. "
|
"Stops the thread with an asynchronous exception. "
|
||||||
"<p>"
|
"<p>"
|
||||||
"The target VM may not support, or may only provide limited support, for "
|
"This command may be used to send an asynchronous "
|
||||||
"this command when the thread is a virtual thread. It may, for example, "
|
"exception to a virtual thread when it is suspended at an event. "
|
||||||
"only support this command when the virtual thread is suspended at a "
|
"An implementation may support sending an asynchronous exception "
|
||||||
"breakpoint or singlestep event."
|
"to a suspended virtual thread in other cases."
|
||||||
|
|
||||||
(Out
|
(Out
|
||||||
(threadObject thread "The thread object ID. ")
|
(threadObject thread "The thread object ID. ")
|
||||||
(object throwable "Asynchronous exception. This object must "
|
(object throwable "Asynchronous exception. This object must "
|
||||||
@ -2018,8 +2019,10 @@ JDWP "Java(tm) Debug Wire Protocol"
|
|||||||
(ErrorSet
|
(ErrorSet
|
||||||
(Error INVALID_THREAD "The thread is null, not a valid thread, or the thread "
|
(Error INVALID_THREAD "The thread is null, not a valid thread, or the thread "
|
||||||
"is not alive.")
|
"is not alive.")
|
||||||
(Error NOT_IMPLEMENTED "The thread is a virtual thread and the target "
|
(Error THREAD_NOT_SUSPENDED "The thread is a virtual thread and was not suspended.")
|
||||||
"VM does not support the command on virtual threads.")
|
(Error OPAQUE_FRAME "The thread is a suspended virtual thread and the implementation "
|
||||||
|
"was unable to throw an asynchronous exception "
|
||||||
|
"from the thread's current frame.")
|
||||||
(Error INVALID_OBJECT "If thread is not a known ID or the asynchronous "
|
(Error INVALID_OBJECT "If thread is not a known ID or the asynchronous "
|
||||||
"exception has been garbage collected.")
|
"exception has been garbage collected.")
|
||||||
(Error VM_DEAD)
|
(Error VM_DEAD)
|
||||||
@ -3166,7 +3169,7 @@ JDWP "Java(tm) Debug Wire Protocol"
|
|||||||
"call stack.")
|
"call stack.")
|
||||||
(Constant OPAQUE_FRAME =32 "Information about the frame is not available "
|
(Constant OPAQUE_FRAME =32 "Information about the frame is not available "
|
||||||
"(e.g. native frame) or the target VM is unable "
|
"(e.g. native frame) or the target VM is unable "
|
||||||
"to perform an operation on the frame.")
|
"to perform an operation on the thread's current frame.")
|
||||||
(Constant NOT_CURRENT_FRAME =33 "Operation can only be performed on current frame.")
|
(Constant NOT_CURRENT_FRAME =33 "Operation can only be performed on current frame.")
|
||||||
(Constant TYPE_MISMATCH =34 "The variable is not an appropriate type for "
|
(Constant TYPE_MISMATCH =34 "The variable is not an appropriate type for "
|
||||||
"the function used.")
|
"the function used.")
|
||||||
|
@ -116,18 +116,20 @@ public interface ThreadReference extends ObjectReference {
|
|||||||
* A debugger thread in the target VM will stop this thread
|
* A debugger thread in the target VM will stop this thread
|
||||||
* with the given {@link java.lang.Throwable} object.
|
* with the given {@link java.lang.Throwable} object.
|
||||||
* <p>
|
* <p>
|
||||||
* The target VM may not support, or may only provide limited support,
|
* This method may be used to send an asynchronous
|
||||||
* for stopping a virtual thread with an asynchronous exception. It may,
|
* exception to a virtual thread when it is suspended at an event.
|
||||||
* for example, only support this operation when the virtual thread is
|
* An implementation may support sending an asynchronous exception
|
||||||
* suspended at a breakpoint or singlestep event.
|
* to a suspended virtual thread in other cases.
|
||||||
|
|
||||||
*
|
*
|
||||||
* @param throwable the asynchronous exception to throw
|
* @param throwable the asynchronous exception to throw
|
||||||
* @throws InvalidTypeException if <code>throwable</code> is not
|
* @throws InvalidTypeException if <code>throwable</code> is not
|
||||||
* an instance of java.lang.Throwable in the target VM
|
* an instance of java.lang.Throwable in the target VM
|
||||||
* @throws IllegalThreadStateException if the thread has terminated
|
* @throws IllegalThreadStateException if the thread has terminated,
|
||||||
* @throws UnsupportedOperationException if the thread is a virtual
|
* or if the thread is a virtual thread and was not suspended
|
||||||
* thread and the target VM does not support this operation on
|
* @throws OpaqueFrameException if the thread is a suspended
|
||||||
* virtual threads
|
* virtual thread and the implementation was unable to throw an
|
||||||
|
* asynchronous exception from the thread's current frame
|
||||||
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only
|
* @throws VMCannotBeModifiedException if the VirtualMachine is read-only
|
||||||
* @see VirtualMachine#canBeModified()
|
* @see VirtualMachine#canBeModified()
|
||||||
*/
|
*/
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -719,9 +719,13 @@ class Commands {
|
|||||||
} catch (InvalidTypeException e) {
|
} catch (InvalidTypeException e) {
|
||||||
MessageOutput.println("Invalid exception object");
|
MessageOutput.println("Invalid exception object");
|
||||||
} catch (IllegalThreadStateException its) {
|
} catch (IllegalThreadStateException its) {
|
||||||
MessageOutput.println("Illegal thread state");
|
if (!thread.isSuspended() && thread.isVirtual()) {
|
||||||
} catch (UnsupportedOperationException uoe) {
|
MessageOutput.println("Illegal thread state (virtual thread not suspended)");
|
||||||
MessageOutput.println("Operation is not supported on the target VM");
|
} else {
|
||||||
|
MessageOutput.println("Illegal thread state");
|
||||||
|
}
|
||||||
|
} catch (OpaqueFrameException ope) {
|
||||||
|
MessageOutput.println("Operation is not supported on the current frame");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MessageOutput.println("Expression must evaluate to an object");
|
MessageOutput.println("Expression must evaluate to an object");
|
||||||
|
@ -142,6 +142,7 @@ public class TTYResources extends java.util.ListResourceBundle {
|
|||||||
{"Illegal Argument Exception", "Illegal Argument Exception"},
|
{"Illegal Argument Exception", "Illegal Argument Exception"},
|
||||||
{"Illegal connector argument", "Illegal connector argument: {0}"},
|
{"Illegal connector argument", "Illegal connector argument: {0}"},
|
||||||
{"Illegal thread state", "Illegal thread state"},
|
{"Illegal thread state", "Illegal thread state"},
|
||||||
|
{"Illegal thread state (virtual thread not suspended)", "Illegal thread state (virtual thread not suspended)"},
|
||||||
{"implementor:", "implementor: {0}"},
|
{"implementor:", "implementor: {0}"},
|
||||||
{"implements:", "implements: {0}"},
|
{"implements:", "implements: {0}"},
|
||||||
{"Initializing progname", "Initializing {0} ..."},
|
{"Initializing progname", "Initializing {0} ..."},
|
||||||
@ -244,7 +245,7 @@ public class TTYResources extends java.util.ListResourceBundle {
|
|||||||
{"Not waiting for a monitor", " Not waiting for a monitor"},
|
{"Not waiting for a monitor", " Not waiting for a monitor"},
|
||||||
{"Nothing suspended.", "Nothing suspended."},
|
{"Nothing suspended.", "Nothing suspended."},
|
||||||
{"object description and id", "({0}){1}"},
|
{"object description and id", "({0}){1}"},
|
||||||
{"Operation is not supported on the target VM", "Operation is not supported on the target VM"},
|
{"Operation is not supported on the current frame", "Operation is not supported on the current frame"},
|
||||||
{"operation not yet supported", "operation not yet supported"},
|
{"operation not yet supported", "operation not yet supported"},
|
||||||
{"Owned by:", " Owned by: {0}, entry count: {1,number,integer}"},
|
{"Owned by:", " Owned by: {0}, entry count: {1,number,integer}"},
|
||||||
{"Owned monitor:", " Owned monitor: {0}"},
|
{"Owned monitor:", " Owned monitor: {0}"},
|
||||||
|
@ -273,7 +273,18 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
JDWP.ThreadReference.Stop.process(vm, this,
|
JDWP.ThreadReference.Stop.process(vm, this,
|
||||||
(ObjectReferenceImpl)throwable);
|
(ObjectReferenceImpl)throwable);
|
||||||
} catch (JDWPException exc) {
|
} catch (JDWPException exc) {
|
||||||
throw exc.toJDIException();
|
switch (exc.errorCode()) {
|
||||||
|
case JDWP.Error.OPAQUE_FRAME:
|
||||||
|
assert isVirtual(); // can only happen with virtual threads
|
||||||
|
throw new OpaqueFrameException();
|
||||||
|
case JDWP.Error.THREAD_NOT_SUSPENDED:
|
||||||
|
assert isVirtual(); // can only happen with virtual threads
|
||||||
|
throw new IllegalThreadStateException("virtual thread not suspended");
|
||||||
|
case JDWP.Error.INVALID_THREAD:
|
||||||
|
throw new IllegalThreadStateException("thread has terminated");
|
||||||
|
default:
|
||||||
|
throw exc.toJDIException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -35,16 +35,21 @@ import nsk.share.jpda.*;
|
|||||||
import nsk.share.jdi.*;
|
import nsk.share.jdi.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The test checks that the JDI method:<br>
|
* The test checks that the JDI method:<br><code>com.sun.jdi.ThreadReference.stop()</code><br>
|
||||||
* <code>com.sun.jdi.ThreadReference.stop()</code><br>
|
* behaves properly in various situations. It consists of 5 subtests.
|
||||||
* properly throws <i>InvalidTypeException</i> - if specified
|
|
||||||
* throwable is not an instance of java.lang.Throwable in the
|
|
||||||
* target VM.<p>
|
|
||||||
*
|
*
|
||||||
* Debugger part of the test tries to stop debuggee thread
|
* TEST #1: Tests that stop() properly throws <i>InvalidTypeException</i> if
|
||||||
* through the JDI method using as a parameter an object
|
* specified throwable is not an instance of java.lang.Throwable in the target VM.<p>
|
||||||
* reference of the main debuggee class <i>stop002t</i> itself
|
*
|
||||||
* which is not <code>Throwable</code>.
|
* TEST #2: Verify that stop() works when suspended at a breakpoint.
|
||||||
|
*
|
||||||
|
* TEST #3: Verify that stop() works when not suspended in a loop. For virtual threads
|
||||||
|
* we expect an IncompatibleThreadStateException.
|
||||||
|
*
|
||||||
|
* TEST #4: Verify that stop() works when suspended in a loop.
|
||||||
|
*
|
||||||
|
* TEST #5: Verify that stop() works when suspended in Thread.sleep(). For virtual
|
||||||
|
* threads we expect an OpaqueFrameException.
|
||||||
*/
|
*/
|
||||||
public class stop002 {
|
public class stop002 {
|
||||||
static final String DEBUGGEE_CLASS =
|
static final String DEBUGGEE_CLASS =
|
||||||
@ -54,12 +59,15 @@ public class stop002 {
|
|||||||
static final String DEBUGGEE_THRNAME = "stop002tThr";
|
static final String DEBUGGEE_THRNAME = "stop002tThr";
|
||||||
|
|
||||||
// debuggee local var used to find needed non-throwable object
|
// debuggee local var used to find needed non-throwable object
|
||||||
static final String DEBUGGEE_LOCALVAR = "stop002tNonThrowable";
|
static final String DEBUGGEE_NON_THROWABLE_VAR= "stop002tNonThrowable";
|
||||||
// debuggee field used to indicate that testing is over
|
// debuggee local var used to find needed throwable object
|
||||||
static final String DEBUGGEE_FIELD = "stopLooping";
|
static final String DEBUGGEE_THROWABLE_VAR = "stop002tThrowable";
|
||||||
|
// debuggee fields used to indicate to exit infinite loops
|
||||||
|
static final String DEBUGGEE_STOP_LOOP1_FIELD = "stopLooping1";
|
||||||
|
static final String DEBUGGEE_STOP_LOOP2_FIELD = "stopLooping2";
|
||||||
|
|
||||||
// debuggee source line where it should be stopped
|
// debuggee source line where it should be stopped
|
||||||
static final int DEBUGGEE_STOPATLINE = 69;
|
static final int DEBUGGEE_STOPATLINE = 88;
|
||||||
|
|
||||||
static final int DELAY = 500; // in milliseconds
|
static final int DELAY = 500; // in milliseconds
|
||||||
|
|
||||||
@ -67,12 +75,15 @@ public class stop002 {
|
|||||||
static final String COMMAND_GO = "go";
|
static final String COMMAND_GO = "go";
|
||||||
static final String COMMAND_QUIT = "quit";
|
static final String COMMAND_QUIT = "quit";
|
||||||
|
|
||||||
|
static final boolean vthreadMode = "Virtual".equals(System.getProperty("main.wrapper"));
|
||||||
|
|
||||||
private ArgumentHandler argHandler;
|
private ArgumentHandler argHandler;
|
||||||
private Log log;
|
private Log log;
|
||||||
private IOPipe pipe;
|
private IOPipe pipe;
|
||||||
private Debugee debuggee;
|
private Debugee debuggee;
|
||||||
private VirtualMachine vm;
|
private VirtualMachine vm;
|
||||||
private BreakpointRequest BPreq;
|
private BreakpointRequest BPreq;
|
||||||
|
private ReferenceType mainClass;
|
||||||
private volatile int tot_res = Consts.TEST_PASSED;
|
private volatile int tot_res = Consts.TEST_PASSED;
|
||||||
private volatile boolean gotEvent = false;
|
private volatile boolean gotEvent = false;
|
||||||
|
|
||||||
@ -101,68 +112,180 @@ public class stop002 {
|
|||||||
return quitDebuggee();
|
return quitDebuggee();
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadReference thrRef = null;
|
ThreadReference thrRef = debuggee.threadByName(DEBUGGEE_THRNAME);
|
||||||
if ((thrRef =
|
if (thrRef == null) {
|
||||||
debuggee.threadByName(DEBUGGEE_THRNAME)) == null) {
|
|
||||||
log.complain("TEST FAILURE: method Debugee.threadByName() returned null for debuggee thread "
|
log.complain("TEST FAILURE: method Debugee.threadByName() returned null for debuggee thread "
|
||||||
+ DEBUGGEE_THRNAME);
|
+ DEBUGGEE_THRNAME);
|
||||||
tot_res = Consts.TEST_FAILED;
|
tot_res = Consts.TEST_FAILED;
|
||||||
return quitDebuggee();
|
return quitDebuggee();
|
||||||
}
|
}
|
||||||
|
|
||||||
Field doExit = null;
|
Field stopLoop1 = null;
|
||||||
|
Field stopLoop2 = null;
|
||||||
ObjectReference objRef = null;
|
ObjectReference objRef = null;
|
||||||
|
ObjectReference throwableRef = null;
|
||||||
try {
|
try {
|
||||||
// debuggee main class
|
// debuggee main class
|
||||||
ReferenceType rType = debuggee.classByName(DEBUGGEE_CLASS);
|
mainClass = debuggee.classByName(DEBUGGEE_CLASS);
|
||||||
|
|
||||||
suspendAtBP(rType, DEBUGGEE_STOPATLINE);
|
suspendAtBP(mainClass, DEBUGGEE_STOPATLINE);
|
||||||
objRef = findObjRef(thrRef, DEBUGGEE_LOCALVAR);
|
objRef = findObjRef(thrRef, DEBUGGEE_NON_THROWABLE_VAR);
|
||||||
|
throwableRef = findObjRef(thrRef, DEBUGGEE_THROWABLE_VAR);
|
||||||
|
|
||||||
// this field is used to indicate that debuggee has to
|
// These fields are used to indicate that debuggee has to stop looping
|
||||||
// stop looping
|
stopLoop1 = mainClass.fieldByName(DEBUGGEE_STOP_LOOP1_FIELD);
|
||||||
doExit = rType.fieldByName(DEBUGGEE_FIELD);
|
stopLoop2 = mainClass.fieldByName(DEBUGGEE_STOP_LOOP2_FIELD);
|
||||||
|
if (stopLoop1 == null || stopLoop2 == null) {
|
||||||
log.display("Resuming debuggee VM ...");
|
throw new RuntimeException("Failed to find a \"stop loop\" field");
|
||||||
vm.resume();
|
|
||||||
log.display("Resumption of debuggee VM done");
|
|
||||||
|
|
||||||
log.display("\nTrying to stop debuggee thread \"" + thrRef
|
|
||||||
+ "\"\n\tusing non-throwable object reference \""
|
|
||||||
+ objRef + "\" as a parameter ...");
|
|
||||||
|
|
||||||
// Check the tested assersion
|
|
||||||
try {
|
|
||||||
thrRef.stop(objRef);
|
|
||||||
log.complain("TEST FAILED: expected IllegalArgumentException was not thrown"
|
|
||||||
+ "\n\twhen attempted to stop debuggee thread \"" + thrRef
|
|
||||||
+ "\"\n\tusing non-throwable object reference \""
|
|
||||||
+ objRef + "\" as a parameter");
|
|
||||||
tot_res = Consts.TEST_FAILED;
|
|
||||||
} catch(InvalidTypeException ee) {
|
|
||||||
log.display("CHECK PASSED: caught expected " + ee);
|
|
||||||
} catch(Exception ue) {
|
|
||||||
ue.printStackTrace();
|
|
||||||
log.complain("TEST FAILED: ThreadReference.stop(): caught unexpected "
|
|
||||||
+ ue + "\n\tinstead of InvalidTypeException"
|
|
||||||
+ "\n\twhen attempted to stop debuggee thread \"" + thrRef
|
|
||||||
+ "\"\n\tusing non-throwable object reference \""
|
|
||||||
+ objRef + "\" as a parameter");
|
|
||||||
tot_res = Consts.TEST_FAILED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.display("non-throwable object: \"" + objRef + "\"");
|
||||||
|
log.display("throwable object: \"" + throwableRef + "\"");
|
||||||
|
log.display("debuggee thread: \"" + thrRef + "\"");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test #1: verify using a non-throwable object with stop() fails appropriately.
|
||||||
|
*/
|
||||||
|
log.display("\nTEST #1: Trying to stop debuggee thread using non-throwable object.");
|
||||||
|
try {
|
||||||
|
thrRef.stop(objRef); // objRef is an instance of the debuggee class, not a Throwable
|
||||||
|
log.complain("TEST #1 FAILED: expected InvalidTypeException was not thrown");
|
||||||
|
tot_res = Consts.TEST_FAILED;
|
||||||
|
} catch (InvalidTypeException ee) {
|
||||||
|
log.display("TEST #1 PASSED: caught expected " + ee);
|
||||||
|
} catch (Exception ue) {
|
||||||
|
ue.printStackTrace();
|
||||||
|
log.complain("TEST #1 FAILED: caught unexpected " + ue + "instead of InvalidTypeException");
|
||||||
|
tot_res = Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
|
log.display("TEST #1: all done.");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test #2: verify that stop() works when suspended at a breakpoint.
|
||||||
|
*/
|
||||||
|
log.display("\nTEST #2: Trying to stop debuggee thread while suspended at a breakpoint.");
|
||||||
|
try {
|
||||||
|
thrRef.stop(throwableRef);
|
||||||
|
log.display("TEST #2 PASSED: stop() call succeeded.");
|
||||||
|
} catch (Exception ue) {
|
||||||
|
ue.printStackTrace();
|
||||||
|
log.complain("TEST #2 FAILED: caught unexpected " + ue);
|
||||||
|
tot_res = Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
|
log.display("TEST #2: Resuming debuggee VM to allow async exception to be handled");
|
||||||
|
vm.resume();
|
||||||
|
log.display("TEST #2: all done.");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test #3: verify that stop() works when not suspended in a loop. Expect
|
||||||
|
* IllegalThreadStateException for virtual threads.
|
||||||
|
*/
|
||||||
|
log.display("\nTEST #3: Trying to stop debuggee thread while not suspended in a loop.");
|
||||||
|
waitForTestReady(3);
|
||||||
|
try {
|
||||||
|
thrRef.stop(throwableRef);
|
||||||
|
if (vthreadMode) {
|
||||||
|
log.complain("TEST #3 FAILED: expected IllegalThreadStateException"
|
||||||
|
+ " was not thrown for virtual thread");
|
||||||
|
tot_res = Consts.TEST_FAILED;
|
||||||
|
} else {
|
||||||
|
log.display("TEST #3 PASSED: stop() call succeeded.");
|
||||||
|
}
|
||||||
|
} catch (Exception ue) {
|
||||||
|
if (vthreadMode && ue instanceof IllegalThreadStateException) {
|
||||||
|
log.display("TEST #3 PASSED: stop() call threw IllegalThreadStateException"
|
||||||
|
+ " for virtual thread");
|
||||||
|
} else {
|
||||||
|
ue.printStackTrace();
|
||||||
|
log.complain("TEST #3 FAILED: caught unexpected " + ue);
|
||||||
|
tot_res = Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// Force the debuggee out of the loop. Not really needed if the stop() call
|
||||||
|
// successfully threw the async exception, but it's easier to just always do this.
|
||||||
|
log.display("TEST #3: clearing loop flag.");
|
||||||
|
objRef.setValue(stopLoop1, vm.mirrorOf(true));
|
||||||
|
}
|
||||||
|
log.display("TEST #3: all done.");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test #4: verify that stop() works when suspended in a loop
|
||||||
|
*/
|
||||||
|
log.display("\nTEST #4: Trying to stop debuggee thread while suspended in a loop.");
|
||||||
|
waitForTestReady(4);
|
||||||
|
try {
|
||||||
|
thrRef.suspend();
|
||||||
|
log.display("TEST #4: thread is suspended.");
|
||||||
|
thrRef.stop(throwableRef);
|
||||||
|
log.display("TEST #4 PASSED: stop() call succeeded.");
|
||||||
|
} catch (Throwable ue) {
|
||||||
|
ue.printStackTrace();
|
||||||
|
log.complain("TEST #4 FAILED: caught unexpected " + ue);
|
||||||
|
tot_res = Consts.TEST_FAILED;
|
||||||
|
} finally {
|
||||||
|
log.display("TEST #4: resuming thread.");
|
||||||
|
thrRef.resume();
|
||||||
|
// Force the debuggee out of the loop. Not really needed if the stop() call
|
||||||
|
// successfully threw the async exception, but it's easier to just always do this.
|
||||||
|
log.display("TEST #4: clearing loop flag.");
|
||||||
|
objRef.setValue(stopLoop2, vm.mirrorOf(true));
|
||||||
|
}
|
||||||
|
log.display("TEST #4: all done.");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test #5: verify that stop() works when suspended in Thread.sleep(). Expect
|
||||||
|
* OpaqueFrameException for virtual threads.
|
||||||
|
*/
|
||||||
|
log.display("\nTEST #5: Trying to stop debuggee thread while suspended in Thread.sleep().");
|
||||||
|
waitForTestReady(5);
|
||||||
|
// Allow debuggee to reach Thread.sleep() first.
|
||||||
|
log.display("TEST #5: waiting for debuggee to sleep...");
|
||||||
|
while (true) {
|
||||||
|
int status = thrRef.status();
|
||||||
|
if (status == ThreadReference.THREAD_STATUS_SLEEPING ||
|
||||||
|
status == ThreadReference.THREAD_STATUS_WAIT)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Thread.sleep(50);
|
||||||
|
}
|
||||||
|
log.display("TEST #5: debuggee is sleeping.");
|
||||||
|
try {
|
||||||
|
thrRef.suspend();
|
||||||
|
log.display("TEST #5: thread is suspended.");
|
||||||
|
thrRef.stop(throwableRef);
|
||||||
|
if (vthreadMode) {
|
||||||
|
log.complain("TEST #5 FAILED: expected OpaqueFrameException was not thrown");
|
||||||
|
tot_res = Consts.TEST_FAILED;
|
||||||
|
} else {
|
||||||
|
log.display("TEST #5 PASSED: stop() call for suspended thread succeeded");
|
||||||
|
}
|
||||||
|
} catch (Throwable ue) {
|
||||||
|
if (vthreadMode && ue instanceof OpaqueFrameException) {
|
||||||
|
log.display("TEST #5 PASSED: stop() call threw OpaqueFrameException for virtual thread");
|
||||||
|
} else {
|
||||||
|
ue.printStackTrace();
|
||||||
|
log.complain("TEST #5 FAILED: caught unexpected " + ue);
|
||||||
|
tot_res = Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
log.display("TEST #5: resuming thread.");
|
||||||
|
thrRef.resume();
|
||||||
|
}
|
||||||
|
log.display("TEST #5: all done.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.complain("TEST FAILURE: caught unexpected exception: " + e);
|
log.complain("TEST FAILURE: caught unexpected exception: " + e);
|
||||||
tot_res = Consts.TEST_FAILED;
|
tot_res = Consts.TEST_FAILED;
|
||||||
} finally {
|
} finally {
|
||||||
// Finish the test
|
// Force the debuggee out of both loops
|
||||||
// force an method to exit
|
if (objRef != null && stopLoop1 != null && stopLoop2 != null) {
|
||||||
if (objRef != null && doExit != null) {
|
|
||||||
try {
|
try {
|
||||||
objRef.setValue(doExit, vm.mirrorOf(true));
|
objRef.setValue(stopLoop1, vm.mirrorOf(true));
|
||||||
} catch(Exception sve) {
|
objRef.setValue(stopLoop2, vm.mirrorOf(true));
|
||||||
|
} catch (Exception sve) {
|
||||||
sve.printStackTrace();
|
sve.printStackTrace();
|
||||||
|
tot_res = Consts.TEST_FAILED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,6 +293,15 @@ public class stop002 {
|
|||||||
return quitDebuggee();
|
return quitDebuggee();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void waitForTestReady(int testNum) {
|
||||||
|
log.display("TEST #" + testNum + ": waiting for test ready...");
|
||||||
|
IntegerValue ival;
|
||||||
|
do {
|
||||||
|
ival = (IntegerValue)mainClass.getValue(mainClass.fieldByName("testNumReady"));
|
||||||
|
} while (ival.value() != testNum);
|
||||||
|
log.display("TEST #" + testNum + ": test ready.");
|
||||||
|
}
|
||||||
|
|
||||||
private ObjectReference findObjRef(ThreadReference thrRef, String varName) {
|
private ObjectReference findObjRef(ThreadReference thrRef, String varName) {
|
||||||
try {
|
try {
|
||||||
List frames = thrRef.frames();
|
List frames = thrRef.frames();
|
||||||
@ -184,9 +316,9 @@ public class stop002 {
|
|||||||
return (ObjectReference)
|
return (ObjectReference)
|
||||||
stackFr.getValue(locVar);
|
stackFr.getValue(locVar);
|
||||||
}
|
}
|
||||||
} catch(AbsentInformationException e) {
|
} catch (AbsentInformationException e) {
|
||||||
// this is not needed stack frame, ignoring
|
// this is not needed stack frame, ignoring
|
||||||
} catch(NativeMethodException ne) {
|
} catch (NativeMethodException ne) {
|
||||||
// current method is native, also ignoring
|
// current method is native, also ignoring
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -35,7 +35,10 @@ import nsk.share.jdi.*;
|
|||||||
public class stop002t {
|
public class stop002t {
|
||||||
private Log log;
|
private Log log;
|
||||||
private IOPipe pipe;
|
private IOPipe pipe;
|
||||||
volatile boolean stopLooping = false;
|
volatile boolean stopLooping1 = false;
|
||||||
|
volatile boolean stopLooping2 = false;
|
||||||
|
volatile static int testNumReady = 0;
|
||||||
|
static final boolean vthreadMode = "Virtual".equals(System.getProperty("main.wrapper"));
|
||||||
|
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
System.exit(run(args) + Consts.JCK_STATUS_BASE);
|
System.exit(run(args) + Consts.JCK_STATUS_BASE);
|
||||||
@ -57,23 +60,138 @@ public class stop002t {
|
|||||||
// as wrong parameter of JDI method ThreadReference.stop()
|
// as wrong parameter of JDI method ThreadReference.stop()
|
||||||
stop002t stop002tNonThrowable = this;
|
stop002t stop002tNonThrowable = this;
|
||||||
|
|
||||||
|
// throwable object which will be used by debugger
|
||||||
|
// as valid parameter of JDI method ThreadReference.stop()
|
||||||
|
Throwable stop002tThrowable = new MyThrowable("Async exception");
|
||||||
|
|
||||||
// Now the debuggee is ready for testing
|
// Now the debuggee is ready for testing
|
||||||
pipe.println(stop002.COMMAND_READY);
|
pipe.println(stop002.COMMAND_READY);
|
||||||
String cmd = pipe.readln();
|
String cmd = pipe.readln();
|
||||||
if (cmd.equals(stop002.COMMAND_QUIT)) {
|
if (cmd.equals(stop002.COMMAND_QUIT)) {
|
||||||
log.complain("Debuggee: exiting due to the command "
|
log.complain("Debuggee: premature debuggee exit due to the command "
|
||||||
+ cmd);
|
+ cmd);
|
||||||
return Consts.TEST_PASSED;
|
return Consts.TEST_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stopMeHere = 0; // stop002.DEBUGGEE_STOPATLINE
|
/*
|
||||||
|
* TEST #1: Tests that stop() properly throws InvalidTypeException if
|
||||||
|
* the specified throwable is not an instance of java.lang.Throwable
|
||||||
|
* in the debuggee. It does not involve the debuggee at all, so there
|
||||||
|
* is no code here for it.
|
||||||
|
*/
|
||||||
|
|
||||||
log.display("Debuggee: going to loop ...");
|
/*
|
||||||
while(!stopLooping) { // looping
|
* TEST #2: async exception while suspended at a breakpoint.
|
||||||
stopMeHere++; stopMeHere--;
|
*/
|
||||||
|
int stopMeHere = 0;
|
||||||
|
try {
|
||||||
|
stopMeHere = 1; // stop002.DEBUGGEE_STOPATLINE
|
||||||
|
log.complain("TEST #2: Failed to throw expected exception");
|
||||||
|
return Consts.TEST_FAILED;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Call Thread.interrupted(). Workaround for JDK-8306324
|
||||||
|
log.display("TEST #2: interrupted = " + Thread.interrupted());
|
||||||
|
if (t instanceof MyThrowable) {
|
||||||
|
log.display("TEST #2: Caught expected exception while at breakpoint: " + t);
|
||||||
|
} else {
|
||||||
|
log.complain("TEST #2: Unexpected exception caught: " + t);
|
||||||
|
t.printStackTrace();
|
||||||
|
return Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.display("Debuggee: looping done");
|
log.display("TEST #2: all done");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TEST #3: async exception while not suspended in a loop.
|
||||||
|
*/
|
||||||
|
log.display("TEST #3: going to loop ...");
|
||||||
|
try {
|
||||||
|
while (!stopLooping1) { // looping
|
||||||
|
testNumReady = 3; // signal debugger side of test that we are ready
|
||||||
|
stopMeHere++; stopMeHere--;
|
||||||
|
}
|
||||||
|
if (vthreadMode) {
|
||||||
|
log.display("TEST #3: Correctly did not throw async exception for virtual thread");
|
||||||
|
} else {
|
||||||
|
log.complain("TEST #3: Failed to throw expected exception");
|
||||||
|
return Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Call Thread.interrupted(). Workaround for JDK-8306324
|
||||||
|
log.display("TEST #3: interrupted = " + Thread.interrupted());
|
||||||
|
// We don't expect the exception to be thrown when in vthread mode.
|
||||||
|
if (!vthreadMode && t instanceof MyThrowable) {
|
||||||
|
log.display("TEST #3: Caught expected exception while in loop: " + t);
|
||||||
|
} else {
|
||||||
|
log.complain("TEST #3: Unexpected exception caught: " + t);
|
||||||
|
t.printStackTrace();
|
||||||
|
return Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.display("TEST #3: all done");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TEST #4: async exception while suspended in a loop.
|
||||||
|
*/
|
||||||
|
log.display("TEST #4: going to loop ...");
|
||||||
|
try {
|
||||||
|
while (!stopLooping2) { // looping
|
||||||
|
testNumReady = 4; // signal debugger side of test that we are ready
|
||||||
|
stopMeHere++; stopMeHere--;
|
||||||
|
}
|
||||||
|
log.complain("TEST #4: Failed to throw expected exception");
|
||||||
|
return Consts.TEST_FAILED;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Call Thread.interrupted(). Workaround for JDK-8306324
|
||||||
|
log.display("TEST #4: interrupted = " + Thread.interrupted());
|
||||||
|
if (t instanceof MyThrowable) {
|
||||||
|
log.display("TEST #4: Caught expected exception while in loop: " + t);
|
||||||
|
} else {
|
||||||
|
log.complain("TEST #4: Unexpected exception caught: " + t);
|
||||||
|
t.printStackTrace();
|
||||||
|
return Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.display("TEST #4: all done");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TEST #5: async exception while suspended doing Thread.sleep().
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
// Signal debugger side of test that we are "almost" ready. The
|
||||||
|
// debugger will still need to check that we are in the sleep state.
|
||||||
|
testNumReady = 5;
|
||||||
|
log.display("TEST #5: going to sleep ...");
|
||||||
|
Thread.sleep(10000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.complain("TEST #5: Unexpected InterruptedException");
|
||||||
|
e.printStackTrace();
|
||||||
|
return Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
|
if (vthreadMode) {
|
||||||
|
log.display("TEST #5: Correctly did not throw exception while in sleep");
|
||||||
|
} else {
|
||||||
|
log.complain("TEST #5: Failed to throw expected exception");
|
||||||
|
return Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Call Thread.interrupted(). Workaround for JDK-8306324
|
||||||
|
log.display("TEST #5: interrupted = " + Thread.interrupted());
|
||||||
|
// We don't expect the exception to be thrown when in vthread mode.
|
||||||
|
if (!vthreadMode && t instanceof MyThrowable) {
|
||||||
|
log.display("TEST #5: Caught expected exception while in loop: " + t);
|
||||||
|
} else {
|
||||||
|
log.complain("TEST #5: Unexpected exception caught: " + t);
|
||||||
|
t.printStackTrace();
|
||||||
|
return Consts.TEST_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.display("TEST #5: all done");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test shutdown.
|
||||||
|
*/
|
||||||
cmd = pipe.readln();
|
cmd = pipe.readln();
|
||||||
if (!cmd.equals(stop002.COMMAND_QUIT)) {
|
if (!cmd.equals(stop002.COMMAND_QUIT)) {
|
||||||
log.complain("TEST BUG: unknown debugger command: "
|
log.complain("TEST BUG: unknown debugger command: "
|
||||||
@ -83,3 +201,10 @@ public class stop002t {
|
|||||||
return Consts.TEST_PASSED;
|
return Consts.TEST_PASSED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MyThrowable extends Throwable
|
||||||
|
{
|
||||||
|
MyThrowable(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user