8256314: JVM TI GetCurrentContendedMonitor is implemented incorrectly

Reviewed-by: dholmes, lmesnik
This commit is contained in:
Serguei Spitsyn 2024-03-08 19:46:23 +00:00
parent 87b40c6ad2
commit 33aa4b26b1
10 changed files with 58 additions and 32 deletions

View File

@ -868,12 +868,7 @@ JvmtiEnv::GetThreadState(jthread thread, jint* thread_state_ptr) {
// is a virtual thread.
return err;
}
if (java_lang_VirtualThread::is_instance(thread_oop)) {
*thread_state_ptr = JvmtiEnvBase::get_vthread_state(thread_oop, java_thread);
} else {
*thread_state_ptr = JvmtiEnvBase::get_thread_state(thread_oop, java_thread);
}
*thread_state_ptr = JvmtiEnvBase::get_thread_or_vthread_state(thread_oop, java_thread);
return JVMTI_ERROR_NONE;
} /* end GetThreadState */

View File

@ -813,6 +813,17 @@ JvmtiEnvBase::get_vthread_state(oop thread_oop, JavaThread* java_thread) {
return state;
}
jint
JvmtiEnvBase::get_thread_or_vthread_state(oop thread_oop, JavaThread* java_thread) {
jint state = 0;
if (java_lang_VirtualThread::is_instance(thread_oop)) {
state = JvmtiEnvBase::get_vthread_state(thread_oop, java_thread);
} else {
state = JvmtiEnvBase::get_thread_state(thread_oop, java_thread);
}
return state;
}
jvmtiError
JvmtiEnvBase::get_live_threads(JavaThread* current_thread, Handle group_hdl, jint *count_ptr, Handle **thread_objs_p) {
jint count = 0;
@ -933,11 +944,16 @@ JvmtiEnvBase::get_current_contended_monitor(JavaThread *calling_thread, JavaThre
obj = mon->object();
assert(obj != nullptr, "ObjectMonitor should have a valid object!");
}
// implied else: no contended ObjectMonitor
} else {
// thread is doing an Object.wait() call
obj = mon->object();
assert(obj != nullptr, "Object.wait() should have an object");
oop thread_oop = get_vthread_or_thread_oop(java_thread);
jint state = get_thread_or_vthread_state(thread_oop, java_thread);
if (state & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) {
// thread is re-entering the monitor in an Object.wait() call
obj = mon->object();
assert(obj != nullptr, "Object.wait() should have an object");
}
}
if (obj == nullptr) {

View File

@ -388,9 +388,12 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
static jint get_thread_state_base(oop thread_oop, JavaThread* jt);
static jint get_thread_state(oop thread_oop, JavaThread* jt);
// get virtual thread thread state
// get virtual thread state
static jint get_vthread_state(oop thread_oop, JavaThread* jt);
// get platform or virtual thread state
static jint get_thread_or_vthread_state(oop thread_oop, JavaThread* jt);
// enumerates the live threads in the given thread group
static jvmtiError get_live_threads(JavaThread* current_thread, Handle group_hdl, jint *count_ptr, Handle **thread_objs_p);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2024, 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
@ -1980,9 +1980,9 @@ JDWP "Java(tm) Debug Wire Protocol"
)
(Command CurrentContendedMonitor=9
"Returns the object, if any, for which this thread is waiting. The "
"thread may be waiting to enter a monitor, or it may be waiting, via "
"the java.lang.Object.wait method, for another thread to invoke the "
"notify method. "
"thread may be waiting to enter the object's monitor, or in "
"java.lang.Object.wait waiting to re-enter the monitor after being "
"notified, interrupted, or timed-out."
"The thread must be suspended, and the returned information is "
"relevant only while the thread is suspended. "
"Requires canGetCurrentContendedMonitor capability - see "

View File

@ -310,8 +310,8 @@ public interface ThreadReference extends ObjectReference {
* for which this thread is currently waiting.
* The thread can be waiting for a monitor through entry into a
* synchronized method, the synchronized statement, or
* {@link Object#wait}. The {@link #status} method can be used
* to differentiate between the first two cases and the third.
* {@link Object#wait} waiting to re-enter the monitor
* after being notified, interrupted, or timed-out.
* <p>
* Not all target virtual machines support this operation.
* Use {@link VirtualMachine#canGetCurrentContendedMonitor()}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2024, 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
@ -154,6 +154,7 @@ public class waitingthreads004 {
fieldName = "lockingObject";
display("CHECK2: checking waitingThreads method for ObjectReference of waitingthreads004a." + fieldName);
objRef = (ObjectReference) debuggeeClass.getValue(debuggeeClass.fieldByName(fieldName));
try {
List waitingThreads = objRef.waitingThreads();
if (waitingThreads.size() != waitingthreads004a.threadCount) {
@ -161,6 +162,12 @@ public class waitingthreads004 {
complain("waitingThreads method returned list with unexpected size for " + fieldName +
"\n\t expected value : " + waitingthreads004a.threadCount + "; got one : " + waitingThreads.size());
} else {
debuggee.VM().resume();
debuggee.sendSignal(SIGNAL_GO);
debuggee.receiveExpectedSignal(SIGNAL_GO);
// tested thread must be blocked on re-entering monitor in lockingObject.wait()
debuggee.VM().suspend();
// check waitingThreads list
Iterator itr = waitingThreads.iterator();
while (itr.hasNext()) {

View File

@ -32,11 +32,12 @@
* The test checks an following assertion of
* com.sun.jdi.ObjectReference.waitingThreads method spec:
* Returns a List containing a ThreadReference for each thread currently
* waiting for this object's monitor.
* waiting to re-enter this object's monitor.
* There are two test cases:
* - An object with no waiting threads.
* A list with zero size is expected to be returned by the method.
* - An object with threads waiting in Object.wait(long) method.
* - An object with threads waiting to re-enter the monitor after being
* notified during execution of the Object.wait(long) method.
* The debugger checks with expected results:
* - a size of returned list of ThreadReferences,
* - the names of thread references,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2024, 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
@ -96,13 +96,17 @@ public class waitingthreads004a {
}
pipe.println(waitingthreads004.SIGNAL_GO);
receiveSignal(waitingthreads004.SIGNAL_QUIT);
receiveSignal(waitingthreads004.SIGNAL_GO);
}
display("exited: synchronized (waitnotifyObj) {}");
synchronized (lockingObject) {
display("entered and notifyAll: synchronized (lockingObject) {}");
lockingObject.notifyAll();
// tested thread must be blocked on re-entering monitor in lockingObject.wait()
pipe.println(waitingthreads004.SIGNAL_GO);
receiveSignal(waitingthreads004.SIGNAL_QUIT);
}
display("exited: synchronized (lockingObject) {}");

View File

@ -46,7 +46,8 @@
* Next, debugger obtains from debuggee classID for tested thread class
* and threadID as the value of a class static field. Also debugger
* suspends the thread before sending the tested command. The tested
* thread is waiting for the object at this moment.
* thread is waiting to re-enter the object monitor after being
* notified during execution of the Object.wait(long) method.
* Then, debugger creates command packet for ThreadReference.CurrenContendedMonitor
* command with the found threadID as an argument, writes packet to
* the transport channel, and waits for a reply packet.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2024, 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
@ -61,6 +61,7 @@ public class curcontmonitor001a {
argumentHandler = new ArgumentHandler(args);
log = new Log(out, argumentHandler);
long timeout = argumentHandler.getWaitTime() * 60 * 1000; // milliseconds
String signal = null;
// make communication pipe to debugger
log.display("Creating pipe");
@ -84,21 +85,19 @@ public class curcontmonitor001a {
// ensure that tested thread is waiting for monitor object
synchronized (TestedClass.thread.monitor) {
TestedClass.thread.monitor.notifyAll();
// send debugger signal READY
log.display("Sending signal to debugger: " + curcontmonitor001.READY);
pipe.println(curcontmonitor001.READY);
// wait for signal QUIT from debugeer
log.display("Waiting for signal from debugger: " + curcontmonitor001.QUIT);
signal = pipe.readln();
log.display("Received signal from debugger: " + signal);
}
}
// wait for signal QUIT from debugeer
log.display("Waiting for signal from debugger: " + curcontmonitor001.QUIT);
String signal = pipe.readln();
log.display("Received signal from debugger: " + signal);
// interrupt waiting thread
log.display("Interrupting tested thread being waited");
TestedClass.thread.interrupt();
// check received signal
if (signal == null || !signal.equals(curcontmonitor001.QUIT)) {
log.complain("Unexpected communication signal from debugee: " + signal