8247972: incorrect implementation of JVM TI GetObjectMonitorUsage
Reviewed-by: dcubed, lmesnik
This commit is contained in:
parent
139681a7eb
commit
b92440f9b1
@ -1495,20 +1495,39 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
|
||||
jint nWant = 0, nWait = 0;
|
||||
markWord mark = hobj->mark();
|
||||
ResourceMark rm(current_thread);
|
||||
GrowableArray<JavaThread*>* wantList = nullptr;
|
||||
|
||||
if (mark.has_monitor()) {
|
||||
mon = mark.monitor();
|
||||
assert(mon != nullptr, "must have monitor");
|
||||
// this object has a heavyweight monitor
|
||||
nWant = mon->contentions(); // # of threads contending for monitor
|
||||
nWait = mon->waiters(); // # of threads in Object.wait()
|
||||
ret.waiter_count = nWant + nWait;
|
||||
ret.notify_waiter_count = nWait;
|
||||
nWant = mon->contentions(); // # of threads contending for monitor entry, but not re-entry
|
||||
nWait = mon->waiters(); // # of threads waiting for notification,
|
||||
// or to re-enter monitor, in Object.wait()
|
||||
|
||||
// Get the actual set of threads trying to enter, or re-enter, the monitor.
|
||||
wantList = Threads::get_pending_threads(tlh.list(), nWant + nWait, (address)mon);
|
||||
nWant = wantList->length();
|
||||
} else {
|
||||
// this object has a lightweight monitor
|
||||
ret.waiter_count = 0;
|
||||
ret.notify_waiter_count = 0;
|
||||
}
|
||||
|
||||
if (mon != nullptr) {
|
||||
// Robustness: the actual waiting list can be smaller.
|
||||
// The nWait count we got from the mon->waiters() may include the re-entering
|
||||
// the monitor threads after being notified. Here we are correcting the actual
|
||||
// number of the waiting threads by excluding those re-entering the monitor.
|
||||
nWait = 0;
|
||||
for (ObjectWaiter* waiter = mon->first_waiter();
|
||||
waiter != nullptr && (nWait == 0 || waiter != mon->first_waiter());
|
||||
waiter = mon->next_waiter(waiter)) {
|
||||
nWait++;
|
||||
}
|
||||
}
|
||||
ret.waiter_count = nWant;
|
||||
ret.notify_waiter_count = nWait;
|
||||
|
||||
// Allocate memory for heavyweight and lightweight monitor.
|
||||
jvmtiError err;
|
||||
err = allocate(ret.waiter_count * sizeof(jthread *), (unsigned char**)&ret.waiters);
|
||||
@ -1526,56 +1545,32 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
if (mon != nullptr) {
|
||||
// this object has a heavyweight monitor
|
||||
|
||||
// Number of waiters may actually be less than the waiter count.
|
||||
// So null out memory so that unused memory will be null.
|
||||
// null out memory for robustness
|
||||
memset(ret.waiters, 0, ret.waiter_count * sizeof(jthread *));
|
||||
memset(ret.notify_waiters, 0, ret.notify_waiter_count * sizeof(jthread *));
|
||||
|
||||
if (ret.waiter_count > 0) {
|
||||
// we have contending and/or waiting threads
|
||||
if (nWant > 0) {
|
||||
// we have contending threads
|
||||
ResourceMark rm(current_thread);
|
||||
// get_pending_threads returns only java thread so we do not need to
|
||||
// check for non java threads.
|
||||
GrowableArray<JavaThread*>* wantList = Threads::get_pending_threads(tlh.list(), nWant, (address)mon);
|
||||
if (wantList->length() < nWant) {
|
||||
// robustness: the pending list has gotten smaller
|
||||
nWant = wantList->length();
|
||||
}
|
||||
for (int i = 0; i < nWant; i++) {
|
||||
JavaThread *pending_thread = wantList->at(i);
|
||||
Handle th(current_thread, get_vthread_or_thread_oop(pending_thread));
|
||||
ret.waiters[i] = (jthread)jni_reference(calling_thread, th);
|
||||
}
|
||||
if (ret.waiter_count > 0) { // we have contending threads waiting to enter/re-enter the monitor
|
||||
// identify threads waiting to enter and re-enter the monitor
|
||||
// get_pending_threads returns only java thread so we do not need to
|
||||
// check for non java threads.
|
||||
for (int i = 0; i < nWant; i++) {
|
||||
JavaThread *pending_thread = wantList->at(i);
|
||||
Handle th(current_thread, get_vthread_or_thread_oop(pending_thread));
|
||||
ret.waiters[i] = (jthread)jni_reference(calling_thread, th);
|
||||
}
|
||||
if (nWait > 0) {
|
||||
// we have threads in Object.wait()
|
||||
int offset = nWant; // add after any contending threads
|
||||
ObjectWaiter *waiter = mon->first_waiter();
|
||||
for (int i = 0, j = 0; i < nWait; i++) {
|
||||
if (waiter == nullptr) {
|
||||
// robustness: the waiting list has gotten smaller
|
||||
nWait = j;
|
||||
break;
|
||||
}
|
||||
JavaThread *w = mon->thread_of_waiter(waiter);
|
||||
if (w != nullptr) {
|
||||
// If the thread was found on the ObjectWaiter list, then
|
||||
// it has not been notified. This thread can't change the
|
||||
// state of the monitor so it doesn't need to be suspended.
|
||||
Handle th(current_thread, get_vthread_or_thread_oop(w));
|
||||
ret.waiters[offset + j] = (jthread)jni_reference(calling_thread, th);
|
||||
ret.notify_waiters[j++] = (jthread)jni_reference(calling_thread, th);
|
||||
}
|
||||
waiter = mon->next_waiter(waiter);
|
||||
}
|
||||
}
|
||||
if (ret.notify_waiter_count > 0) { // we have threads waiting to be notified in Object.wait()
|
||||
ObjectWaiter *waiter = mon->first_waiter();
|
||||
for (int i = 0; i < nWait; i++) {
|
||||
JavaThread *w = mon->thread_of_waiter(waiter);
|
||||
assert(w != nullptr, "sanity check");
|
||||
// If the thread was found on the ObjectWaiter list, then
|
||||
// it has not been notified.
|
||||
Handle th(current_thread, get_vthread_or_thread_oop(w));
|
||||
ret.notify_waiters[i] = (jthread)jni_reference(calling_thread, th);
|
||||
waiter = mon->next_waiter(waiter);
|
||||
}
|
||||
} // ThreadsListHandle is destroyed here.
|
||||
|
||||
// Adjust count. nWant and nWait count values may be less than original.
|
||||
ret.waiter_count = nWant + nWait;
|
||||
ret.notify_waiter_count = nWait;
|
||||
}
|
||||
} else {
|
||||
// this object has a lightweight monitor and we have nothing more
|
||||
// to do here because the defaults are just fine.
|
||||
|
@ -60,6 +60,7 @@
|
||||
#include "oops/symbol.hpp"
|
||||
#include "prims/jvm_misc.hpp"
|
||||
#include "prims/jvmtiAgentList.hpp"
|
||||
#include "prims/jvmtiEnvBase.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
#include "runtime/flags/jvmFlagLimit.hpp"
|
||||
@ -1181,10 +1182,12 @@ void Threads::metadata_handles_do(void f(Metadata*)) {
|
||||
threads_do(&handles_closure);
|
||||
}
|
||||
|
||||
// Get count Java threads that are waiting to enter the specified monitor.
|
||||
#if INCLUDE_JVMTI
|
||||
// Get count of Java threads that are waiting to enter or re-enter the specified monitor.
|
||||
GrowableArray<JavaThread*>* Threads::get_pending_threads(ThreadsList * t_list,
|
||||
int count,
|
||||
address monitor) {
|
||||
assert(Thread::current()->is_VM_thread(), "Must be the VM thread");
|
||||
GrowableArray<JavaThread*>* result = new GrowableArray<JavaThread*>(count);
|
||||
|
||||
int i = 0;
|
||||
@ -1194,7 +1197,14 @@ GrowableArray<JavaThread*>* Threads::get_pending_threads(ThreadsList * t_list,
|
||||
// The first stage of async deflation does not affect any field
|
||||
// used by this comparison so the ObjectMonitor* is usable here.
|
||||
address pending = (address)p->current_pending_monitor();
|
||||
if (pending == monitor) { // found a match
|
||||
address waiting = (address)p->current_waiting_monitor();
|
||||
oop thread_oop = JvmtiEnvBase::get_vthread_or_thread_oop(p);
|
||||
bool is_virtual = java_lang_VirtualThread::is_instance(thread_oop);
|
||||
jint state = is_virtual ? JvmtiEnvBase::get_vthread_state(thread_oop, p)
|
||||
: JvmtiEnvBase::get_thread_state(thread_oop, p);
|
||||
if (pending == monitor || (waiting == monitor &&
|
||||
(state & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER))
|
||||
) { // found a match
|
||||
if (i < count) result->append(p); // save the first count matches
|
||||
i++;
|
||||
}
|
||||
@ -1202,7 +1212,7 @@ GrowableArray<JavaThread*>* Threads::get_pending_threads(ThreadsList * t_list,
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // INCLUDE_JVMTI
|
||||
|
||||
JavaThread *Threads::owning_thread_from_monitor_owner(ThreadsList * t_list,
|
||||
address owner) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -131,7 +131,7 @@ public:
|
||||
// Print threads busy compiling, and returns the number of printed threads.
|
||||
static unsigned print_threads_compiling(outputStream* st, char* buf, int buflen, bool short_form = false);
|
||||
|
||||
// Get Java threads that are waiting to enter a monitor.
|
||||
// Get count of Java threads that are waiting to enter or re-enter the specified monitor.
|
||||
static GrowableArray<JavaThread*>* get_pending_threads(ThreadsList * t_list,
|
||||
int count, address monitor);
|
||||
|
||||
|
@ -1619,8 +1619,8 @@ JDWP "Java(tm) Debug Wire Protocol"
|
||||
(Reply
|
||||
(threadObject owner "The monitor owner, or null if it is not currently owned.")
|
||||
(int entryCount "The number of times the monitor has been entered.")
|
||||
(Repeat waiters "The number of threads that are waiting for the monitor "
|
||||
"0 if there is no current owner"
|
||||
(Repeat waiters "The total number of threads that are waiting to enter or re-enter "
|
||||
"the monitor, or waiting to be notified by the monitor."
|
||||
(threadObject thread "A thread waiting for this monitor.")
|
||||
)
|
||||
)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2020, 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
|
||||
@ -219,14 +219,19 @@ monitorInfo(PacketInputStream *in, PacketOutputStream *out)
|
||||
int i;
|
||||
(void)outStream_writeObjectRef(env, out, info.owner);
|
||||
(void)outStream_writeInt(out, info.entry_count);
|
||||
(void)outStream_writeInt(out, info.waiter_count);
|
||||
(void)outStream_writeInt(out, info.waiter_count + info.notify_waiter_count);
|
||||
for (i = 0; i < info.waiter_count; i++) {
|
||||
(void)outStream_writeObjectRef(env, out, info.waiters[i]);
|
||||
}
|
||||
for (i = 0; i < info.notify_waiter_count; i++) {
|
||||
(void)outStream_writeObjectRef(env, out, info.notify_waiters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (info.waiters != NULL )
|
||||
jvmtiDeallocate(info.waiters);
|
||||
if (info.notify_waiters != NULL )
|
||||
jvmtiDeallocate(info.notify_waiters);
|
||||
|
||||
} END_WITH_LOCAL_REFS(env);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2018, 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
|
||||
@ -969,7 +969,6 @@ vmTestbase_nsk_jvmti_quick = \
|
||||
vmTestbase/nsk/jvmti/GetMethodName/methname003/TestDescription.java \
|
||||
vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage001/TestDescription.java \
|
||||
vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage002/TestDescription.java \
|
||||
vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage003/TestDescription.java \
|
||||
vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage004/TestDescription.java \
|
||||
vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage005/TestDescription.java \
|
||||
vmTestbase/nsk/jvmti/GetObjectSize/objsize001/TestDescription.java \
|
||||
|
@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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 8247972
|
||||
*
|
||||
* @summary converted from VM Testbase nsk/jvmti/GetObjectMonitorUsage/objmonusage003
|
||||
* DESCRIPTION
|
||||
* The test checks if the JVMTI function GetObjectMonitorUsage returns
|
||||
* the expected values for the owner, entry_count, waiter_count
|
||||
* fields of JVMTI_monitor_info.
|
||||
* The testcases are the following:
|
||||
* - unowned object without any threads waiting
|
||||
* - unowned object with threads waiting to be notified
|
||||
* - owned object without any threads waiting
|
||||
* - owned object with N threads waiting to enter the monitor
|
||||
* - owned object with N threads waiting to be notified
|
||||
* - owned object with N threads waiting to enter, from 0 to N threads
|
||||
* waiting to re-enter, from N to 0 threads waiting to be notified
|
||||
* - all the above scenarios are executed with platform and virtual threads
|
||||
* @requires vm.jvmti
|
||||
* @run main/othervm/native
|
||||
* -Djdk.virtualThreadScheduler.parallelism=10
|
||||
* -agentlib:ObjectMonitorUsage ObjectMonitorUsage
|
||||
*/
|
||||
|
||||
public class ObjectMonitorUsage {
|
||||
final static int NUMBER_OF_ENTERING_THREADS = 4;
|
||||
final static int NUMBER_OF_WAITING_THREADS = 4;
|
||||
final static int NUMBER_OF_THREADS = NUMBER_OF_ENTERING_THREADS + NUMBER_OF_WAITING_THREADS;
|
||||
|
||||
static Object lockCheck = new Object();
|
||||
|
||||
native static int getRes();
|
||||
native static int waitsToEnter();
|
||||
native static int waitsToBeNotified();
|
||||
native static int setTestedMonitor(Object monitor);
|
||||
native static void check(Object obj, Thread owner,
|
||||
int entryCount, int waiterCount, int notifyWaiterCount);
|
||||
|
||||
static void log(String msg) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
static String vtag(boolean isVirtual) {
|
||||
return isVirtual ? "virtual" : "platform";
|
||||
}
|
||||
|
||||
static void sleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
static Thread startTask(int idx, TestTask task, boolean isVirtual, String kind) {
|
||||
Thread thread = isVirtual ? Thread.ofVirtual().name(kind + "VT" + idx).start(task)
|
||||
: Thread.ofPlatform().name(kind + "PT" + idx).start(task);
|
||||
task.setName(thread.getName());
|
||||
task.waitReady();
|
||||
return thread;
|
||||
}
|
||||
|
||||
static Thread[] startWaitingThreads(boolean isVirtual) {
|
||||
Thread[] threads = new Thread[NUMBER_OF_WAITING_THREADS];
|
||||
for (int i = 0; i < NUMBER_OF_WAITING_THREADS; i++) {
|
||||
// the WaitingTask has to wait to be notified in lockCheck.wait()
|
||||
threads[i] = startTask(i, new WaitingTask(), isVirtual, "Waiting");
|
||||
}
|
||||
while (waitsToBeNotified() < NUMBER_OF_WAITING_THREADS) {
|
||||
sleep(1);
|
||||
}
|
||||
return threads;
|
||||
}
|
||||
|
||||
static Thread[] startEnteringThreads(boolean isVirtual) {
|
||||
Thread[] threads = new Thread[NUMBER_OF_ENTERING_THREADS];
|
||||
for (int i = 0; i < NUMBER_OF_ENTERING_THREADS; i++) {
|
||||
// the EnteringTask has to be blocked at the lockCheck enter
|
||||
threads[i] = startTask(i, new EnteringTask(), isVirtual, "Entering");
|
||||
}
|
||||
while (waitsToEnter() < NUMBER_OF_ENTERING_THREADS) {
|
||||
sleep(1);
|
||||
}
|
||||
return threads;
|
||||
}
|
||||
|
||||
static void joinThreads(Thread[] threads) {
|
||||
try {
|
||||
for (Thread t : threads) {
|
||||
t.join();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new Error("Unexpected " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/* Scenario #0:
|
||||
* - owning: 0
|
||||
* - entering: 0
|
||||
* - re-entering: 0
|
||||
* - to be notified: N
|
||||
*/
|
||||
static void test0(boolean isVirtual) {
|
||||
String vtag = vtag(isVirtual);
|
||||
log("\n### test0: started " + vtag);
|
||||
|
||||
setTestedMonitor(lockCheck);
|
||||
Thread[] wThreads = startWaitingThreads(isVirtual);
|
||||
|
||||
// entry count: 0
|
||||
// count of threads waiting to enter: 0
|
||||
// count of threads waiting to re-enter: 0
|
||||
// count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
|
||||
check(lockCheck, null, 0, // no owner thread
|
||||
0, // count of threads waiting to enter: 0
|
||||
NUMBER_OF_WAITING_THREADS);
|
||||
|
||||
synchronized (lockCheck) {
|
||||
lockCheck.notifyAll();
|
||||
}
|
||||
joinThreads(wThreads);
|
||||
setTestedMonitor(null);
|
||||
log("### test0: finished " + vtag);
|
||||
}
|
||||
|
||||
/* Scenario #1:
|
||||
* - owning: 1
|
||||
* - entering: N
|
||||
* - re-entering: 0
|
||||
* - to be notified: 0
|
||||
*/
|
||||
static void test1(boolean isVirtual) {
|
||||
String vtag = vtag(isVirtual);
|
||||
log("\n### test1: started " + vtag);
|
||||
|
||||
setTestedMonitor(lockCheck);
|
||||
Thread[] eThreads = null;
|
||||
|
||||
synchronized (lockCheck) {
|
||||
// entry count: 1
|
||||
// count of threads waiting to enter: 0
|
||||
// count of threads waiting to re-enter: 0
|
||||
// count of threads waiting to be notified: 0
|
||||
check(lockCheck, Thread.currentThread(), 1, 0, 0);
|
||||
|
||||
eThreads = startEnteringThreads(isVirtual);
|
||||
|
||||
// entry count: 1
|
||||
// count of threads waiting to enter: NUMBER_OF_ENTERING_THREADS
|
||||
// count of threads waiting to re-enter: 0
|
||||
// count of threads waiting to be notified: 0
|
||||
check(lockCheck, Thread.currentThread(), 1,
|
||||
NUMBER_OF_ENTERING_THREADS,
|
||||
0 /* count of threads waiting to be notified: 0 */);
|
||||
|
||||
}
|
||||
joinThreads(eThreads);
|
||||
setTestedMonitor(null);
|
||||
log("### test1: finished " + vtag);
|
||||
}
|
||||
|
||||
/* Scenario #2:
|
||||
* - owning: 1
|
||||
* - entering: N
|
||||
* - re-entering: 0
|
||||
* - to be notified: N
|
||||
*/
|
||||
static void test2(boolean isVirtual) throws Error {
|
||||
String vtag = vtag(isVirtual);
|
||||
log("\n### test2: started " + vtag);
|
||||
|
||||
setTestedMonitor(lockCheck);
|
||||
Thread[] wThreads = startWaitingThreads(isVirtual);
|
||||
Thread[] eThreads = null;
|
||||
|
||||
synchronized (lockCheck) {
|
||||
eThreads = startEnteringThreads(isVirtual);
|
||||
|
||||
// entry count: 1
|
||||
// count of threads waiting to enter: NUMBER_OF_ENTERING_THREADS
|
||||
// count of threads waiting to re-enter: 0
|
||||
// count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
|
||||
check(lockCheck, Thread.currentThread(), 1,
|
||||
NUMBER_OF_ENTERING_THREADS,
|
||||
NUMBER_OF_WAITING_THREADS);
|
||||
|
||||
lockCheck.notifyAll();
|
||||
}
|
||||
joinThreads(wThreads);
|
||||
joinThreads(eThreads);
|
||||
setTestedMonitor(null);
|
||||
log("### test2: finished " + vtag);
|
||||
}
|
||||
|
||||
/* Scenario #3:
|
||||
* Initially we have:
|
||||
* - owning: 1
|
||||
* - entering: 0
|
||||
* - re-entering: 0
|
||||
* - to be notified: N
|
||||
*
|
||||
* The threads waiting to be notified are being notified one-by-one
|
||||
* until all threads are blocked on re-entering the monitor.
|
||||
* The numbers of entering/re-entering and waiting threads are checked
|
||||
* for correctness after each notification.
|
||||
*/
|
||||
static void test3(boolean isVirtual) throws Error {
|
||||
String vtag = vtag(isVirtual);
|
||||
log("\n### test3: started " + vtag);
|
||||
|
||||
setTestedMonitor(lockCheck);
|
||||
Thread[] wThreads = startWaitingThreads(isVirtual);
|
||||
Thread[] eThreads = null;
|
||||
|
||||
synchronized (lockCheck) {
|
||||
// entry count: 1
|
||||
// count of threads waiting to enter: 0
|
||||
// count of threads waiting to re-enter: 0
|
||||
// count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
|
||||
check(lockCheck, Thread.currentThread(), 1,
|
||||
0, // number of threads waiting to enter or re-enter
|
||||
NUMBER_OF_WAITING_THREADS);
|
||||
|
||||
eThreads = startEnteringThreads(isVirtual);
|
||||
|
||||
// entry count: 1
|
||||
// count of threads waiting to enter: NUMBER_OF_ENTERING_THREADS
|
||||
// count of threads waiting to re-enter: 0
|
||||
// count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
|
||||
check(lockCheck, Thread.currentThread(), 1,
|
||||
NUMBER_OF_ENTERING_THREADS,
|
||||
NUMBER_OF_WAITING_THREADS);
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_WAITING_THREADS; i++) {
|
||||
lockCheck.notify(); // notify waiting threads one by one
|
||||
// now the notified WaitingTask has to be blocked on the lockCheck re-enter
|
||||
|
||||
// entry count: 1
|
||||
// count of threads waiting to enter: NUMBER_OF_ENTERING_THREADS
|
||||
// count of threads waiting to re-enter: i + 1
|
||||
// count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS - i - 1
|
||||
check(lockCheck, Thread.currentThread(), 1,
|
||||
NUMBER_OF_ENTERING_THREADS + i + 1,
|
||||
NUMBER_OF_WAITING_THREADS - i - 1);
|
||||
}
|
||||
}
|
||||
joinThreads(wThreads);
|
||||
joinThreads(eThreads);
|
||||
setTestedMonitor(null);
|
||||
log("### test3: finished " + vtag);
|
||||
}
|
||||
|
||||
static void test(boolean isVirtual) {
|
||||
test0(isVirtual);
|
||||
test1(isVirtual);
|
||||
test2(isVirtual);
|
||||
test3(isVirtual);
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
log("\n### main: started\n");
|
||||
check(lockCheck, null, 0, 0, 0);
|
||||
|
||||
test(false); // test platform threads
|
||||
test(true); // test virtual threads
|
||||
|
||||
check(lockCheck, null, 0, 0, 0);
|
||||
if (getRes() > 0) {
|
||||
throw new RuntimeException("Failed status returned from the agent");
|
||||
}
|
||||
log("\n### main: finished\n");
|
||||
}
|
||||
|
||||
static abstract class TestTask implements Runnable {
|
||||
volatile boolean ready = false;
|
||||
String name;
|
||||
|
||||
public abstract void run();
|
||||
|
||||
String getName() { return name; }
|
||||
void setName(String name) { this.name = name; }
|
||||
|
||||
void waitReady() {
|
||||
try {
|
||||
while (!ready) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new Error("Unexpected " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class EnteringTask extends TestTask {
|
||||
public void run() {
|
||||
ready = true;
|
||||
synchronized (lockCheck) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class WaitingTask extends TestTask {
|
||||
public void run() {
|
||||
synchronized (lockCheck) {
|
||||
try {
|
||||
ready = true;
|
||||
// no protection against spurious wakeups here
|
||||
lockCheck.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new Error("Unexpected " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#define PASSED 0
|
||||
#define STATUS_FAILED 2
|
||||
|
||||
static jvmtiEnv *jvmti = nullptr;
|
||||
static jrawMonitorID event_lock = nullptr;
|
||||
static jint result = PASSED;
|
||||
static int check_idx = 0;
|
||||
static int waits_to_enter = 0;
|
||||
static int waits_to_be_notified = 0;
|
||||
static jobject tested_monitor = nullptr;
|
||||
|
||||
static bool is_tested_monitor(JNIEnv *jni, jobject monitor) {
|
||||
if (tested_monitor == nullptr) {
|
||||
return false; // tested_monitor was not set yet
|
||||
}
|
||||
return jni->IsSameObject(monitor, tested_monitor) == JNI_TRUE;
|
||||
}
|
||||
|
||||
static void log_event(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread,
|
||||
const char* title, int counter) {
|
||||
char* tname = get_thread_name(jvmti, jni, thread);
|
||||
LOG(">>> %s event: %s counter: %d\n", title, tname, counter);
|
||||
deallocate(jvmti, jni, (void*)tname);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
MonitorContendedEnter(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject monitor) {
|
||||
RawMonitorLocker rml(jvmti, jni, event_lock);
|
||||
if (is_tested_monitor(jni, monitor)) {
|
||||
waits_to_enter++;
|
||||
log_event(jvmti, jni, thread, "MonitorContendedEnter", waits_to_enter);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
MonitorContendedEntered(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject monitor) {
|
||||
RawMonitorLocker rml(jvmti, jni, event_lock);
|
||||
if (is_tested_monitor(jni, monitor)) {
|
||||
waits_to_enter--;
|
||||
log_event(jvmti, jni, thread, "MonitorContendedEntered", waits_to_enter);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
MonitorWait(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject monitor, jlong timeout) {
|
||||
RawMonitorLocker rml(jvmti, jni, event_lock);
|
||||
if (is_tested_monitor(jni, monitor)) {
|
||||
waits_to_be_notified++;
|
||||
log_event(jvmti, jni, thread, "MonitorWait", waits_to_be_notified);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
MonitorWaited(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jobject monitor, jboolean timed_out) {
|
||||
RawMonitorLocker rml(jvmti, jni, event_lock);
|
||||
if (is_tested_monitor(jni, monitor)) {
|
||||
waits_to_be_notified--;
|
||||
log_event(jvmti, jni, thread, "MonitorWaited", waits_to_be_notified);
|
||||
}
|
||||
}
|
||||
|
||||
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
|
||||
jint res;
|
||||
jvmtiError err;
|
||||
jvmtiCapabilities caps;
|
||||
jvmtiEventCallbacks callbacks;
|
||||
|
||||
res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
|
||||
if (res != JNI_OK || jvmti == nullptr) {
|
||||
LOG("Wrong result of a valid call to GetEnv !\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
err = jvmti->GetPotentialCapabilities(&caps);
|
||||
check_jvmti_error(err, "Agent_Initialize: error in JVMTI GetPotentialCapabilities");
|
||||
|
||||
err = jvmti->AddCapabilities(&caps);
|
||||
check_jvmti_error(err, "Agent_Initialize: error in JVMTI AddCapabilities");
|
||||
|
||||
err = jvmti->GetCapabilities(&caps);
|
||||
check_jvmti_error(err, "Agent_Initialize: error in JVMTI GetCapabilities");
|
||||
|
||||
if (!caps.can_get_monitor_info) {
|
||||
LOG("Warning: GetObjectMonitorUsage is not implemented\n");
|
||||
}
|
||||
if (!caps.can_generate_monitor_events) {
|
||||
LOG("Warning: Monitor events are not implemented\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.MonitorContendedEnter = &MonitorContendedEnter;
|
||||
callbacks.MonitorContendedEntered = &MonitorContendedEntered;
|
||||
callbacks.MonitorWait = &MonitorWait;
|
||||
callbacks.MonitorWaited = &MonitorWaited;
|
||||
|
||||
err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
|
||||
check_jvmti_error(err, "Agent_Initialize: error in JVMTI SetEventCallbacks");
|
||||
|
||||
event_lock = create_raw_monitor(jvmti, "Events Monitor");
|
||||
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
|
||||
return Agent_Initialize(jvm, options, reserved);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
|
||||
return Agent_Initialize(jvm, options, reserved);
|
||||
}
|
||||
|
||||
static void print_monitor_info(JNIEnv *jni, jvmtiMonitorUsage &inf) {
|
||||
jvmtiError err;
|
||||
jvmtiThreadInfo tinf;
|
||||
|
||||
LOG(">>> [%d]\n", check_idx);
|
||||
if (inf.owner == nullptr) {
|
||||
LOG(">>> owner: none (0x0)\n");
|
||||
} else {
|
||||
err = jvmti->GetThreadInfo(inf.owner, &tinf);
|
||||
check_jvmti_status(jni, err, "error in JVMTI GetThreadInfo");
|
||||
LOG(">>> owner: %s (0x%p)\n",
|
||||
tinf.name, inf.owner);
|
||||
deallocate(jvmti, jni, tinf.name);
|
||||
}
|
||||
LOG(">>> entry_count: %d\n", inf.entry_count);
|
||||
LOG(">>> waiter_count: %d\n", inf.waiter_count);
|
||||
LOG(">>> notify_waiter_count: %d\n", inf.notify_waiter_count);
|
||||
|
||||
if (inf.waiter_count > 0) {
|
||||
LOG(">>> waiters:\n");
|
||||
for (int j = 0; j < inf.waiter_count; j++) {
|
||||
err = jvmti->GetThreadInfo(inf.waiters[j], &tinf);
|
||||
check_jvmti_status(jni, err, "error in JVMTI GetThreadInfo");
|
||||
LOG(">>> %2d: %s (0x%p)\n",
|
||||
j, tinf.name, inf.waiters[j]);
|
||||
deallocate(jvmti, jni, tinf.name);
|
||||
}
|
||||
}
|
||||
if (inf.notify_waiter_count > 0) {
|
||||
LOG(">>> notify_waiters:\n");
|
||||
for (int j = 0; j < inf.notify_waiter_count; j++) {
|
||||
err = jvmti->GetThreadInfo(inf.notify_waiters[j], &tinf);
|
||||
check_jvmti_status(jni, err, "error in JVMTI GetThreadInfo");
|
||||
LOG(">>> %2d: %s (0x%p)\n",
|
||||
j, tinf.name, inf.notify_waiters[j]);
|
||||
deallocate(jvmti, jni, tinf.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ObjectMonitorUsage_check(JNIEnv *jni, jclass cls, jobject obj, jthread owner,
|
||||
jint entryCount, jint waiterCount, jint notifyWaiterCount) {
|
||||
jvmtiError err;
|
||||
jvmtiMonitorUsage inf;
|
||||
|
||||
check_idx++;
|
||||
|
||||
err = jvmti->GetObjectMonitorUsage(obj, &inf);
|
||||
check_jvmti_status(jni, err, "error in JVMTI GetObjectMonitorUsage");
|
||||
|
||||
print_monitor_info(jni, inf);
|
||||
|
||||
if (!jni->IsSameObject(owner, inf.owner)) {
|
||||
LOG("FAILED: (%d) unexpected owner: 0x%p\n", check_idx, inf.owner);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
if (inf.entry_count != entryCount) {
|
||||
LOG("FAILED: (%d) entry_count expected: %d, actually: %d\n",
|
||||
check_idx, entryCount, inf.entry_count);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
if (inf.waiter_count != waiterCount) {
|
||||
LOG("FAILED: (%d) waiter_count expected: %d, actually: %d\n",
|
||||
check_idx, waiterCount, inf.waiter_count);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
if (inf.notify_waiter_count != notifyWaiterCount) {
|
||||
LOG("FAILED: (%d) notify_waiter_count expected: %d, actually: %d\n",
|
||||
check_idx, notifyWaiterCount, inf.notify_waiter_count);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ObjectMonitorUsage_setTestedMonitor(JNIEnv *jni, jclass cls, jobject monitor) {
|
||||
jvmtiError err;
|
||||
jvmtiEventMode event_mode = (monitor != nullptr) ? JVMTI_ENABLE : JVMTI_DISABLE;
|
||||
|
||||
RawMonitorLocker rml(jvmti, jni, event_lock);
|
||||
|
||||
if (tested_monitor != nullptr) {
|
||||
jni->DeleteGlobalRef(tested_monitor);
|
||||
}
|
||||
tested_monitor = (monitor != nullptr) ? jni->NewGlobalRef(monitor) : nullptr;
|
||||
waits_to_enter = 0;
|
||||
waits_to_be_notified = 0;
|
||||
|
||||
err = jvmti->SetEventNotificationMode(event_mode, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, nullptr);
|
||||
check_jvmti_status(jni, err, "setTestedMonitor: error in JVMTI SetEventNotificationMode #1");
|
||||
|
||||
err = jvmti->SetEventNotificationMode(event_mode, JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, nullptr);
|
||||
check_jvmti_status(jni, err, "setTestedMonitor: error in JVMTI SetEventNotificationMode #2");
|
||||
|
||||
err = jvmti->SetEventNotificationMode(event_mode, JVMTI_EVENT_MONITOR_WAIT, nullptr);
|
||||
check_jvmti_status(jni, err, "setTestedMonitor: error in JVMTI SetEventNotificationMode #3");
|
||||
|
||||
err = jvmti->SetEventNotificationMode(event_mode, JVMTI_EVENT_MONITOR_WAITED, nullptr);
|
||||
check_jvmti_status(jni, err, "setTestedMonitor: error in JVMTI SetEventNotificationMode #4");
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ObjectMonitorUsage_waitsToEnter(JNIEnv *jni, jclass cls) {
|
||||
RawMonitorLocker rml(jvmti, jni, event_lock);
|
||||
return waits_to_enter;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ObjectMonitorUsage_waitsToBeNotified(JNIEnv *jni, jclass cls) {
|
||||
RawMonitorLocker rml(jvmti, jni, event_lock);
|
||||
return waits_to_be_notified;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ObjectMonitorUsage_getRes(JNIEnv *jni, jclass cls) {
|
||||
return result;
|
||||
}
|
||||
|
||||
} // extern "C"
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -28,6 +28,7 @@ import java.io.PrintStream;
|
||||
public class objmonusage001 {
|
||||
final static int JCK_STATUS_BASE = 95;
|
||||
final static int NUMBER_OF_THREADS = 32;
|
||||
final static boolean ADD_DELAYS_FOR_RACES = false;
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -41,7 +42,9 @@ public class objmonusage001 {
|
||||
}
|
||||
|
||||
native static int getResult();
|
||||
native static void check(int i, Object o, Thread owner, int ec, int wc);
|
||||
native static void check(int index, Object syncObject, Thread owner, int entryCount,
|
||||
Thread waiterThread, int waiterCount,
|
||||
Thread notifyWaiterThread, int notifyWaiterCount);
|
||||
|
||||
public static void main(String argv[]) {
|
||||
argv = nsk.share.jvmti.JVMTITest.commonInit(argv);
|
||||
@ -50,12 +53,13 @@ public class objmonusage001 {
|
||||
}
|
||||
|
||||
public static int run(String argv[], PrintStream out) {
|
||||
Thread mainThread = Thread.currentThread();
|
||||
Object syncObject[] = new Object[NUMBER_OF_THREADS];
|
||||
objmonusage001a runn[] = new objmonusage001a[NUMBER_OF_THREADS];
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
|
||||
syncObject[i] = new Object();
|
||||
runn[i] = new objmonusage001a(i, syncObject[i]);
|
||||
runn[i] = new objmonusage001a(mainThread, i, syncObject[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
|
||||
@ -67,8 +71,53 @@ public class objmonusage001 {
|
||||
out.println(e);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Check #2:
|
||||
// - owner == main:
|
||||
// main thread owns the monitor and worker thread
|
||||
// is in wait() and is not notified
|
||||
// - entry_count == 1:
|
||||
// main thread reentered 1 time
|
||||
// - waiter_count == 0:
|
||||
// main thread has already reentered the monitor and worker thread
|
||||
// is in wait() and is not notified so it is not waiting to reenter
|
||||
// the monitor
|
||||
// - waiter_thread == null:
|
||||
// no thread is waiting to reenter the monitor
|
||||
// - notify_waiter_count == 1:
|
||||
// worker thread is in wait() and is not notified
|
||||
// - notify_waiter_thread == runn[i]:
|
||||
// worker thread is in wait() and is not notified
|
||||
//
|
||||
// This is a stable verification point because the worker thread is in wait()
|
||||
// and is not notified and the main thread is doing the verification.
|
||||
//
|
||||
check(NUMBER_OF_THREADS + i, syncObject[i], mainThread, 1,
|
||||
null, 0, runn[i], 1);
|
||||
}
|
||||
check(NUMBER_OF_THREADS + i, syncObject[i], null, 0, 1);
|
||||
|
||||
// Check #3:
|
||||
// - owner == null:
|
||||
// main thread does not own the monitor and worker thread is in
|
||||
// wait() and is not notified so there is no owner
|
||||
// - entry_count == 0:
|
||||
// no owner so entry_count is 0
|
||||
// - waiter_count == 0:
|
||||
// main thread is not trying to enter the monitor and worker thread
|
||||
// is in wait() and is not notified so it is not waiting to reenter
|
||||
// the monitor
|
||||
// - waiter_thread == null:
|
||||
// no thread is waiting to reenter the monitor
|
||||
// - notify_waiter_count == 1:
|
||||
// worker thread is in wait() and is not notified
|
||||
// - notify_waiter_thread == runn[i]:
|
||||
// worker thread is in wait() and is not notified
|
||||
//
|
||||
// This is a stable verification point because the worker thread is in wait()
|
||||
// and is not notified and the main thread is doing the verification.
|
||||
//
|
||||
check((NUMBER_OF_THREADS * 2) + i, syncObject[i], null, 0,
|
||||
null, 0, runn[i], 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
|
||||
@ -87,20 +136,46 @@ public class objmonusage001 {
|
||||
}
|
||||
|
||||
class objmonusage001a extends Thread {
|
||||
Thread mainThread;
|
||||
Object syncObject;
|
||||
int ind;
|
||||
int index;
|
||||
|
||||
public objmonusage001a(int i, Object s) {
|
||||
ind = i;
|
||||
public objmonusage001a(Thread mt, int i, Object s) {
|
||||
mainThread = mt;
|
||||
index = i;
|
||||
syncObject = s;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
synchronized (syncObject) {
|
||||
objmonusage001.check(ind, syncObject, this, 1, 1);
|
||||
// Check #1:
|
||||
// - owner == this_thread:
|
||||
// this worker thread is owner
|
||||
// - entry_count == 1:
|
||||
// worker thread entered 1 time
|
||||
// - waiter_count == 0:
|
||||
// main thread is in wait() and is not notified so it is not
|
||||
// waiting to reenter the monitor
|
||||
// - waiter_thread == null:
|
||||
// no thread is waiting to reenter the monitor
|
||||
// - notify_waiter_count == 1:
|
||||
// main thread is in wait() and is not notified
|
||||
// - notify_waiter_thread == mainThread:
|
||||
// main thread is in wait() and is not notified
|
||||
//
|
||||
// This is a stable verification point because the main thread is in wait()
|
||||
// and is not notified and this worker thread is doing the verification.
|
||||
//
|
||||
objmonusage001.check(index, syncObject, this, 1,
|
||||
null, 0, mainThread, 1);
|
||||
syncObject.notify();
|
||||
|
||||
try {
|
||||
syncObject.wait();
|
||||
|
||||
if (objmonusage001.ADD_DELAYS_FOR_RACES) {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new Error("Unexpected " + e);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -29,7 +29,14 @@
|
||||
* VM Testbase keywords: [quick, jpda, jvmti, noras]
|
||||
* VM Testbase readme:
|
||||
* DESCRIPTION
|
||||
* The test exercises JVMTI function GetObjectMonitorUsage.
|
||||
* The test exercises JVMTI function GetObjectMonitorUsage. The main
|
||||
* thread uses a monitor to do coordinated launches of work threads.
|
||||
* Each worker thread verifies expected GetObjectMonitorUsage values
|
||||
* when it gets going and the main thread also verifies expected
|
||||
* GetObjectMonitorUsage values once the worker thread returns
|
||||
* control flow to the main thread. The test scenario is repeated
|
||||
* for a fixed number of threads.
|
||||
*
|
||||
* COMMENTS
|
||||
* Fixed according to 4669812 bug.
|
||||
* Ported from JVMDI test nsk/jvmdi/GetMonitorInfo/getmoninfo001.
|
||||
|
@ -94,13 +94,15 @@ jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
|
||||
JNIEXPORT void JNICALL
|
||||
Java_nsk_jvmti_GetObjectMonitorUsage_objmonusage001_check(JNIEnv *env,
|
||||
jclass cls, jint i, jobject obj,
|
||||
jthread owner, jint entryCount, jint waiterCount) {
|
||||
jthread owner, jint entryCount,
|
||||
jthread waiterThread, jint waiterCount,
|
||||
jthread notifyWaiterThread, jint notifyWaiterCount) {
|
||||
jvmtiError err;
|
||||
jvmtiMonitorUsage inf;
|
||||
jvmtiThreadInfo tinf;
|
||||
int j;
|
||||
|
||||
if (result == STATUS_FAILED) {
|
||||
if (result == STATUS_FAILED && printdump != JNI_TRUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -152,16 +154,38 @@ Java_nsk_jvmti_GetObjectMonitorUsage_objmonusage001_check(JNIEnv *env,
|
||||
}
|
||||
|
||||
if (inf.entry_count != entryCount) {
|
||||
printf("(%d) entry_count expected: %d, actually: %d\n",
|
||||
printf("FAILED: (%d) entry_count expected: %d, actually: %d\n",
|
||||
i, entryCount, inf.entry_count);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
|
||||
if (inf.waiter_count != waiterCount) {
|
||||
printf("(%d) waiter_count expected: %d, actually: %d\n",
|
||||
printf("FAILED: (%d) waiter_count expected: %d, actually: %d\n",
|
||||
i, waiterCount, inf.waiter_count);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
|
||||
if (inf.waiters != nullptr &&
|
||||
!env->IsSameObject(waiterThread, inf.waiters[0])) {
|
||||
jvmti->GetThreadInfo(inf.waiters[0], &tinf);
|
||||
printf("FAILED: (%d) unexpected waiterThread: %s (0x%p)\n", i,
|
||||
tinf.name, inf.waiters[0]);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
|
||||
if (inf.notify_waiter_count != notifyWaiterCount) {
|
||||
printf("FAILED: (%d) notify_waiter_count expected: %d, actually: %d\n",
|
||||
i, notifyWaiterCount, inf.notify_waiter_count);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
|
||||
if (inf.notify_waiters != nullptr &&
|
||||
!env->IsSameObject(notifyWaiterThread, inf.notify_waiters[0])) {
|
||||
jvmti->GetThreadInfo(inf.notify_waiters[0], &tinf);
|
||||
printf("FAILED: (%d) unexpected waiterThread: %s (0x%p)\n", i,
|
||||
tinf.name, inf.notify_waiters[0]);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2018, 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.
|
||||
*/
|
||||
|
||||
package nsk.jvmti.GetObjectMonitorUsage;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class objmonusage003 {
|
||||
|
||||
final static int JCK_STATUS_BASE = 95;
|
||||
final static int NUMBER_OF_THREADS = 16;
|
||||
final static int WAIT_TIME = 100;
|
||||
volatile static boolean waiterInLockCheck = false;
|
||||
|
||||
static {
|
||||
try {
|
||||
System.loadLibrary("objmonusage003");
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
System.err.println("Could not load objmonusage003 library");
|
||||
System.err.println("java.library.path:"
|
||||
+ System.getProperty("java.library.path"));
|
||||
throw ule;
|
||||
}
|
||||
}
|
||||
|
||||
static Object lockStart = new Object();
|
||||
static Object lockCheck = new Object();
|
||||
|
||||
native static int getRes();
|
||||
native static void check(Object obj, Thread owner,
|
||||
int entryCount, int waiterCount);
|
||||
|
||||
public static void main(String args[]) {
|
||||
args = nsk.share.jvmti.JVMTITest.commonInit(args);
|
||||
|
||||
// produce JCK-like exit status.
|
||||
System.exit(run(args, System.out) + JCK_STATUS_BASE);
|
||||
}
|
||||
|
||||
public static int run(String args[], PrintStream out) {
|
||||
check(lockCheck, null, 0, 0);
|
||||
|
||||
synchronized (lockCheck) {
|
||||
check(lockCheck, Thread.currentThread(), 1, 0);
|
||||
}
|
||||
|
||||
WaiterThread thr[] = new WaiterThread[NUMBER_OF_THREADS];
|
||||
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
|
||||
thr[i] = new WaiterThread();
|
||||
synchronized (lockStart) {
|
||||
thr[i].start();
|
||||
try {
|
||||
lockStart.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new Error("Unexpected " + e);
|
||||
}
|
||||
}
|
||||
synchronized (lockCheck) {
|
||||
while (!waiterInLockCheck) {
|
||||
try {
|
||||
lockCheck.wait(WAIT_TIME);
|
||||
} catch (InterruptedException e) {
|
||||
throw new Error("Unexpected " + e);
|
||||
}
|
||||
}
|
||||
waiterInLockCheck = false;
|
||||
}
|
||||
check(lockCheck, null, 0, i + 1);
|
||||
}
|
||||
|
||||
synchronized (lockCheck) {
|
||||
lockCheck.notifyAll();
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
|
||||
try {
|
||||
thr[i].join();
|
||||
} catch (InterruptedException e) {
|
||||
throw new Error("Unexpected " + e);
|
||||
}
|
||||
}
|
||||
|
||||
check(lockCheck, null, 0, 0);
|
||||
return getRes();
|
||||
}
|
||||
|
||||
static class WaiterThread extends Thread {
|
||||
public synchronized void run() {
|
||||
synchronized (lockStart) {
|
||||
lockStart.notify();
|
||||
}
|
||||
synchronized (lockCheck) {
|
||||
try {
|
||||
waiterInLockCheck = true;
|
||||
lockCheck.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new Error("Unexpected " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2020, 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
|
||||
*
|
||||
* @summary converted from VM Testbase nsk/jvmti/GetObjectMonitorUsage/objmonusage003.
|
||||
* VM Testbase keywords: [quick, jpda, jvmti, noras]
|
||||
* VM Testbase readme:
|
||||
* DESCRIPTION
|
||||
* The test checks if JVMTI function GetObjectMonitorUsage returns
|
||||
* the expected values for the owner, entry_count, water_count
|
||||
* fields of JVMTI_monitor_info. The tescases are the following:
|
||||
* - unowned object without any waitings
|
||||
* - owned object without any waitings
|
||||
* - unowned object with waitings through Object.wait()
|
||||
* - unowned object has been waiting
|
||||
* COMMENTS
|
||||
* Fixed according to 4669812 bug.
|
||||
* Ported from JVMDI test nsk/jvmdi/GetMonitorInfo/getmoninfo003.
|
||||
*
|
||||
* @library /vmTestbase
|
||||
* /test/lib
|
||||
* @run main/othervm/native -agentlib:objmonusage003 nsk.jvmti.GetObjectMonitorUsage.objmonusage003
|
||||
*/
|
||||
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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 "native_thread.cpp"
|
||||
#include "nsk_tools.cpp"
|
||||
#include "jni_tools.cpp"
|
||||
#include "jvmti_tools.cpp"
|
||||
#include "agent_tools.cpp"
|
||||
#include "jvmti_FollowRefObjects.cpp"
|
||||
#include "Injector.cpp"
|
||||
#include "JVMTITools.cpp"
|
||||
#include "agent_common.cpp"
|
||||
#include "objmonusage003.cpp"
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 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
|
||||
* 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 "agent_common.hpp"
|
||||
#include "JVMTITools.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
||||
#define PASSED 0
|
||||
#define STATUS_FAILED 2
|
||||
|
||||
static jvmtiEnv *jvmti = nullptr;
|
||||
static jvmtiCapabilities caps;
|
||||
static jint result = PASSED;
|
||||
static jboolean printdump = JNI_FALSE;
|
||||
static int count = 0;
|
||||
|
||||
#ifdef STATIC_BUILD
|
||||
JNIEXPORT jint JNICALL Agent_OnLoad_objmonusage003(JavaVM *jvm, char *options, void *reserved) {
|
||||
return Agent_Initialize(jvm, options, reserved);
|
||||
}
|
||||
JNIEXPORT jint JNICALL Agent_OnAttach_objmonusage003(JavaVM *jvm, char *options, void *reserved) {
|
||||
return Agent_Initialize(jvm, options, reserved);
|
||||
}
|
||||
JNIEXPORT jint JNI_OnLoad_objmonusage003(JavaVM *jvm, char *options, void *reserved) {
|
||||
return JNI_VERSION_1_8;
|
||||
}
|
||||
#endif
|
||||
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
|
||||
jint res;
|
||||
jvmtiError err;
|
||||
|
||||
if (options != nullptr && strcmp(options, "printdump") == 0) {
|
||||
printdump = JNI_TRUE;
|
||||
}
|
||||
|
||||
res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
|
||||
if (res != JNI_OK || jvmti == nullptr) {
|
||||
printf("Wrong result of a valid call to GetEnv !\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
err = jvmti->GetPotentialCapabilities(&caps);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
printf("(GetPotentialCapabilities) unexpected error: %s (%d)\n",
|
||||
TranslateError(err), err);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
err = jvmti->AddCapabilities(&caps);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
printf("(AddCapabilities) unexpected error: %s (%d)\n",
|
||||
TranslateError(err), err);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
err = jvmti->GetCapabilities(&caps);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
printf("(GetCapabilities) unexpected error: %s (%d)\n",
|
||||
TranslateError(err), err);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
if (!caps.can_get_monitor_info) {
|
||||
printf("Warning: GetObjectMonitorUsage is not implemented\n");
|
||||
}
|
||||
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_nsk_jvmti_GetObjectMonitorUsage_objmonusage003_check(JNIEnv *env,
|
||||
jclass cls, jobject obj, jthread owner,
|
||||
jint entryCount, jint waiterCount) {
|
||||
jvmtiError err;
|
||||
jvmtiMonitorUsage inf;
|
||||
jvmtiThreadInfo tinf;
|
||||
int j;
|
||||
|
||||
count++;
|
||||
|
||||
err = jvmti->GetObjectMonitorUsage(obj, &inf);
|
||||
if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
|
||||
!caps.can_get_monitor_info) {
|
||||
/* Ok, it's expected */
|
||||
return;
|
||||
} else if (err != JVMTI_ERROR_NONE) {
|
||||
printf("(GetMonitorInfo#%d) unexpected error: %s (%d)\n",
|
||||
count, TranslateError(err), err);
|
||||
result = STATUS_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (printdump == JNI_TRUE) {
|
||||
if (inf.owner == nullptr) {
|
||||
printf(">>> [%2d] owner: none (0x0)\n", count);
|
||||
} else {
|
||||
err = jvmti->GetThreadInfo(inf.owner, &tinf);
|
||||
printf(">>> [%2d] owner: %s (0x%p)\n",
|
||||
count, tinf.name, inf.owner);
|
||||
}
|
||||
printf(">>> entry_count: %d\n", inf.entry_count);
|
||||
printf(">>> waiter_count: %d\n", inf.waiter_count);
|
||||
if (inf.waiter_count > 0) {
|
||||
printf(">>> waiters:\n");
|
||||
for (j = 0; j < inf.waiter_count; j++) {
|
||||
err = jvmti->GetThreadInfo(inf.waiters[j], &tinf);
|
||||
printf(">>> %2d: %s (0x%p)\n",
|
||||
j, tinf.name, inf.waiters[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!env->IsSameObject(owner, inf.owner)) {
|
||||
printf("(%d) unexpected owner: 0x%p\n", count, inf.owner);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
|
||||
if (inf.entry_count != entryCount) {
|
||||
printf("(%d) entry_count expected: %d, actually: %d\n",
|
||||
count, entryCount, inf.entry_count);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
|
||||
if (inf.waiter_count != waiterCount) {
|
||||
printf("(%d) waiter_count expected: %d, actually: %d\n",
|
||||
count, waiterCount, inf.waiter_count);
|
||||
result = STATUS_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_nsk_jvmti_GetObjectMonitorUsage_objmonusage003_getRes(JNIEnv *env, jclass cls) {
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user