Skip to content

Commit

Permalink
review: remove test objmonusage003; improve test ObjectMonitorUsage
Browse files Browse the repository at this point in the history
  • Loading branch information
sspitsyn committed Feb 22, 2024
1 parent e095cff commit ef77916
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 562 deletions.
1 change: 0 additions & 1 deletion test/hotspot/jtreg/TEST.quick-groups
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,24 @@
*
* @summary converted from VM Testbase nsk/jvmti/GetObjectMonitorUsage/objmonusage003
* DESCRIPTION
* The test checks if JVMTI function GetObjectMonitorUsage returns
* The test checks if the JVMTI function GetObjectMonitorUsage returns
* the expected values for the owner, entry_count, water_count
* fields of JVMTI_monitor_info. The tescases are the following:
* fields of JVMTI_monitor_info.
* The testcases are the following:
* - unowned object without any waitings
* - unowned object with waitings to be notified
* - owned object without any waitings
* - owned object with N waitings to enter the monitor
* - owned object with N waitings to be notified
* - owned object with N waitings to enter, from 0 to N waitings to re-enter,
* from N to 0 waitings to be notified
* - unowned object with waitings through Object.wait()
* - all the above is checked for both platform and virtual threads
* - all the above scenarios are executed with platform and virtual threads
* @requires vm.jvmti
* @compile ObjectMonitorUsage.java
* @run main/othervm/native -agentlib:ObjectMonitorUsage ObjectMonitorUsage
*/

