8247972: incorrect implementation of JVM TI GetObjectMonitorUsage

Reviewed-by: dcubed, lmesnik
This commit is contained in:
Serguei Spitsyn 2024-03-12 08:55:28 +00:00
parent 139681a7eb
commit b92440f9b1
15 changed files with 790 additions and 439 deletions

View File

@ -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.

View File

@ -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) {

View File

@ -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);

View File

@ -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.")
)
)

View File

@ -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);

View File

@ -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 \

View File

@ -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);
}
}
}
}
}

View File

@ -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"

View File

@ -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);
}

View File

@ -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.

View File

@ -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

View File

@ -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);
}
}
}
}
}

View File

@ -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
*/

View File

@ -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"

View File

@ -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;
}
}