diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml index 926f7cecb63..1aa24ba4f83 100644 --- a/src/hotspot/share/prims/jvmti.xml +++ b/src/hotspot/share/prims/jvmti.xml @@ -3071,8 +3071,12 @@ err = (*jvmti)->Deallocate(jvmti, stack_info); - 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 called method. 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.

- 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); 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); 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); 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); 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); 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); 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. diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index 283a06e2aa3..0b09501c040 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -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); } diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 563eb39f60c..dae1775d8f8 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -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()) { diff --git a/src/hotspot/share/prims/jvmtiEnvBase.hpp b/src/hotspot/share/prims/jvmtiEnvBase.hpp index 2b7d37f8f16..d0474e544b3 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.hpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.hpp @@ -224,6 +224,10 @@ class JvmtiEnvBase : public CHeapObj { // 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())); }; diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index efe88a86045..1ddeb75ef35 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -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 diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/BoundVThreadTest/libBoundVThreadTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/BoundVThreadTest/libBoundVThreadTest.cpp index 6245f2acefe..0fa0f065ae8 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/BoundVThreadTest/libBoundVThreadTest.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/BoundVThreadTest/libBoundVThreadTest.cpp @@ -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); diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/ForceEarlyReturnTest/ForceEarlyReturnTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/ForceEarlyReturnTest/ForceEarlyReturnTest.java new file mode 100644 index 00000000000..b9542844bf8 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/ForceEarlyReturnTest/ForceEarlyReturnTest.java @@ -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); + } + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/ForceEarlyReturnTest/libForceEarlyReturnTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/ForceEarlyReturnTest/libForceEarlyReturnTest.cpp new file mode 100644 index 00000000000..86cdf781f7f --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/ForceEarlyReturnTest/libForceEarlyReturnTest.cpp @@ -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 +#include +#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" diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadUnsupportedTest/libVThreadUnsupportedTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadUnsupportedTest/libVThreadUnsupportedTest.cpp index 2ec30dc4a5a..14698b6e528 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadUnsupportedTest/libVThreadUnsupportedTest.cpp +++ b/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);