8295976: GetThreadListStackTraces returns wrong state for blocked VirtualThread
Reviewed-by: cjplummer, amenkov
This commit is contained in:
parent
fadcd65018
commit
a25b7b8b55
src/hotspot/share/prims
test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest
@ -1780,9 +1780,9 @@ JvmtiEnv::GetAllStackTraces(jint max_frame_count, jvmtiStackInfo** stack_info_pt
|
||||
jvmtiError
|
||||
JvmtiEnv::GetThreadListStackTraces(jint thread_count, const jthread* thread_list, jint max_frame_count, jvmtiStackInfo** stack_info_ptr) {
|
||||
jvmtiError err = JVMTI_ERROR_NONE;
|
||||
JvmtiVTMSTransitionDisabler disabler;
|
||||
|
||||
if (thread_count == 1) {
|
||||
JvmtiVTMSTransitionDisabler disabler;
|
||||
|
||||
// Use direct handshake if we need to get only one stack trace.
|
||||
JavaThread *current_thread = JavaThread::current();
|
||||
|
@ -1344,13 +1344,15 @@ JvmtiEnvBase::current_thread_obj_or_resolve_external_guard(jthread thread) {
|
||||
}
|
||||
|
||||
jvmtiError
|
||||
JvmtiEnvBase::get_threadOop_and_JavaThread(ThreadsList* t_list, jthread thread,
|
||||
JvmtiEnvBase::get_threadOop_and_JavaThread(ThreadsList* t_list, jthread thread, JavaThread* cur_thread,
|
||||
JavaThread** jt_pp, oop* thread_oop_p) {
|
||||
JavaThread* cur_thread = JavaThread::current();
|
||||
JavaThread* java_thread = nullptr;
|
||||
oop thread_oop = nullptr;
|
||||
|
||||
if (thread == nullptr) {
|
||||
if (cur_thread == nullptr) { // cur_thread can be null when called from a VM_op
|
||||
return JVMTI_ERROR_INVALID_THREAD;
|
||||
}
|
||||
java_thread = cur_thread;
|
||||
thread_oop = get_vthread_or_thread_oop(java_thread);
|
||||
if (thread_oop == nullptr || !thread_oop->is_a(vmClasses::Thread_klass())) {
|
||||
@ -1381,6 +1383,14 @@ JvmtiEnvBase::get_threadOop_and_JavaThread(ThreadsList* t_list, jthread thread,
|
||||
return JVMTI_ERROR_NONE;
|
||||
}
|
||||
|
||||
jvmtiError
|
||||
JvmtiEnvBase::get_threadOop_and_JavaThread(ThreadsList* t_list, jthread thread,
|
||||
JavaThread** jt_pp, oop* thread_oop_p) {
|
||||
JavaThread* cur_thread = JavaThread::current();
|
||||
jvmtiError err = get_threadOop_and_JavaThread(t_list, thread, cur_thread, jt_pp, thread_oop_p);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Check for JVMTI_ERROR_NOT_SUSPENDED and JVMTI_ERROR_OPAQUE_FRAME errors.
|
||||
// Used in PopFrame and ForceEarlyReturn implementations.
|
||||
jvmtiError
|
||||
@ -1931,13 +1941,15 @@ VM_GetThreadListStackTraces::doit() {
|
||||
jthread jt = _thread_list[i];
|
||||
JavaThread* java_thread = nullptr;
|
||||
oop thread_oop = nullptr;
|
||||
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), jt, &java_thread, &thread_oop);
|
||||
jvmtiError err = JvmtiEnvBase::get_threadOop_and_JavaThread(tlh.list(), jt, nullptr, &java_thread, &thread_oop);
|
||||
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
// We got an error code so we don't have a JavaThread *, but
|
||||
// only return an error from here if we didn't get a valid
|
||||
// thread_oop.
|
||||
// In the virtual thread case the cv_external_thread_to_JavaThread is expected to correctly set
|
||||
// the thread_oop and return JVMTI_ERROR_INVALID_THREAD which we ignore here.
|
||||
// In the virtual thread case the get_threadOop_and_JavaThread is expected to correctly set
|
||||
// the thread_oop and return JVMTI_ERROR_THREAD_NOT_ALIVE which we ignore here.
|
||||
// The corresponding thread state will be recorded in the jvmtiStackInfo.state.
|
||||
if (thread_oop == nullptr) {
|
||||
_collector.set_result(err);
|
||||
return;
|
||||
@ -1952,7 +1964,7 @@ VM_GetThreadListStackTraces::doit() {
|
||||
void
|
||||
GetSingleStackTraceClosure::do_thread(Thread *target) {
|
||||
JavaThread *jt = JavaThread::cast(target);
|
||||
oop thread_oop = jt->threadObj();
|
||||
oop thread_oop = JNIHandles::resolve_external_guard(_jthread);
|
||||
|
||||
if (!jt->is_exiting() && thread_oop != nullptr) {
|
||||
ResourceMark rm;
|
||||
|
@ -214,6 +214,8 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
||||
return result;
|
||||
}
|
||||
|
||||
static jvmtiError get_threadOop_and_JavaThread(ThreadsList* t_list, jthread thread, JavaThread* cur_thread,
|
||||
JavaThread** jt_pp, oop* thread_oop_p);
|
||||
static jvmtiError get_threadOop_and_JavaThread(ThreadsList* t_list, jthread thread,
|
||||
JavaThread** jt_pp, oop* thread_oop_p);
|
||||
|
||||
|
135
test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.java
Normal file
135
test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.java
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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
|
||||
* @bug 8295976
|
||||
* @summary GetThreadListStackTraces returns wrong state for blocked VirtualThread
|
||||
* @requires vm.continuations
|
||||
* @run main/othervm/native -agentlib:ThreadListStackTracesTest ThreadListStackTracesTest
|
||||
*/
|
||||
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
abstract class TestTask implements Runnable {
|
||||
volatile boolean threadReady = false;
|
||||
|
||||
static void log(String msg) { System.out.println(msg); }
|
||||
|
||||
static void sleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Interruption in TestTask.sleep: \n\t" + e);
|
||||
}
|
||||
}
|
||||
|
||||
public void ensureReady(Thread vt, Thread.State expState) {
|
||||
// wait while the thread is not ready or thread state is unexpected
|
||||
while (!threadReady || (vt.getState() != expState)) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void run();
|
||||
}
|
||||
|
||||
class ReentrantLockTestTask extends TestTask {
|
||||
public void run() {
|
||||
log("grabbing reentrantLock");
|
||||
threadReady = true;
|
||||
ThreadListStackTracesTest.reentrantLock.lock();
|
||||
log("grabbed reentrantLock");
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectMonitorTestTask extends TestTask {
|
||||
public void run() {
|
||||
log("entering synchronized statement");
|
||||
threadReady = true;
|
||||
synchronized (ThreadListStackTracesTest.objectMonitor) {
|
||||
log("entered synchronized statement");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ThreadListStackTracesTest {
|
||||
static final int JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400;
|
||||
static final int JVMTI_THREAD_STATE_WAITING = 0x0080;
|
||||
|
||||
static final ReentrantLock reentrantLock = new ReentrantLock();
|
||||
static final Object objectMonitor = new Object();
|
||||
|
||||
private static native int getStateSingle(Thread thread);
|
||||
private static native int getStateMultiple(Thread thread, Thread other);
|
||||
|
||||
static void log(String msg) { System.out.println(msg); }
|
||||
static void failed(String msg) { throw new RuntimeException(msg); }
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
checkReentrantLock();
|
||||
checkSynchronized();
|
||||
}
|
||||
|
||||
private static void checkReentrantLock() throws InterruptedException {
|
||||
final Thread.State expState = Thread.State.WAITING;
|
||||
reentrantLock.lock();
|
||||
String name = "ReentrantLockTestTask";
|
||||
TestTask task = new ReentrantLockTestTask();
|
||||
Thread vt = Thread.ofVirtual().name(name).start(task);
|
||||
task.ensureReady(vt, expState);
|
||||
checkStates(vt, expState);
|
||||
}
|
||||
|
||||
private static void checkSynchronized() throws InterruptedException {
|
||||
final Thread.State expState = Thread.State.BLOCKED;
|
||||
synchronized (objectMonitor) {
|
||||
String name = "ObjectMonitorTestTask";
|
||||
TestTask task = new ObjectMonitorTestTask();
|
||||
Thread vt = Thread.ofVirtual().name(name).start(task);
|
||||
task.ensureReady(vt, expState);
|
||||
checkStates(vt, expState);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkStates(Thread vt, Thread.State expState) {
|
||||
int singleState = getStateSingle(vt);
|
||||
int multiState = getStateMultiple(vt, Thread.currentThread());
|
||||
int jvmtiExpState = (expState == Thread.State.WAITING) ?
|
||||
JVMTI_THREAD_STATE_WAITING :
|
||||
JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER;
|
||||
|
||||
System.out.printf("State: expected: %s single: %x multi: %x\n",
|
||||
vt.getState(), singleState, multiState);
|
||||
|
||||
if (vt.getState() != expState) {
|
||||
failed("Java thread state is wrong");
|
||||
}
|
||||
if ((singleState & jvmtiExpState) == 0) {
|
||||
failed("JVMTI single thread state is wrong");
|
||||
}
|
||||
if ((multiState & jvmtiExpState) == 0) {
|
||||
failed("JVMTI multi thread state is wrong");
|
||||
}
|
||||
}
|
||||
}
|
64
test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/libThreadListStackTracesTest.cpp
Normal file
64
test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/libThreadListStackTracesTest.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 <jni.h>
|
||||
#include <jvmti.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "jvmti_common.h"
|
||||
|
||||
static jvmtiEnv* jvmti = nullptr;
|
||||
static const jint MAX_FRAME_COUNT = 32;
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ThreadListStackTracesTest_getStateSingle(JNIEnv* jni, jclass clazz, jthread vthread) {
|
||||
jvmtiStackInfo* info = NULL;
|
||||
|
||||
jvmtiError err = jvmti->GetThreadListStackTraces(1, &vthread, MAX_FRAME_COUNT, &info);
|
||||
check_jvmti_status(jni, err, "getStateSingle: error in JVMTI GetThreadListStackTraces");
|
||||
|
||||
return info[0].state;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ThreadListStackTracesTest_getStateMultiple(JNIEnv* jni, jclass clazz, jthread vhread, jthread other) {
|
||||
jthread threads[2] = { vhread, other };
|
||||
jvmtiStackInfo* info = NULL;
|
||||
|
||||
jvmtiError err = jvmti->GetThreadListStackTraces(2, threads, MAX_FRAME_COUNT, &info);
|
||||
check_jvmti_status(jni, err, "getStateMultiple: error in JVMTI GetThreadListStackTraces");
|
||||
|
||||
return info[0].state;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
|
||||
if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
|
||||
LOG("Agent_OnLoad: error in GetEnv");
|
||||
return JNI_ERR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // extern "C"
|
Loading…
x
Reference in New Issue
Block a user