8306034: add support of virtual threads to JVMTI StopThread
Reviewed-by: cjplummer
This commit is contained in:
parent
489658dbd2
commit
51b8f3cfb9
src/hotspot/share
test/hotspot/jtreg
ProblemList-Virtual.txt
serviceability/jvmti/vthread
BoundVThreadTest
StopThreadTest
VThreadUnsupportedTest
@ -1892,7 +1892,7 @@ jvmtiEnv *jvmti;
|
||||
<function id="StopThread" num="7">
|
||||
<synopsis>Stop Thread</synopsis>
|
||||
<description>
|
||||
Send the specified asynchronous exception to the specified platform thread.
|
||||
Send the specified asynchronous exception to the specified thread.
|
||||
</description>
|
||||
<origin>jvmdi</origin>
|
||||
<capabilities>
|
||||
@ -1903,8 +1903,10 @@ jvmtiEnv *jvmti;
|
||||
<jthread impl="noconvert"/>
|
||||
<description>
|
||||
The thread to stop.
|
||||
The <code>thread</code> may not be a virtual thread. Otherwise, the error code
|
||||
<errorlink id="JVMTI_ERROR_UNSUPPORTED_OPERATION"></errorlink> will be returned.
|
||||
The <functionlink id="StopThread"></functionlink> function may be used to send
|
||||
an asynchronous exception to a virtual thread when it is suspended at an event.
|
||||
An implementation may support sending an asynchronous exception to a suspended
|
||||
virtual thread in other cases.
|
||||
</description>
|
||||
</param>
|
||||
<param id="exception">
|
||||
@ -1915,8 +1917,12 @@ jvmtiEnv *jvmti;
|
||||
</param>
|
||||
</parameters>
|
||||
<errors>
|
||||
<error id="JVMTI_ERROR_UNSUPPORTED_OPERATION">
|
||||
<paramlink id="thread"/> is a virtual thread.
|
||||
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
|
||||
Thread is a virtual thread and was not suspended and was not the current thread.
|
||||
</error>
|
||||
<error id="JVMTI_ERROR_OPAQUE_FRAME">
|
||||
The thread is a suspended virtual thread and the implementation was unable
|
||||
to throw an asynchronous exception from the current frame.
|
||||
</error>
|
||||
</errors>
|
||||
</function>
|
||||
@ -11975,7 +11981,8 @@ myInit() {
|
||||
There are no Java programming language or JNI stack frames at the specified depth.
|
||||
</errorid>
|
||||
<errorid id="JVMTI_ERROR_OPAQUE_FRAME" num="32">
|
||||
Information about the frame is not available (e.g. for native frames).
|
||||
Information about the frame is not available (e.g. for native frames),
|
||||
or the function cannot be performed on the thread's current frame.
|
||||
</errorid>
|
||||
<errorid id="JVMTI_ERROR_DUPLICATE" num="40">
|
||||
Item already set.
|
||||
|
@ -1190,9 +1190,15 @@ JvmtiEnv::StopThread(jthread thread, jobject exception) {
|
||||
|
||||
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
|
||||
|
||||
if (thread_oop != nullptr && thread_oop->is_a(vmClasses::BaseVirtualThread_klass())) {
|
||||
// No support for virtual threads (yet).
|
||||
return JVMTI_ERROR_UNSUPPORTED_OPERATION;
|
||||
bool is_virtual = thread_oop != nullptr && thread_oop->is_a(vmClasses::BaseVirtualThread_klass());
|
||||
|
||||
if (is_virtual && !is_JavaThread_current(java_thread, thread_oop)) {
|
||||
if (!is_vthread_suspended(thread_oop, java_thread)) {
|
||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
}
|
||||
if (java_thread == nullptr) { // unmounted virtual thread
|
||||
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||
}
|
||||
}
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
return err;
|
||||
|
@ -1319,6 +1319,19 @@ JvmtiEnvBase::is_cthread_with_continuation(JavaThread* jt) {
|
||||
return cont_entry != nullptr && is_cthread_with_mounted_vthread(jt);
|
||||
}
|
||||
|
||||
// Check if VirtualThread or BoundVirtualThread is suspended.
|
||||
bool
|
||||
JvmtiEnvBase::is_vthread_suspended(oop vt_oop, JavaThread* jt) {
|
||||
bool suspended = false;
|
||||
if (java_lang_VirtualThread::is_instance(vt_oop)) {
|
||||
suspended = JvmtiVTSuspender::is_vthread_suspended(vt_oop);
|
||||
}
|
||||
if (vt_oop->is_a(vmClasses::BoundVirtualThread_klass())) {
|
||||
suspended = jt->is_suspended();
|
||||
}
|
||||
return suspended;
|
||||
}
|
||||
|
||||
// If (thread == null) then return current thread object.
|
||||
// Otherwise return JNIHandles::resolve_external_guard(thread).
|
||||
oop
|
||||
|
@ -221,6 +221,9 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
||||
static bool is_cthread_with_mounted_vthread(JavaThread* jt);
|
||||
static bool is_cthread_with_continuation(JavaThread* jt);
|
||||
|
||||
// Check if VirtualThread or BoundVirtualThread is suspended.
|
||||
static bool is_vthread_suspended(oop vt_oop, JavaThread* jt);
|
||||
|
||||
static JvmtiEnv* JvmtiEnv_from_jvmti_env(jvmtiEnv *env) {
|
||||
return (JvmtiEnv*)((intptr_t)env - in_bytes(jvmti_external_offset()));
|
||||
};
|
||||
|
@ -1100,9 +1100,12 @@ void JavaThread::install_async_exception(AsyncExceptionHandshake* aeh) {
|
||||
// for AbortVMOnException flag
|
||||
Exceptions::debug_check_abort(exception->klass()->external_name());
|
||||
|
||||
// Interrupt thread so it will wake up from a potential wait()/sleep()/park()
|
||||
java_lang_Thread::set_interrupted(threadObj(), true);
|
||||
this->interrupt();
|
||||
oop vt_oop = vthread();
|
||||
if (vt_oop == nullptr || !vt_oop->is_a(vmClasses::BaseVirtualThread_klass())) {
|
||||
// Interrupt thread so it will wake up from a potential wait()/sleep()/park()
|
||||
java_lang_Thread::set_interrupted(threadObj(), true);
|
||||
this->interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
class InstallAsyncExceptionHandshake : public HandshakeClosure {
|
||||
|
@ -79,6 +79,7 @@ vmTestbase/nsk/jdb/where/where005/where005.java 8278470 generic-all
|
||||
|
||||
vmTestbase/nsk/jdb/list/list003/list003.java 8300707 generic-all
|
||||
vmTestbase/nsk/jdb/repeat/repeat001/repeat001.java 8300707 generic-all
|
||||
vmTestbase/nsk/jdb/kill/kill001/kill001.java 8306467 generic-all
|
||||
|
||||
####
|
||||
## NSK JDI tests failing with wrapper
|
||||
|
@ -115,10 +115,6 @@ test_unsupported_jvmti_functions(jvmtiEnv *jvmti, JNIEnv *jni, jthread vthread,
|
||||
fatal(jni, "Virtual threads are not supported");
|
||||
}
|
||||
|
||||
LOG("Testing StopThread\n");
|
||||
err = jvmti->StopThread(vthread, vthread);
|
||||
check_jvmti_error_unsupported_operation(jni, "StopThread", err);
|
||||
|
||||
LOG("Testing PopFrame\n");
|
||||
err = jvmti->PopFrame(vthread);
|
||||
check_jvmti_error_opaque_frame(jni, "PopFrame", err);
|
||||
|
@ -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 StopThread support for virtual threads.
|
||||
* @requires vm.continuations
|
||||
* @run main/othervm/native -agentlib:StopThreadTest StopThreadTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=no-vmcontinuations
|
||||
* @summary Verifies JVMTI StopThread support for bound virtual threads.
|
||||
* @run main/othervm/native -agentlib:StopThreadTest -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations StopThreadTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=platform
|
||||
* @summary Verifies JVMTI StopThread support for platform threads.
|
||||
* @run main/othervm/native -agentlib:StopThreadTest StopThreadTest platform
|
||||
*/
|
||||
|
||||
import java.lang.AssertionError;
|
||||
|
||||
/*
|
||||
* The test exercises the JVMTI function: StopThread(jthread).
|
||||
* 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 send AssertionError exception to its own thread
|
||||
* All cases are using JVMTI StopThread to send an AssertionError object.
|
||||
*/
|
||||
public class StopThreadTest {
|
||||
private static final String agentLib = "StopThreadTest";
|
||||
static final int JVMTI_ERROR_NONE = 0;
|
||||
static final int THREAD_NOT_SUSPENDED = 13;
|
||||
static final int PASSED = 0;
|
||||
static final int FAILED = 2;
|
||||
|
||||
static void log(String str) { System.out.println(str); }
|
||||
|
||||
static native void prepareAgent(Class taskClass, Object exceptionObject);
|
||||
static native void suspendThread(Thread thread);
|
||||
static native void resumeThread(Thread thread);
|
||||
static native void ensureAtBreakpoint();
|
||||
static native void notifyAtBreakpoint();
|
||||
static native int stopThread(Thread thread);
|
||||
|
||||
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("StopThreadTest failed!");
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
is_virtual = !(args.length > 0 && args[0].equals("platform"));
|
||||
run();
|
||||
if (status == FAILED) {
|
||||
throwFailed("StopThreadTest!");
|
||||
}
|
||||
log("\nStopThreadTest passed");
|
||||
}
|
||||
|
||||
public static void run() {
|
||||
TestTask testTask = new TestTask();
|
||||
Thread testTaskThread = null;
|
||||
AssertionError excObject = new AssertionError();
|
||||
int retCode;
|
||||
|
||||
prepareAgent(TestTask.class, excObject);
|
||||
|
||||
log("\nMain #A: method A() must be blocked on entering a synchronized statement");
|
||||
synchronized (TestTask.lock) {
|
||||
if (is_virtual) {
|
||||
testTaskThread = Thread.ofVirtual().name("TestTaskThread").start(testTask);
|
||||
} else {
|
||||
testTaskThread = Thread.ofPlatform().name("TestTaskThread").start(testTask);
|
||||
}
|
||||
testTask.ensureStarted();
|
||||
|
||||
if (is_virtual) { // this check is for virtual target thread only
|
||||
log("\nMain #A.1: unsuspended");
|
||||
retCode = stopThread(testTaskThread);
|
||||
if (retCode != THREAD_NOT_SUSPENDED) {
|
||||
throwFailed("Main #A.1: expected THREAD_NOT_SUSPENDED instead of: " + retCode);
|
||||
} else {
|
||||
log("Main #A.1: got expected THREAD_NOT_SUSPENDED");
|
||||
}
|
||||
}
|
||||
|
||||
log("\nMain #A.2: suspended");
|
||||
suspendThread(testTaskThread);
|
||||
retCode = stopThread(testTaskThread);
|
||||
if (retCode != JVMTI_ERROR_NONE) {
|
||||
throwFailed("Main #A.2: expected JVMTI_ERROR_NONE instead of: " + retCode);
|
||||
} else {
|
||||
log("Main #A.2: got expected JVMTI_ERROR_NONE");
|
||||
}
|
||||
resumeThread(testTaskThread);
|
||||
}
|
||||
log("\nMain #B: method B() must be blocked in a breakpoint event handler");
|
||||
{
|
||||
ensureAtBreakpoint();
|
||||
|
||||
if (is_virtual) { // this check is for virtual target thread only
|
||||
log("\nMain #B.1: unsuspended");
|
||||
retCode = stopThread(testTaskThread);
|
||||
if (retCode != THREAD_NOT_SUSPENDED) {
|
||||
throwFailed("Main #B.1: expected THREAD_NOT_SUSPENDED instead of: " + retCode);
|
||||
}
|
||||
}
|
||||
|
||||
log("\nMain #B.2: suspended");
|
||||
suspendThread(testTaskThread);
|
||||
retCode = stopThread(testTaskThread);
|
||||
if (retCode != JVMTI_ERROR_NONE) {
|
||||
throwFailed("Main #B.2: expected JVMTI_ERROR_NONE");
|
||||
}
|
||||
resumeThread(testTaskThread);
|
||||
|
||||
notifyAtBreakpoint();
|
||||
}
|
||||
|
||||
log("\nMain #C: method C() sends AssertionError object to its own thread");
|
||||
{
|
||||
// StopThread 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 Object lock = new Object();
|
||||
static void log(String str) { System.out.println(str); }
|
||||
|
||||
private volatile boolean started = false;
|
||||
private volatile boolean finished = false;
|
||||
|
||||
static public 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.
|
||||
public void ensureStarted() {
|
||||
while (!started) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure thread is finished.
|
||||
public void ensureFinished() {
|
||||
while (!finished) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
log("TestTask.run: started");
|
||||
started = true;
|
||||
|
||||
boolean seenExceptionFromA = false;
|
||||
try {
|
||||
A();
|
||||
} catch (AssertionError ex) {
|
||||
log("TestTask.run: caught expected AssertionError from method A()");
|
||||
seenExceptionFromA = true;
|
||||
if (!Thread.currentThread().isVirtual()) { // platform thread
|
||||
// clear the interrupt status
|
||||
Thread.interrupted();
|
||||
}
|
||||
}
|
||||
if (!seenExceptionFromA) {
|
||||
StopThreadTest.setFailed("TestTask.run: expected AssertionError from method A()");
|
||||
}
|
||||
sleep(1); // to cause yield
|
||||
|
||||
boolean seenExceptionFromB = false;
|
||||
try {
|
||||
B();
|
||||
} catch (AssertionError ex) {
|
||||
log("TestTask.run: caught expected AssertionError from method B()");
|
||||
seenExceptionFromB = true;
|
||||
if (!Thread.currentThread().isVirtual()) { // platform thread
|
||||
// clear the interrupt status
|
||||
Thread.interrupted();
|
||||
}
|
||||
}
|
||||
if (!seenExceptionFromB) {
|
||||
StopThreadTest.setFailed("TestTask.run: expected AssertionError from method B()");
|
||||
}
|
||||
sleep(1); // to cause yield
|
||||
|
||||
boolean seenExceptionFromC = false;
|
||||
try {
|
||||
C();
|
||||
} catch (AssertionError ex) {
|
||||
log("TestTask.run: caught expected AssertionError from method C()");
|
||||
seenExceptionFromC = true;
|
||||
}
|
||||
if (!seenExceptionFromC) {
|
||||
StopThreadTest.setFailed("TestTask.run: expected AssertionError from method C()");
|
||||
}
|
||||
finished = true;
|
||||
}
|
||||
|
||||
// Method is blocked on entering a synchronized statement.
|
||||
// StopThread is used to send an AssertionError object two times:
|
||||
// - when not suspended: THREAD_NOT_SUSPENDED is expected
|
||||
// - when suspended: JVMTI_ERROR_NONE is expected
|
||||
static void A() {
|
||||
log("TestTask.A: started");
|
||||
synchronized (lock) {
|
||||
}
|
||||
log("TestTask.A: finished");
|
||||
}
|
||||
|
||||
// A breakpoint is set at start of this method.
|
||||
// StopThread is used to send an AssertionError object two times:
|
||||
// - when not suspended: THREAD_NOT_SUSPENDED is expected
|
||||
// - when suspended: expected to succeed
|
||||
static void B() {
|
||||
log("TestTask.B: started");
|
||||
}
|
||||
|
||||
// This method uses StopThread to send an AssertionError object to
|
||||
// its own thread. It is expected to succeed.
|
||||
static void C() {
|
||||
log("TestTask.C: started");
|
||||
StopThreadTest.stopThread(Thread.currentThread());
|
||||
log("TestTask.C: finished");
|
||||
}
|
||||
}
|
||||
}
|
195
test/hotspot/jtreg/serviceability/jvmti/vthread/StopThreadTest/libStopThreadTest.cpp
Normal file
195
test/hotspot/jtreg/serviceability/jvmti/vthread/StopThreadTest/libStopThreadTest.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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 jobject exception_obj = 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()");
|
||||
}
|
||||
err = jvmti->ClearBreakpoint(mid_B, 0);
|
||||
check_jvmti_status(jni, err, "Breakpoint: Failed in JVMTI ClearBreakpoint");
|
||||
|
||||
LOG("Breakpoint: In method TestTask.B(): before sync section enter\n");
|
||||
|
||||
err = jvmti->RawMonitorEnter(monitor);
|
||||
check_jvmti_status(jni, err, "Breakpoint: Failed in RawMonitorEnter");
|
||||
|
||||
bp_sync_reached = true;
|
||||
|
||||
// wait for notify from notifyAtBreakpoint or JVMTI_ERROR_INTERRUPT from JVMTI StopThread
|
||||
err = jvmti->RawMonitorWait(monitor, 0);
|
||||
if (err == JVMTI_ERROR_INTERRUPT) {
|
||||
LOG("Breakpoint: In method TestTask.B(): expected JVMTI_ERROR_INTERRUPT from RawMonitorWait\n");
|
||||
} else {
|
||||
check_jvmti_status(jni, err, "Breakpoint: Failed in RawMonitorWait");
|
||||
}
|
||||
err = jvmti->RawMonitorExit(monitor);
|
||||
check_jvmti_status(jni, err, "Breakpoint: Failed in RawMonitorExit");
|
||||
|
||||
LOG("Breakpoint: In method TestTask.B(): after sync section exit\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_StopThreadTest_prepareAgent(JNIEnv *jni, jclass cls, jclass task_clazz, jobject exc_obj) {
|
||||
jvmtiError err;
|
||||
|
||||
LOG("Main: prepareAgent started\n");
|
||||
|
||||
if (jvmti == NULL) {
|
||||
fatal(jni, "prepareAgent: Failed as JVMTI client was not properly loaded!\n");
|
||||
}
|
||||
exception_obj = jni->NewGlobalRef(exc_obj);
|
||||
if (exception_obj == NULL) {
|
||||
fatal(jni, "prepareAgent: Failed in JNI NewGlobalRef\n");
|
||||
}
|
||||
mid_B = jni->GetStaticMethodID(task_clazz, "B", "()V");
|
||||
if (mid_B == NULL) {
|
||||
fatal(jni, "prepareAgent: Failed to find Method ID for method: TestTask.B()\n");
|
||||
}
|
||||
err = jvmti->SetBreakpoint(mid_B, 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_StopThreadTest_suspendThread(JNIEnv *jni, jclass cls, jthread thread) {
|
||||
LOG("Main: suspendThread\n");
|
||||
suspend_thread(jvmti, jni, thread);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_StopThreadTest_resumeThread(JNIEnv *jni, jclass cls, jthread thread) {
|
||||
LOG("Main: resumeThread\n");
|
||||
resume_thread(jvmti, jni, thread);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_StopThreadTest_stopThread(JNIEnv *jni, jclass cls, jthread thread) {
|
||||
jvmtiError err = jvmti->StopThread(thread, exception_obj);
|
||||
LOG("Main: stopThread: StopThread returned code: %s (%d)\n", TranslateError(err), err);
|
||||
return (jint)err;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_StopThreadTest_ensureAtBreakpoint(JNIEnv *jni, jclass cls) {
|
||||
jvmtiError err;
|
||||
bool need_stop = false;
|
||||
|
||||
LOG("Main: ensureAtBreakpoint\n");
|
||||
|
||||
while (!need_stop) {
|
||||
err = jvmti->RawMonitorEnter(monitor);
|
||||
check_jvmti_status(jni, err, "ensureAtBreakpoint: Failed in RawMonitorEnter");
|
||||
|
||||
need_stop = bp_sync_reached;
|
||||
|
||||
err = jvmti->RawMonitorExit(monitor);
|
||||
check_jvmti_status(jni, err, "ensureAtBreakpoint: Failed in RawMonitorExit");
|
||||
|
||||
sleep_ms(1); // 1 millisecond
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_StopThreadTest_notifyAtBreakpoint(JNIEnv *jni, jclass cls) {
|
||||
jvmtiError err;
|
||||
|
||||
LOG("Main: notifyAtBreakpoint\n");
|
||||
|
||||
err = jvmti->RawMonitorEnter(monitor);
|
||||
check_jvmti_status(jni, err, "notifyAtBreakpoint: Fatal Error in RawMonitorEnter");
|
||||
|
||||
err = jvmti->RawMonitorNotify(monitor);
|
||||
check_jvmti_status(jni, err, "notifyAtBreakpoint: Fatal Error in RawMonitorNotify");
|
||||
|
||||
err = jvmti->RawMonitorExit(monitor);
|
||||
check_jvmti_status(jni, err, "notifyAtBreakpoint: Fatal Error in RawMonitorExit");
|
||||
}
|
||||
|
||||
} // extern "C"
|
3
test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadUnsupportedTest/VThreadUnsupportedTest.java
3
test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadUnsupportedTest/VThreadUnsupportedTest.java
@ -23,7 +23,8 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Verifies that specific JVMTI functions returns JVMTI_ERROR_INVALID_THREAD if called with virtual threads.
|
||||
* @summary Verifies that specific JVMTI functions return UNSUPPORTED_OPERATION
|
||||
* or OPAQUE_FRAME if called with virtual threads.
|
||||
* @requires vm.continuations
|
||||
* @compile VThreadUnsupportedTest.java
|
||||
* @run main/othervm/native -agentlib:VThreadUnsupportedTest VThreadUnsupportedTest
|
||||
|
10
test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadUnsupportedTest/libVThreadUnsupportedTest.cpp
10
test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadUnsupportedTest/libVThreadUnsupportedTest.cpp
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -60,8 +60,8 @@ Java_VThreadUnsupportedTest_isCompletedTestInEvent(JNIEnv *env, jobject obj) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute JVMTI functions which currently don't support vthreads and check that they
|
||||
* return error code JVMTI_ERROR_INVALID_THREAD or JVMTI_ERROR_OPAQUE_FRAME correctly.
|
||||
* Execute JVMTI functions that don't support vthreads and check they return error
|
||||
* code JVMTI_ERROR_UNSUPPORTED_OPERATION or JVMTI_ERROR_OPAQUE_FRAME correctly.
|
||||
*/
|
||||
static void
|
||||
test_unsupported_jvmti_functions(jvmtiEnv *jvmti, JNIEnv *jni, jthread vthread) {
|
||||
@ -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 StopThread\n");
|
||||
err = jvmti->StopThread(vthread, vthread);
|
||||
check_jvmti_error_unsupported_operation(jni, "StopThread", err);
|
||||
|
||||
LOG("Testing PopFrame\n");
|
||||
err = jvmti->PopFrame(vthread);
|
||||
check_jvmti_error_opaque_frame(jni, "PopFrame", err);
|
||||
|
Loading…
x
Reference in New Issue
Block a user