8308400: add ForceEarlyReturn support for virtual threads
Reviewed-by: alanb, lmesnik
This commit is contained in:
parent
1451ac1770
commit
89b99143ac
src/hotspot/share/prims
test/hotspot/jtreg
ProblemList-Virtual.txt
serviceability/jvmti/vthread
BoundVThreadTest
ForceEarlyReturnTest
VThreadUnsupportedTest
@ -3071,8 +3071,12 @@ err = (*jvmti)->Deallocate(jvmti, stack_info);
|
||||
|
||||
<category id="ForceEarlyReturn" label="Force Early Return">
|
||||
<intro>
|
||||
These functions allow an agent to force a method
|
||||
to return at any point during its execution.
|
||||
These functions allow an agent to force a return from the current frame.
|
||||
The specified thread must be suspended or must be the current thread.
|
||||
These functions 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.
|
||||
The method which will return early is referred to as the <i>called method</i>.
|
||||
The called method is the current method
|
||||
(as defined by
|
||||
@ -3080,7 +3084,6 @@ err = (*jvmti)->Deallocate(jvmti, stack_info);
|
||||
for the specified thread at
|
||||
the time the function is called.
|
||||
<p/>
|
||||
The specified thread must be suspended or must be the current thread.
|
||||
The return occurs when execution of Java programming
|
||||
language code is resumed on this thread.
|
||||
Between calling one of these functions and resumption
|
||||
@ -3136,7 +3139,7 @@ err = (*jvmti)->Deallocate(jvmti, stack_info);
|
||||
<error id="JVMTI_ERROR_OPAQUE_FRAME">
|
||||
Attempted to return early from a frame
|
||||
corresponding to a native method.
|
||||
The thread is a virtual thread and the implementation is
|
||||
The thread is a suspended virtual thread and the implementation was
|
||||
unable to force its current frame to return.
|
||||
Or the implementation is unable to provide
|
||||
this functionality on this frame.
|
||||
@ -3188,7 +3191,7 @@ err = (*jvmti)->Deallocate(jvmti, stack_info);
|
||||
<error id="JVMTI_ERROR_OPAQUE_FRAME">
|
||||
Attempted to return early from a frame
|
||||
corresponding to a native method.
|
||||
The thread is a virtual thread and the implementation is
|
||||
The thread is a suspended virtual thread and the implementation was
|
||||
unable to force its current frame to return.
|
||||
Or the implementation is unable to provide
|
||||
this functionality on this frame.
|
||||
@ -3236,7 +3239,7 @@ err = (*jvmti)->Deallocate(jvmti, stack_info);
|
||||
<error id="JVMTI_ERROR_OPAQUE_FRAME">
|
||||
Attempted to return early from a frame
|
||||
corresponding to a native method.
|
||||
The thread is a virtual thread and the implementation is
|
||||
The thread is a suspended virtual thread and the implementation was
|
||||
unable to force its current frame to return.
|
||||
Or the implementation is unable to provide
|
||||
this functionality on this frame.
|
||||
@ -3281,7 +3284,7 @@ err = (*jvmti)->Deallocate(jvmti, stack_info);
|
||||
<error id="JVMTI_ERROR_OPAQUE_FRAME">
|
||||
Attempted to return early from a frame
|
||||
corresponding to a native method.
|
||||
The thread is a virtual thread and the implementation is
|
||||
The thread is a suspended virtual thread and the implementation was
|
||||
unable to force its current frame to return.
|
||||
Or the implementation is unable to provide
|
||||
this functionality on this frame.
|
||||
@ -3326,7 +3329,7 @@ err = (*jvmti)->Deallocate(jvmti, stack_info);
|
||||
<error id="JVMTI_ERROR_OPAQUE_FRAME">
|
||||
Attempted to return early from a frame
|
||||
corresponding to a native method.
|
||||
The thread is a virtual thread and the implementation is
|
||||
The thread is a suspended virtual thread and the implementation was
|
||||
unable to force its current frame to return.
|
||||
Or the implementation is unable to provide
|
||||
this functionality on this frame.
|
||||
@ -3365,7 +3368,7 @@ err = (*jvmti)->Deallocate(jvmti, stack_info);
|
||||
<error id="JVMTI_ERROR_OPAQUE_FRAME">
|
||||
Attempted to return early from a frame
|
||||
corresponding to a native method.
|
||||
The thread is a virtual thread and the implementation is
|
||||
The thread is a suspended virtual thread and the implementation was
|
||||
unable to force its current frame to return.
|
||||
Or the implementation is unable to provide
|
||||
this functionality on this frame.
|
||||
|
@ -1886,22 +1886,11 @@ JvmtiEnv::PopFrame(jthread thread) {
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
return err;
|
||||
}
|
||||
bool is_virtual = thread_obj != nullptr && thread_obj->is_a(vmClasses::BaseVirtualThread_klass());
|
||||
bool self = java_thread == current_thread;
|
||||
|
||||
if (is_virtual) {
|
||||
if (!is_JavaThread_current(java_thread, thread_obj)) {
|
||||
if (!is_vthread_suspended(thread_obj, java_thread)) {
|
||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
}
|
||||
if (java_thread == nullptr) { // unmounted virtual thread
|
||||
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||
}
|
||||
}
|
||||
} else { // platform thread
|
||||
if (java_thread != current_thread && !java_thread->is_suspended() &&
|
||||
!java_thread->is_carrier_thread_suspended()) {
|
||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
}
|
||||
err = check_non_suspended_or_opaque_frame(java_thread, thread_obj, self);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// retrieve or create the state
|
||||
@ -1919,8 +1908,8 @@ JvmtiEnv::PopFrame(jthread thread) {
|
||||
|
||||
MutexLocker mu(JvmtiThreadState_lock);
|
||||
UpdateForPopTopFrameClosure op(state);
|
||||
if (java_thread == current_thread) {
|
||||
op.doit(java_thread, true /* self */);
|
||||
if (self) {
|
||||
op.doit(java_thread, self);
|
||||
} else {
|
||||
Handshake::execute(&op, java_thread);
|
||||
}
|
||||
|
@ -1381,6 +1381,30 @@ JvmtiEnvBase::get_threadOop_and_JavaThread(ThreadsList* t_list, jthread thread,
|
||||
return JVMTI_ERROR_NONE;
|
||||
}
|
||||
|
||||
// Check for JVMTI_ERROR_NOT_SUSPENDED and JVMTI_ERROR_OPAQUE_FRAME errors.
|
||||
// Used in PopFrame and ForceEarlyReturn implementations.
|
||||
jvmtiError
|
||||
JvmtiEnvBase::check_non_suspended_or_opaque_frame(JavaThread* jt, oop thr_obj, bool self) {
|
||||
bool is_virtual = thr_obj != nullptr && thr_obj->is_a(vmClasses::BaseVirtualThread_klass());
|
||||
|
||||
if (is_virtual) {
|
||||
if (!is_JavaThread_current(jt, thr_obj)) {
|
||||
if (!is_vthread_suspended(thr_obj, jt)) {
|
||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
}
|
||||
if (jt == nullptr) { // unmounted virtual thread
|
||||
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||
}
|
||||
}
|
||||
} else { // platform thread
|
||||
if (!self && !jt->is_suspended() &&
|
||||
!jt->is_carrier_thread_suspended()) {
|
||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
}
|
||||
}
|
||||
return JVMTI_ERROR_NONE;
|
||||
}
|
||||
|
||||
jvmtiError
|
||||
JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject object, jvmtiMonitorUsage* info_ptr) {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
|
||||
@ -2036,10 +2060,12 @@ JvmtiEnvBase::force_early_return(jthread thread, jvalue value, TosState tos) {
|
||||
oop thread_obj = nullptr;
|
||||
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
|
||||
|
||||
if (thread_obj != nullptr && thread_obj->is_a(vmClasses::BaseVirtualThread_klass())) {
|
||||
// No support for virtual threads (yet).
|
||||
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
return err;
|
||||
}
|
||||
bool self = java_thread == current_thread;
|
||||
|
||||
err = check_non_suspended_or_opaque_frame(java_thread, thread_obj, self);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
return err;
|
||||
}
|
||||
@ -2058,8 +2084,8 @@ JvmtiEnvBase::force_early_return(jthread thread, jvalue value, TosState tos) {
|
||||
}
|
||||
|
||||
SetForceEarlyReturn op(state, value, tos);
|
||||
if (java_thread == current_thread) {
|
||||
op.doit(java_thread, true /* self */);
|
||||
if (self) {
|
||||
op.doit(java_thread, self);
|
||||
} else {
|
||||
Handshake::execute(&op, java_thread);
|
||||
}
|
||||
@ -2075,12 +2101,6 @@ SetForceEarlyReturn::doit(Thread *target, bool self) {
|
||||
if (java_thread->is_exiting()) {
|
||||
return; /* JVMTI_ERROR_THREAD_NOT_ALIVE (default) */
|
||||
}
|
||||
if (!self) {
|
||||
if (!java_thread->is_suspended()) {
|
||||
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if a ForceEarlyReturn was already in progress
|
||||
if (_state->is_earlyret_pending()) {
|
||||
|
@ -224,6 +224,10 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
||||
// Check if VirtualThread or BoundVirtualThread is suspended.
|
||||
static bool is_vthread_suspended(oop vt_oop, JavaThread* jt);
|
||||
|
||||
// Check for JVMTI_ERROR_NOT_SUSPENDED and JVMTI_ERROR_OPAQUE_FRAME errors.
|
||||
// Used in PopFrame and ForceEarlyReturn implementations.
|
||||
static jvmtiError check_non_suspended_or_opaque_frame(JavaThread* jt, oop thr_obj, bool self);
|
||||
|
||||
static JvmtiEnv* JvmtiEnv_from_jvmti_env(jvmtiEnv *env) {
|
||||
return (JvmtiEnv*)((intptr_t)env - in_bytes(jvmti_external_offset()));
|
||||
};
|
||||
|
@ -99,6 +99,7 @@ vmTestbase/nsk/jdi/ExceptionEvent/catchLocation/location002/TestDescription.java
|
||||
## 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
|
||||
|
@ -115,10 +115,6 @@ test_unsupported_jvmti_functions(jvmtiEnv *jvmti, JNIEnv *jni, jthread vthread,
|
||||
fatal(jni, "Virtual threads are not supported");
|
||||
}
|
||||
|
||||
LOG("Testing ForceEarlyReturnVoid\n");
|
||||
err = jvmti->ForceEarlyReturnVoid(vthread);
|
||||
check_jvmti_error_opaque_frame(jni, "ForceEarlyReturnVoid", err);
|
||||
|
||||
LOG("Testing GetThreadCpuTime\n");
|
||||
err = jvmti->GetThreadCpuTime(vthread, &nanos);
|
||||
check_jvmti_error_unsupported_operation(jni, "GetThreadCpuTime", err);
|
||||
|
272
test/hotspot/jtreg/serviceability/jvmti/vthread/ForceEarlyReturnTest/ForceEarlyReturnTest.java
Normal file
272
test/hotspot/jtreg/serviceability/jvmti/vthread/ForceEarlyReturnTest/ForceEarlyReturnTest.java
Normal file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* 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 id=default
|
||||
* @summary Verifies JVMTI ForceEarlyReturn support for virtual threads.
|
||||
* @requires vm.continuations
|
||||
* @run main/othervm/native -agentlib:ForceEarlyReturnTest ForceEarlyReturnTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=no-vmcontinuations
|
||||
* @summary Verifies JVMTI ForceEarlyReturn support for bound virtual threads.
|
||||
* @run main/othervm/native -agentlib:ForceEarlyReturnTest -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations ForceEarlyReturnTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=platform
|
||||
* @summary Verifies JVMTI ForceEarlyReturn support for platform threads.
|
||||
* @run main/othervm/native -agentlib:ForceEarlyReturnTest ForceEarlyReturnTest platform
|
||||
*/
|
||||
|
||||
import java.lang.AssertionError;
|
||||
|
||||
/*
|
||||
* The test exercises the JVMTI function ForceEarlyReturn.
|
||||
* The test creates a new virtual or platform thread.
|
||||
* Its method run() invokes the following methods:
|
||||
* - method A() that is blocked on a monitor
|
||||
* - method B() that is stopped at a breakpoint
|
||||
* - method C() that forces agent to call ForceEarlyReturn on its own thread
|
||||
* JVMTI ForceEarlyReturn is called in all cases.
|
||||
*/
|
||||
public class ForceEarlyReturnTest {
|
||||
private static final String agentLib = "ForceEarlyReturnTest";
|
||||
static final int JVMTI_ERROR_NONE = 0;
|
||||
static final int THREAD_NOT_SUSPENDED = 13;
|
||||
static final int OPAQUE_FRAME = 32;
|
||||
static final int PASSED = 0;
|
||||
static final int FAILED = 2;
|
||||
static final int expValA1 = 111;
|
||||
static final int expValA2 = 222;
|
||||
static final String expValB1 = "B1";
|
||||
static final String expValB2 = "B2";
|
||||
static final String expValB3 = "B3";
|
||||
|
||||
static void log(String str) { System.out.println(str); }
|
||||
|
||||
static native void prepareAgent(Class taskClass);
|
||||
static native void suspendThread(Thread thread);
|
||||
static native void resumeThread(Thread thread);
|
||||
static native void ensureAtBreakpoint();
|
||||
static native void notifyAtBreakpoint();
|
||||
static native int forceEarlyReturnV(Thread thread);
|
||||
static native int forceEarlyReturnI(Thread thread, int val);
|
||||
static native int forceEarlyReturnO(Thread thread, Object obj);
|
||||
|
||||
static int status = PASSED;
|
||||
static boolean is_virtual = true;
|
||||
|
||||
static void setFailed(String msg) {
|
||||
log("\nFAILED: " + msg);
|
||||
status = FAILED;
|
||||
}
|
||||
|
||||
static void throwFailed(String msg) {
|
||||
log("\nFAILED: " + msg);
|
||||
throw new RuntimeException("ForceEarlyReturnTest failed!");
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
is_virtual = !(args.length > 0 && args[0].equals("platform"));
|
||||
run();
|
||||
if (status == FAILED) {
|
||||
throwFailed("ForceEarlyReturnTest!");
|
||||
}
|
||||
log("\nForceEarlyReturnTest passed");
|
||||
}
|
||||
|
||||
public static void run() {
|
||||
TestTask testTask = new TestTask();
|
||||
Thread testTaskThread = null;
|
||||
int errCode;
|
||||
|
||||
prepareAgent(TestTask.class);
|
||||
|
||||
log("\nMain #A: method A() must be blocked on entering a synchronized statement");
|
||||
if (is_virtual) {
|
||||
testTaskThread = Thread.ofVirtual().name("TestTaskThread").start(testTask);
|
||||
} else {
|
||||
testTaskThread = Thread.ofPlatform().name("TestTaskThread").start(testTask);
|
||||
}
|
||||
|
||||
{
|
||||
TestTask.ensureAtPointA();
|
||||
|
||||
log("\nMain #A.1: unsuspended");
|
||||
errCode = forceEarlyReturnI(testTaskThread, expValA1);
|
||||
if (errCode != THREAD_NOT_SUSPENDED) {
|
||||
throwFailed("Main #A.1: expected THREAD_NOT_SUSPENDED instead of: " + errCode);
|
||||
} else {
|
||||
log("Main #A.1: got expected THREAD_NOT_SUSPENDED");
|
||||
}
|
||||
|
||||
log("\nMain #A.2: suspended");
|
||||
suspendThread(testTaskThread);
|
||||
errCode = forceEarlyReturnI(testTaskThread, expValA2);
|
||||
if (errCode != JVMTI_ERROR_NONE) {
|
||||
throwFailed("Main #A.2: expected JVMTI_ERROR_NONE instead of: " + errCode);
|
||||
} else {
|
||||
log("Main #A.2: got expected JVMTI_ERROR_NONE");
|
||||
}
|
||||
resumeThread(testTaskThread);
|
||||
TestTask.clearDoLoop();
|
||||
TestTask.sleep(5);
|
||||
}
|
||||
|
||||
log("\nMain #B: method B() must be blocked in a breakpoint event handler");
|
||||
{
|
||||
ensureAtBreakpoint();
|
||||
|
||||
log("\nMain #B.1: unsuspended");
|
||||
errCode = forceEarlyReturnO(testTaskThread, expValB1);
|
||||
if (errCode != THREAD_NOT_SUSPENDED) {
|
||||
throwFailed("Main #B.1: expected THREAD_NOT_SUSPENDED instead of: " + errCode);
|
||||
}
|
||||
log("Main #B.1: got expected THREAD_NOT_SUSPENDED");
|
||||
|
||||
log("\nMain #B.2: suspended");
|
||||
suspendThread(testTaskThread);
|
||||
errCode = forceEarlyReturnO(testTaskThread, expValB2);
|
||||
if (errCode != JVMTI_ERROR_NONE) {
|
||||
throwFailed("Main #B.2: expected JVMTI_ERROR_NONE");
|
||||
}
|
||||
log("Main #B.2: got expected JVMTI_ERROR_NONE");
|
||||
resumeThread(testTaskThread);
|
||||
notifyAtBreakpoint();
|
||||
TestTask.sleep(5);
|
||||
|
||||
log("\nMain #B.3: unsuspended, call ForceEarlyReturn on own thread");
|
||||
ensureAtBreakpoint();
|
||||
notifyAtBreakpoint();
|
||||
TestTask.sleep(5);
|
||||
}
|
||||
|
||||
log("\nMain #C: method C() calls ForceEarlyReturn on its own thread");
|
||||
{
|
||||
// ForceEarlyReturn is called from the test task (own thread) and expected to succeed.
|
||||
// No suspension of the test task thread is required or can be done in this case.
|
||||
TestTask.ensureFinished();
|
||||
}
|
||||
|
||||
try {
|
||||
testTaskThread.join();
|
||||
} catch (InterruptedException ex) {
|
||||
throwFailed("Unexpected " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class TestTask implements Runnable {
|
||||
static void log(String str) { System.out.println(str); }
|
||||
|
||||
static volatile boolean doLoop = true;
|
||||
static volatile boolean atPointA = false;
|
||||
static volatile boolean finished = false;
|
||||
|
||||
static void sleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Interruption in TestTask.sleep: \n\t" + e);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure thread is ready.
|
||||
static void ensureAtPointA() {
|
||||
while (!atPointA) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure thread is finished.
|
||||
static void ensureFinished() {
|
||||
while (!finished) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void clearDoLoop() {
|
||||
doLoop = false;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
log("TestTask.run: started");
|
||||
|
||||
int valA2 = A();
|
||||
if (valA2 != expValA2) {
|
||||
setFailed("TestTask.A: expValA2: " + expValA2 + "got: " + valA2);
|
||||
}
|
||||
sleep(1); // to cause yield
|
||||
|
||||
String valB2 = B(false, expValB2); // false: do not force early return in breakpoint
|
||||
if (!valB2.equals(expValB2)) {
|
||||
setFailed("TestTask.B.2: expValB2: " + expValB2 + "got: " + valB2);
|
||||
}
|
||||
sleep(1); // to cause yield
|
||||
|
||||
String valB3 = B(true, expValB3); // true: force early return in breakpoint
|
||||
if (!valB3.equals(expValB3)) {
|
||||
setFailed("TestTask.B.3: expected valB3: " + expValB3 + "got: " + valB3);
|
||||
}
|
||||
sleep(1); // to cause yield
|
||||
|
||||
C();
|
||||
finished = true;
|
||||
}
|
||||
|
||||
// Method is busy in a while loop.
|
||||
// ForceEarlyReturn is used two times:
|
||||
// - when not suspended: THREAD_NOT_SUSPENDED is expected
|
||||
// - when suspended: JVMTI_ERROR_NONE is expected
|
||||
static int A() {
|
||||
log("TestTask.A: started");
|
||||
atPointA = true;
|
||||
while (doLoop) {
|
||||
}
|
||||
log("TestTask.A: finished");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// A breakpoint is set at start of this method.
|
||||
// ForceEarlyReturn is used two times:
|
||||
// - when not suspended: THREAD_NOT_SUSPENDED is expected
|
||||
// - when suspended: expected to succeed
|
||||
static String B(boolean forceRet, String retObj) {
|
||||
log("TestTask.B: started");
|
||||
return "00";
|
||||
}
|
||||
|
||||
// This method uses ForceEarlyReturn on its own thread. It is expected to return OPAQUE_FRAME.
|
||||
static void C() {
|
||||
log("TestTask.C: started");
|
||||
int errCode = ForceEarlyReturnTest.forceEarlyReturnV(Thread.currentThread());
|
||||
if (errCode == OPAQUE_FRAME) {
|
||||
log("TestTask.C: got expected OPAQUE_FRAME");
|
||||
} else {
|
||||
setFailed("TestTask.C: expected OPAQUE_FRAME from ForceEarlyReturn instead of: " + errCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
196
test/hotspot/jtreg/serviceability/jvmti/vthread/ForceEarlyReturnTest/libForceEarlyReturnTest.cpp
Normal file
196
test/hotspot/jtreg/serviceability/jvmti/vthread/ForceEarlyReturnTest/libForceEarlyReturnTest.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
#include "jvmti.h"
|
||||
#include "jvmti_common.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
static jvmtiEnv *jvmti = NULL;
|
||||
static jmethodID mid_B = NULL;
|
||||
static jrawMonitorID monitor = NULL;
|
||||
static volatile bool bp_sync_reached = false;
|
||||
|
||||
static void JNICALL
|
||||
Breakpoint(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread,
|
||||
jmethodID method, jlocation location) {
|
||||
jvmtiError err;
|
||||
|
||||
if (method != mid_B) {
|
||||
fatal(jni, "Breakpoint: Failed with wrong location: expected in method TestTask.B()");
|
||||
}
|
||||
|
||||
LOG("Breakpoint: In method TestTask.B() before sync section\n");
|
||||
{
|
||||
RawMonitorLocker rml(jvmti, jni, monitor);
|
||||
bp_sync_reached = true;
|
||||
rml.wait(0);
|
||||
}
|
||||
LOG("Breakpoint: In method TestTask.B() after sync section\n");
|
||||
|
||||
jint force_return = 0;
|
||||
err = jvmti->GetLocalInt(thread, 0 /* top frame */, 0 /* slot #0 */, &force_return);
|
||||
check_jvmti_status(jni, err, "Breakpoint: Failed in JVMTI GetLocalInt");
|
||||
|
||||
if (force_return != 0) {
|
||||
jobject ret_obj = NULL;
|
||||
|
||||
err = jvmti->ClearBreakpoint(mid_B, 0);
|
||||
check_jvmti_status(jni, err, "Breakpoint: Failed in JVMTI ClearBreakpoint");
|
||||
|
||||
err = jvmti->GetLocalObject(thread, 0 /* top frame */, 1 /* slot #1 */, &ret_obj);
|
||||
check_jvmti_status(jni, err, "Breakpoint: Failed in JVMTI GetLocalObject");
|
||||
|
||||
LOG("Breakpoint: Self early return from method TestTask.B()\n");
|
||||
err = jvmti->ForceEarlyReturnObject(thread, ret_obj);
|
||||
LOG("Breakpoint: ForceEarlyReturnObject returned code: %s (%d)\n", TranslateError(err), err);
|
||||
check_jvmti_status(jni, err, "Breakpoint: Failed in ForceEarlyReturnObject");
|
||||
}
|
||||
LOG("Breakpoint: In method TestTask.B() finished\n");
|
||||
}
|
||||
|
||||
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
|
||||
static jvmtiCapabilities caps;
|
||||
static jvmtiEventCallbacks callbacks;
|
||||
jvmtiError err;
|
||||
jint res;
|
||||
|
||||
LOG("Agent init\n");
|
||||
res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
|
||||
if (res != JNI_OK || jvmti == NULL) {
|
||||
LOG("Agent init: Failed in GetEnv!\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
err = jvmti->GetPotentialCapabilities(&caps);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
LOG("Agent init: Failed in GetPotentialCapabilities: %s (%d)\n", TranslateError(err), err);
|
||||
return JNI_ERR;
|
||||
}
|
||||
err = jvmti->AddCapabilities(&caps);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
LOG("Agent init: Failed in AddCapabilities: %s (%d)\n", TranslateError(err), err);
|
||||
return JNI_ERR;
|
||||
}
|
||||
err = jvmti->GetCapabilities(&caps);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
LOG("Agent init: Failed in GetCapabilities: %s (%d)\n", TranslateError(err), err);
|
||||
return JNI_ERR;
|
||||
}
|
||||
if (!caps.can_generate_breakpoint_events) {
|
||||
LOG("Agent init: Failed: Breakpoint event is not implemented\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
callbacks.Breakpoint = &Breakpoint;
|
||||
err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
LOG("Agent init: Failed in SetEventCallbacks: %s (%d)\n", TranslateError(err), err);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
monitor = create_raw_monitor(jvmti, "Raw monitor to test");
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
extern JNIEXPORT jint JNICALL
|
||||
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
|
||||
return Agent_Initialize(jvm, options, reserved);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ForceEarlyReturnTest_prepareAgent(JNIEnv *jni, jclass cls, jclass task_clazz) {
|
||||
jvmtiError err;
|
||||
|
||||
LOG("Main: prepareAgent started\n");
|
||||
|
||||
if (jvmti == NULL) {
|
||||
fatal(jni, "prepareAgent: Failed as JVMTI client was not properly loaded!\n");
|
||||
}
|
||||
mid_B = jni->GetStaticMethodID(task_clazz, "B", "(ZLjava/lang/String;)Ljava/lang/String;");
|
||||
if (mid_B == NULL) {
|
||||
fatal(jni, "prepareAgent: Failed to find Method ID for method: TestTask.B()\n");
|
||||
}
|
||||
err = jvmti->SetBreakpoint(mid_B, 0); // location: 0
|
||||
check_jvmti_status(jni, err, "prepareAgent: Failed in JVMTI SetBreakpoint");
|
||||
|
||||
set_event_notification_mode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, NULL);
|
||||
|
||||
LOG("Main: prepareAgent finished\n");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ForceEarlyReturnTest_suspendThread(JNIEnv *jni, jclass cls, jthread thread) {
|
||||
LOG("Main: suspendThread\n");
|
||||
suspend_thread(jvmti, jni, thread);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ForceEarlyReturnTest_resumeThread(JNIEnv *jni, jclass cls, jthread thread) {
|
||||
LOG("Main: resumeThread\n");
|
||||
resume_thread(jvmti, jni, thread);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ForceEarlyReturnTest_forceEarlyReturnV(JNIEnv *jni, jclass cls, jthread thread) {
|
||||
jvmtiError err = jvmti->ForceEarlyReturnVoid(thread);
|
||||
LOG("Main: forceEarlyReturn: ForceEarlyReturnVoid returned code: %s (%d)\n", TranslateError(err), err);
|
||||
return (jint)err;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ForceEarlyReturnTest_forceEarlyReturnI(JNIEnv *jni, jclass cls, jthread thread, jint val) {
|
||||
jvmtiError err = jvmti->ForceEarlyReturnInt(thread, val);
|
||||
LOG("Main: forceEarlyReturn: ForceEarlyReturnInt returned code: %s (%d)\n", TranslateError(err), err);
|
||||
return (jint)err;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ForceEarlyReturnTest_forceEarlyReturnO(JNIEnv *jni, jclass cls, jthread thread, jobject obj) {
|
||||
jvmtiError err = jvmti->ForceEarlyReturnObject(thread, obj);
|
||||
LOG("Main: forceEarlyReturn: ForceEarlyReturnObject returned code: %s (%d)\n", TranslateError(err), err);
|
||||
return (jint)err;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ForceEarlyReturnTest_ensureAtBreakpoint(JNIEnv *jni, jclass cls) {
|
||||
bool need_stop = false;
|
||||
|
||||
LOG("Main: ensureAtBreakpoint\n");
|
||||
while (!need_stop) {
|
||||
RawMonitorLocker rml(jvmti, jni, monitor);
|
||||
need_stop = bp_sync_reached;
|
||||
sleep_ms(1); // 1 millisecond
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ForceEarlyReturnTest_notifyAtBreakpoint(JNIEnv *jni, jclass cls) {
|
||||
LOG("Main: notifyAtBreakpoint\n");
|
||||
RawMonitorLocker rml(jvmti, jni, monitor);
|
||||
bp_sync_reached = false;
|
||||
rml.notify_all();
|
||||
}
|
||||
|
||||
} // extern "C"
|
4
test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadUnsupportedTest/libVThreadUnsupportedTest.cpp
4
test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadUnsupportedTest/libVThreadUnsupportedTest.cpp
@ -87,10 +87,6 @@ test_unsupported_jvmti_functions(jvmtiEnv *jvmti, JNIEnv *jni, jthread vthread)
|
||||
|
||||
LOG("Testing JVMTI functions which should not accept a virtual thread argument\n");
|
||||
|
||||
LOG("Testing ForceEarlyReturnVoid\n");
|
||||
err = jvmti->ForceEarlyReturnVoid(vthread);
|
||||
check_jvmti_error_opaque_frame(jni, "ForceEarlyReturnVoid", err);
|
||||
|
||||
LOG("Testing GetThreadCpuTime\n");
|
||||
err = jvmti->GetThreadCpuTime(vthread, &nanos);
|
||||
check_jvmti_error_unsupported_operation(jni, "GetThreadCpuTime", err);
|
||||
|
Loading…
x
Reference in New Issue
Block a user