summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2017-09-05 16:54:25 -0700
committer Alex Light <allight@google.com> 2017-09-06 17:50:27 -0700
commitce56864b347983155a6b810c19eaa8297d77be96 (patch)
treec572198fee2fe2cc4b2291b7af124c5429e63f7e
parent82629c9182dffb823f05dec30f7ac72cf8fb3ba5 (diff)
Add support for JVMTI GetObjectMonitorUsage function.
Adds support for the can_get_monitor_info capability and all associated functionality. Also fixes a minor bug where monitor info incorrectly said that an unlocked monitor had 1 entry in some cases. Test: ./test.py --host -j50 Test: art/tools/run-jdwp-tests --mode=host Bug: 62821960 Bug: 34409230 Change-Id: I9a4817ea309aaf94c56e9c0a694b88c93e7b629c
-rw-r--r--openjdkjvmti/OpenjdkJvmTi.cc6
-rw-r--r--openjdkjvmti/art_jvmti.h2
-rw-r--r--openjdkjvmti/ti_object.cc57
-rw-r--r--openjdkjvmti/ti_object.h2
-rw-r--r--runtime/monitor.cc10
-rw-r--r--test/1930-monitor-info/expected.txt31
-rw-r--r--test/1930-monitor-info/info.txt3
-rw-r--r--test/1930-monitor-info/monitor.cc67
-rwxr-xr-xtest/1930-monitor-info/run17
-rw-r--r--test/1930-monitor-info/src/Main.java21
-rw-r--r--test/1930-monitor-info/src/art/Monitors.java72
-rw-r--r--test/1930-monitor-info/src/art/Test1930.java155
-rw-r--r--test/Android.bp4
-rw-r--r--test/ti-agent/jvmti_helper.cc2
-rw-r--r--test/ti-agent/monitors_helper.cc62
15 files changed, 504 insertions, 7 deletions
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 6c0d492be6..277f611eb7 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -810,11 +810,11 @@ class JvmtiFunctions {
}
static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env,
- jobject object ATTRIBUTE_UNUSED,
- jvmtiMonitorUsage* info_ptr ATTRIBUTE_UNUSED) {
+ jobject object,
+ jvmtiMonitorUsage* info_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_get_monitor_info);
- return ERR(NOT_IMPLEMENTED);
+ return ObjectUtil::GetObjectMonitorUsage(env, object, info_ptr);
}
static jvmtiError GetFieldName(jvmtiEnv* env,
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 93eee28c0a..d3f52f638d 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -226,7 +226,7 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_get_synthetic_attribute = 1,
.can_get_owned_monitor_info = 1,
.can_get_current_contended_monitor = 0,
- .can_get_monitor_info = 0,
+ .can_get_monitor_info = 1,
.can_pop_frame = 0,
.can_redefine_classes = 1,
.can_signal_thread = 0,
diff --git a/openjdkjvmti/ti_object.cc b/openjdkjvmti/ti_object.cc
index 2506acac3a..89ce35256d 100644
--- a/openjdkjvmti/ti_object.cc
+++ b/openjdkjvmti/ti_object.cc
@@ -35,6 +35,8 @@
#include "mirror/object-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
+#include "thread_list.h"
+#include "ti_thread.h"
namespace openjdkjvmti {
@@ -73,4 +75,59 @@ jvmtiError ObjectUtil::GetObjectHashCode(jvmtiEnv* env ATTRIBUTE_UNUSED,
return ERR(NONE);
}
+jvmtiError ObjectUtil::GetObjectMonitorUsage(
+ jvmtiEnv* baseenv, jobject obj, jvmtiMonitorUsage* usage) {
+ ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(baseenv);
+ if (obj == nullptr) {
+ return ERR(INVALID_OBJECT);
+ }
+ if (usage == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ art::Thread* self = art::Thread::Current();
+ ThreadUtil::SuspendCheck(self);
+ art::JNIEnvExt* jni = self->GetJniEnv();
+ std::vector<jthread> wait;
+ std::vector<jthread> notify_wait;
+ {
+ art::ScopedObjectAccess soa(self); // Now we know we have the shared lock.
+ art::ScopedThreadSuspension sts(self, art::kNative);
+ art::ScopedSuspendAll ssa("GetObjectMonitorUsage", /*long_suspend*/false);
+ art::ObjPtr<art::mirror::Object> target(self->DecodeJObject(obj));
+ // This gets the list of threads trying to lock or wait on the monitor.
+ art::MonitorInfo info(target.Ptr());
+ usage->owner = info.owner_ != nullptr ?
+ jni->AddLocalReference<jthread>(info.owner_->GetPeerFromOtherThread()) : nullptr;
+ usage->entry_count = info.entry_count_;
+ for (art::Thread* thd : info.waiters_) {
+ // RI seems to consider waiting for notify to be included in those waiting to acquire the
+ // monitor. We will match this behavior.
+ notify_wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+ wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+ }
+ {
+ // Scan all threads to see which are waiting on this particular monitor.
+ art::MutexLock tll(self, *art::Locks::thread_list_lock_);
+ for (art::Thread* thd : art::Runtime::Current()->GetThreadList()->GetList()) {
+ if (thd != info.owner_ && target.Ptr() == thd->GetMonitorEnterObject()) {
+ wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+ }
+ }
+ }
+ }
+ usage->waiter_count = wait.size();
+ usage->notify_waiter_count = notify_wait.size();
+ jvmtiError ret = CopyDataIntoJvmtiBuffer(env,
+ reinterpret_cast<const unsigned char*>(wait.data()),
+ wait.size() * sizeof(jthread),
+ reinterpret_cast<unsigned char**>(&usage->waiters));
+ if (ret != OK) {
+ return ret;
+ }
+ return CopyDataIntoJvmtiBuffer(env,
+ reinterpret_cast<const unsigned char*>(notify_wait.data()),
+ notify_wait.size() * sizeof(jthread),
+ reinterpret_cast<unsigned char**>(&usage->notify_waiters));
+}
+
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_object.h b/openjdkjvmti/ti_object.h
index fa3bd0f51a..977ec3950f 100644
--- a/openjdkjvmti/ti_object.h
+++ b/openjdkjvmti/ti_object.h
@@ -42,6 +42,8 @@ class ObjectUtil {
static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr);
static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr);
+
+ static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env, jobject object, jvmtiMonitorUsage* usage);
};
} // namespace openjdkjvmti
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 5c63dcad78..80e6ad3d47 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1498,13 +1498,21 @@ MonitorInfo::MonitorInfo(mirror::Object* obj) : owner_(nullptr), entry_count_(0)
break;
case LockWord::kThinLocked:
owner_ = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner());
+ DCHECK(owner_ != nullptr) << "Thin-locked without owner!";
entry_count_ = 1 + lock_word.ThinLockCount();
// Thin locks have no waiters.
break;
case LockWord::kFatLocked: {
Monitor* mon = lock_word.FatLockMonitor();
owner_ = mon->owner_;
- entry_count_ = 1 + mon->lock_count_;
+ // Here it is okay for the owner to be null since we don't reset the LockWord back to
+ // kUnlocked until we get a GC. In cases where this hasn't happened yet we will have a fat
+ // lock without an owner.
+ if (owner_ != nullptr) {
+ entry_count_ = 1 + mon->lock_count_;
+ } else {
+ DCHECK_EQ(mon->lock_count_, 0) << "Monitor is fat-locked without any owner!";
+ }
for (Thread* waiter = mon->wait_set_; waiter != nullptr; waiter = waiter->GetWaitNext()) {
waiters_.push_back(waiter);
}
diff --git a/test/1930-monitor-info/expected.txt b/test/1930-monitor-info/expected.txt
new file mode 100644
index 0000000000..b43f1b20b6
--- /dev/null
+++ b/test/1930-monitor-info/expected.txt
@@ -0,0 +1,31 @@
+Running with single thread.
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Running with single thread in native.
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Lock twice
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice native
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice Java then native
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice native then Java
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+lock with wait
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: main, entryCount: 1, waiters: [Test1930 Thread - testLockWait], notify_waiters: [] }
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Wait for notify.
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: main, entryCount: 1, waiters: [Test1930 Thread - testLockWait], notify_waiters: [Test1930 Thread - testLockWait] }
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
diff --git a/test/1930-monitor-info/info.txt b/test/1930-monitor-info/info.txt
new file mode 100644
index 0000000000..8e19edce92
--- /dev/null
+++ b/test/1930-monitor-info/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetObjectMonitorUsage function works correctly.
diff --git a/test/1930-monitor-info/monitor.cc b/test/1930-monitor-info/monitor.cc
new file mode 100644
index 0000000000..7f97c05a73
--- /dev/null
+++ b/test/1930-monitor-info/monitor.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+
+#include <cstdio>
+#include <iostream>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "jvmti.h"
+
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1930MonitorInfo {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1930_executeLockedNative(JNIEnv* env,
+ jclass klass,
+ jobject run,
+ jobject l) {
+ ScopedLocalRef<jclass> runnable(env, env->FindClass("java/lang/Runnable"));
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ jmethodID method = env->GetMethodID(runnable.get(), "run", "()V");
+
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ jmethodID printMethod = env->GetStaticMethodID(klass, "printPreLock", "(Ljava/lang/Object;)V");
+ if (env->ExceptionCheck()) {
+ return;
+ }
+
+ env->CallStaticVoidMethod(klass, printMethod, l);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ if (env->MonitorEnter(l) != 0) {
+ return;
+ }
+ env->CallVoidMethod(run, method);
+ env->MonitorExit(l);
+}
+
+} // namespace Test1930MonitorInfo
+} // namespace art
diff --git a/test/1930-monitor-info/run b/test/1930-monitor-info/run
new file mode 100755
index 0000000000..e92b873956
--- /dev/null
+++ b/test/1930-monitor-info/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1930-monitor-info/src/Main.java b/test/1930-monitor-info/src/Main.java
new file mode 100644
index 0000000000..332846133b
--- /dev/null
+++ b/test/1930-monitor-info/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1930.run();
+ }
+}
diff --git a/test/1930-monitor-info/src/art/Monitors.java b/test/1930-monitor-info/src/art/Monitors.java
new file mode 100644
index 0000000000..26f7718dc5
--- /dev/null
+++ b/test/1930-monitor-info/src/art/Monitors.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+public class Monitors {
+ public static class NamedLock {
+ public final String name;
+ public NamedLock(String name) {
+ this.name = name;
+ }
+ public String toString() {
+ return String.format("NamedLock[%s]", name);
+ }
+ }
+
+ public static final class MonitorUsage {
+ public final Object monitor;
+ public final Thread owner;
+ public final int entryCount;
+ public final Thread[] waiters;
+ public final Thread[] notifyWaiters;
+
+ public MonitorUsage(
+ Object monitor,
+ Thread owner,
+ int entryCount,
+ Thread[] waiters,
+ Thread[] notifyWaiters) {
+ this.monitor = monitor;
+ this.entryCount = entryCount;
+ this.owner = owner;
+ this.waiters = waiters;
+ this.notifyWaiters = notifyWaiters;
+ }
+
+ private static String toNameList(Thread[] ts) {
+ return Arrays.toString(Arrays.stream(ts).map((Thread t) -> t.getName()).toArray());
+ }
+
+ public String toString() {
+ return String.format(
+ "MonitorUsage{ monitor: %s, owner: %s, entryCount: %d, waiters: %s, notify_waiters: %s }",
+ monitor,
+ (owner != null) ? owner.getName() : "<NULL>",
+ entryCount,
+ toNameList(waiters),
+ toNameList(notifyWaiters));
+ }
+ }
+
+ public static native MonitorUsage getObjectMonitorUsage(Object monitor);
+}
+
diff --git a/test/1930-monitor-info/src/art/Test1930.java b/test/1930-monitor-info/src/art/Test1930.java
new file mode 100644
index 0000000000..a7fa1c78cb
--- /dev/null
+++ b/test/1930-monitor-info/src/art/Test1930.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+
+public class Test1930 {
+ public static final int NUM_RETRY = 100;
+ private static void testSingleThread() {
+ Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testSingleThread");
+ executeLocked(() -> { printMonitorUsage(lk); }, lk);
+ }
+ private static void testSingleThreadNative() {
+ Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testSingleThread");
+ executeLockedNative(() -> { printMonitorUsage(lk); }, lk);
+ }
+
+ private static void testLockedTwice() {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwice");
+ executeLocked(() -> { executeLocked(() -> { printMonitorUsage(lk); }, lk); }, lk);
+ }
+
+ private static void testLockedTwiceNJ() {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceNJ");
+ executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+ }
+
+ private static void testLockedTwiceJN() {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceJN");
+ executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+ }
+
+ private static void testLockedTwiceNative() {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceNative");
+ executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+ }
+
+ public final static class ThreadSignaler {
+ public volatile boolean signal = false;
+ }
+
+ private static void testLockWait() throws Exception {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockWait");
+ final Semaphore sem = new Semaphore(0);
+ final Thread t = new Thread(() -> {
+ sem.release();
+ synchronized (lk) {
+ printMonitorUsage(lk);
+ }
+ }, "Test1930 Thread - testLockWait");
+ synchronized (lk) {
+ t.start();
+ // Wait for the other thread to actually start.
+ sem.acquire();
+ // Wait for the other thread to go to sleep trying to get the mutex. This might take a (short)
+ // time since we try spinning first for better performance.
+ boolean found_wait = false;
+ for (long i = 0; i < NUM_RETRY; i++) {
+ if (Arrays.asList(Monitors.getObjectMonitorUsage(lk).waiters).contains(t)) {
+ found_wait = true;
+ break;
+ } else {
+ Thread.sleep(500);
+ Thread.yield();
+ }
+ }
+ if (!found_wait) {
+ System.out.println("other thread doesn't seem to be waiting.");
+ }
+ printMonitorUsage(lk);
+ }
+ t.join();
+ printMonitorUsage(lk);
+ }
+
+ private static void testNotifyWait() throws Exception {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testNotifyWait");
+ final Semaphore sem = new Semaphore(0);
+ Thread t = new Thread(() -> {
+ synchronized (lk) {
+ printMonitorUsage(lk);
+ sem.release();
+ try {
+ lk.wait();
+ } catch (Exception e) {
+ throw new Error("Error waiting!", e);
+ }
+ printMonitorUsage(lk);
+ }
+ }, "Test1930 Thread - testLockWait");
+ t.start();
+ sem.acquire();
+ synchronized (lk) {
+ printMonitorUsage(lk);
+ lk.notifyAll();
+ }
+ t.join();
+ printMonitorUsage(lk);
+ }
+
+ public static void run() throws Exception {
+ // Single threaded tests.
+ System.out.println("Running with single thread.");
+ testSingleThread();
+ System.out.println("Running with single thread in native.");
+ testSingleThreadNative();
+ System.out.println("Lock twice");
+ testLockedTwice();
+ System.out.println("Lock twice native");
+ testLockedTwiceNative();
+ System.out.println("Lock twice Java then native");
+ testLockedTwiceJN();
+ System.out.println("Lock twice native then Java");
+ testLockedTwiceNJ();
+
+ // Mutli threaded tests.
+ System.out.println("lock with wait");
+ testLockWait();
+ System.out.println("Wait for notify.");
+ testNotifyWait();
+ }
+
+ public static void printPreLock(Object lock) {
+ System.out.println(String.format("Pre-lock[%s]: %s",
+ Thread.currentThread().getName(), Monitors.getObjectMonitorUsage(lock)));
+ }
+
+ public static void executeLocked(Runnable r, Object lock) {
+ printPreLock(lock);
+ synchronized (lock) {
+ r.run();
+ }
+ }
+
+ public native static void executeLockedNative(Runnable r, Object m);
+ public static void printMonitorUsage(Object m) {
+ System.out.println(String.format("Thread[%s]: %s",
+ Thread.currentThread().getName(), Monitors.getObjectMonitorUsage(m)));
+ }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 2a88af11d6..d019df8642 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -250,6 +250,7 @@ art_cc_defaults {
"ti-agent/common_helper.cc",
"ti-agent/frame_pop_helper.cc",
"ti-agent/locals_helper.cc",
+ "ti-agent/monitors_helper.cc",
"ti-agent/redefinition_helper.cc",
"ti-agent/suspension_helper.cc",
"ti-agent/stack_trace_helper.cc",
@@ -299,7 +300,8 @@ art_cc_defaults {
"1922-owned-monitors-info/owned_monitors.cc",
"1924-frame-pop-toggle/frame_pop_toggle.cc",
"1926-missed-frame-pop/frame_pop_missed.cc",
- "1927-exception-event/exception_event.cc"
+ "1927-exception-event/exception_event.cc",
+ "1930-monitor-info/monitor.cc",
],
shared_libs: [
"libbase",
diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc
index 7280102c6f..c290e9b1ae 100644
--- a/test/ti-agent/jvmti_helper.cc
+++ b/test/ti-agent/jvmti_helper.cc
@@ -50,7 +50,7 @@ static const jvmtiCapabilities standard_caps = {
.can_get_synthetic_attribute = 1,
.can_get_owned_monitor_info = 0,
.can_get_current_contended_monitor = 0,
- .can_get_monitor_info = 0,
+ .can_get_monitor_info = 1,
.can_pop_frame = 0,
.can_redefine_classes = 1,
.can_signal_thread = 0,
diff --git a/test/ti-agent/monitors_helper.cc b/test/ti-agent/monitors_helper.cc
new file mode 100644
index 0000000000..7c28edecd2
--- /dev/null
+++ b/test/ti-agent/monitors_helper.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "jni.h"
+#include "jvmti.h"
+#include <vector>
+#include "jvmti_helper.h"
+#include "jni_helper.h"
+#include "test_env.h"
+#include "scoped_local_ref.h"
+namespace art {
+namespace common_monitors {
+
+extern "C" JNIEXPORT jobject JNICALL Java_art_Monitors_getObjectMonitorUsage(
+ JNIEnv* env, jclass, jobject obj) {
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/Monitors$MonitorUsage"));
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jmethodID constructor = env->GetMethodID(
+ klass.get(),
+ "<init>",
+ "(Ljava/lang/Object;Ljava/lang/Thread;I[Ljava/lang/Thread;[Ljava/lang/Thread;)V");
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jvmtiMonitorUsage usage;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetObjectMonitorUsage(obj, &usage))) {
+ return nullptr;
+ }
+ jobjectArray wait = CreateObjectArray(env, usage.waiter_count, "java/lang/Thread",
+ [&](jint i) { return usage.waiters[i]; });
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.waiters));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.notify_waiters));
+ return nullptr;
+ }
+ jobjectArray notify_wait = CreateObjectArray(env, usage.notify_waiter_count, "java/lang/Thread",
+ [&](jint i) { return usage.notify_waiters[i]; });
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.waiters));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.notify_waiters));
+ return nullptr;
+ }
+ return env->NewObject(klass.get(), constructor,
+ obj, usage.owner, usage.entry_count, wait, notify_wait);
+}
+
+} // namespace common_monitors
+} // namespace art