public class ObjectMonitorUsage {

final static int JCK_STATUS_BASE = 95;
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;
Expand All @@ -57,19 +56,66 @@ public class ObjectMonitorUsage {
native static void check(Object obj, Thread owner,
int entryCount, int waiterCount, int notifyWaiterCount);

static Thread startTask(TestTask task, boolean isVirtual) {
Thread thread = isVirtual ? Thread.ofVirtual().start(task)
: Thread.ofPlatform().start(task);
static void log(String msg) {
System.out.println(msg);
}

static String vtag(boolean isVirtual) {
return isVirtual ? "virtual" : "platform";
}

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.waitReady();
return thread;
}

/* 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);

for (int i = 0; i < NUMBER_OF_WAITING_THREADS; i++) {
// the WaitingTask has to wait to be notified in a lockCheck.wait()
thr[i] = startTask(i, new WaitingTask(), isVirtual, "Waiting");
}
// 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_ENTERING_THREADS);

synchronized (lockCheck) {
lockCheck.notifyAll();
}
for (int i = 0; i < NUMBER_OF_WAITING_THREADS; i++) {
try {
thr[i].join();
} catch (InterruptedException e) {
throw new Error("Unexpected " + e);
}
}
log("###test0: finished " + vtag);
}

/* Scenario #1:
* - non-zero entering threads
* - zero re-entering threads
* - zero threads waiting to be notified
* - owning: 1
* - entering: N
* - re-entering: 0
* - to be notified: 0
*/
static void test1(boolean isVirtual) throws Error {
static void test1(boolean isVirtual) {
String vtag = vtag(isVirtual);
log("\n###test1: started " + vtag);

synchronized (lockCheck) {
// entry count: 1
// count of threads waiting to enter: 0
Expand All @@ -79,7 +125,7 @@ static void test1(boolean isVirtual) throws Error {

for (int i = 0; i < NUMBER_OF_ENTERING_THREADS; i++) {
// this EnteringTask has to be blocked on the lockCheck enter
thr[i] = startTask(new EnteringTask(), isVirtual);
thr[i] = startTask(i, new EnteringTask(), isVirtual, "Entering");
}
// entry count: 1
// count of threads waiting to enter: NUMBER_OF_ENTERING_THREADS
Expand All @@ -96,22 +142,27 @@ static void test1(boolean isVirtual) throws Error {
throw new Error("Unexpected " + e);
}
}
log("###test1: finished " + vtag);
}

/* Scenario #2:
* - non-zero entering threads
* - zero re-entering threads
* - non-zero waiting to be notified
* - owning: 1
* - entering: N
* - re-entering: 0
* - to be notified: N
*/
static void test2(boolean isVirtual) throws Error {
for (int i = NUMBER_OF_ENTERING_THREADS; i < NUMBER_OF_THREADS; i++) {
String vtag = vtag(isVirtual);
log("\n###test2: started " + vtag);

for (int i = 0; i < NUMBER_OF_WAITING_THREADS; i++) {
// the WaitingTask has to wait to be notified in a lockCheck.wait()
thr[i] = startTask(new WaitingTask(), isVirtual);
thr[i] = startTask(i, new WaitingTask(), isVirtual, "Waiting");
}
synchronized (lockCheck) {
for (int i = 0; i < NUMBER_OF_ENTERING_THREADS; i++) {
// this EnteringTask has to be blocked on the lockCheck enter
thr[i] = startTask(new EnteringTask(), isVirtual);
thr[NUMBER_OF_WAITING_THREADS + i] = startTask(i, new EnteringTask(), isVirtual, "Entering");
}
// entry count: 1
// count of threads waiting to enter: NUMBER_OF_ENTERING_THREADS
Expand All @@ -130,23 +181,28 @@ static void test2(boolean isVirtual) throws Error {
throw new Error("Unexpected " + e);
}
}
log("###test2: finished " + vtag);
}

/* Scenario #3:
* Initially we have:
* - zero entering threads
* - zero re-entering threads
* - non-zero threads waiting to be notified
* - 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 {
for (int i = NUMBER_OF_ENTERING_THREADS; i < NUMBER_OF_THREADS; i++) {
String vtag = vtag(isVirtual);
log("\n###test3: started " + vtag);

for (int i = 0; i < NUMBER_OF_WAITING_THREADS; i++) {
// the WaitingTask has to wait to be notified in a lockCheck.wait()
thr[i] = startTask(new WaitingTask(), isVirtual);
thr[i] = startTask(i, new WaitingTask(), isVirtual, "Waiting");
}
synchronized (lockCheck) {
// entry count: 1
Expand All @@ -159,7 +215,7 @@ static void test3(boolean isVirtual) throws Error {

for (int i = 0; i < NUMBER_OF_ENTERING_THREADS; i++) {
// this EnteringTask has to be blocked on the lockCheck enter
thr[i] = startTask(new EnteringTask(), isVirtual);
thr[NUMBER_OF_WAITING_THREADS + i] = startTask(i, new EnteringTask(), isVirtual, "Entering");
}

// entry count: 1
Expand Down Expand Up @@ -190,25 +246,30 @@ static void test3(boolean isVirtual) throws Error {
throw new Error("Unexpected " + e);
}
}
log("###test3: finished " + vtag);
}

public static void main(String args[]) {
log("\n###main: started\n");
check(lockCheck, null, 0, 0, 0);

// test platform threads
test0(false);
test1(false);
test2(false);
test3(false);

// test virtual threads
test1(false);
test2(false);
test3(false);
test0(true);
test1(true);
test2(true);
test3(true);

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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,12 @@ extern "C" {
static jvmtiEnv *jvmti = nullptr;
static jvmtiCapabilities caps;
static jint result = PASSED;
static jboolean printdump = JNI_FALSE;
static int count = 0;
static int check_idx = 0;

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) {
LOG("Wrong result of a valid call to GetEnv !\n");
Expand Down Expand Up @@ -90,72 +85,59 @@ Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
}

JNIEXPORT void JNICALL
Java_ObjectMonitorUsage_check(JNIEnv *env,
jclass cls, jobject obj, jthread owner,
Java_ObjectMonitorUsage_check(JNIEnv *jni, jclass cls, jobject obj, jthread owner,
jint entryCount, jint waiterCount, jint notifyWaiterCount) {
jvmtiError err;
jvmtiMonitorUsage inf;
jvmtiThreadInfo tinf;
int j;

count++;
check_idx++;

err = jvmti->GetObjectMonitorUsage(obj, &inf);
if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY && !caps.can_get_monitor_info) {
return; /* Ok, it's expected */
} else if (err != JVMTI_ERROR_NONE) {
LOG("(GetMonitorInfo#%d) unexpected error: %s (%d)\n",
count, TranslateError(err), err);
result = STATUS_FAILED;
return;
check_jvmti_status(jni, err, "error in JVMTI GetObjectMonitorUsage");

if (inf.owner == nullptr) {
LOG(">>> [%2d] owner: none (0x0)\n", check_idx);
} else {
err = jvmti->GetThreadInfo(inf.owner, &tinf);
check_jvmti_status(jni, err, "error in JVMTI GetThreadInfo");
LOG(">>> [%2d] owner: %s (0x%p)\n",
check_idx, tinf.name, inf.owner);
}

if (printdump == JNI_TRUE) {
if (inf.owner == nullptr) {
LOG(">>> [%2d] owner: none (0x0)\n", count);
} else {
err = jvmti->GetThreadInfo(inf.owner, &tinf);
LOG(">>> [%2d] owner: %s (0x%p)\n",
count, tinf.name, inf.owner);
}
LOG(">>> entry_count: %d\n", inf.entry_count);
LOG(">>> waiter_count: %d\n", inf.waiter_count);
if (inf.waiter_count > 0) {
LOG(">>> waiters:\n");
for (j = 0; j < inf.waiter_count; j++) {
err = jvmti->GetThreadInfo(inf.waiters[j], &tinf);
LOG(">>> %2d: %s (0x%p)\n",
j, tinf.name, inf.waiters[j]);
}
LOG(">>> entry_count: %d\n", inf.entry_count);
LOG(">>> waiter_count: %d\n", inf.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]);
}
}

if (!env->IsSameObject(owner, inf.owner)) {
LOG("(%d) unexpected owner: 0x%p\n", count, inf.owner);
if (!jni->IsSameObject(owner, inf.owner)) {
LOG("(%d) unexpected owner: 0x%p\n", check_idx, inf.owner);
result = STATUS_FAILED;
}

if (inf.entry_count != entryCount) {
LOG("(%d) entry_count expected: %d, actually: %d\n",
count, entryCount, inf.entry_count);
check_idx, entryCount, inf.entry_count);
result = STATUS_FAILED;
}

if (inf.waiter_count != waiterCount) {
LOG("(%d) waiter_count expected: %d, actually: %d\n",
count, waiterCount, inf.waiter_count);
check_idx, waiterCount, inf.waiter_count);
result = STATUS_FAILED;
}

if (inf.notify_waiter_count != notifyWaiterCount) {
LOG("(%d) notify_waiter_count expected: %d, actually: %d\n",
count, notifyWaiterCount, inf.notify_waiter_count);
check_idx, notifyWaiterCount, inf.notify_waiter_count);
result = STATUS_FAILED;
}
}

JNIEXPORT jint JNICALL
Java_ObjectMonitorUsage_getRes(JNIEnv *env, jclass cls) {
Java_ObjectMonitorUsage_getRes(JNIEnv *jni, jclass cls) {
return result;
}

Expand Down
Loading

0 comments on commit ef77916

Please sign in to comment.