summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2017-09-14 09:51:14 -0700
committer Alex Light <allight@google.com> 2017-09-15 09:59:31 -0700
commit41006c6e8c0c5132a22bb7e100b6cd545dbb55a6 (patch)
tree3cdfe6aeed8f5e6bd7c865bdc3911191577af402
parent77fee87b262e969b29a9ac121a8bcbf87b68d9ce (diff)
Implement JVMTI GetCurrentContendedMonitor
Adds the JVMTI can_get_current_contended_monitor capability and implements all associated functions and behaviors. Test: ./test.py --host -j50 Bug: 62821960 Bug: 34415266 Change-Id: Ia3f19f0fbb21125bc85fb71f55e52ec61141c4ec
-rw-r--r--openjdkjvmti/OpenjdkJvmTi.cc6
-rw-r--r--openjdkjvmti/art_jvmti.h2
-rw-r--r--openjdkjvmti/ti_monitor.cc76
-rw-r--r--openjdkjvmti/ti_monitor.h2
-rw-r--r--test/1930-monitor-info/src/art/Monitors.java17
-rw-r--r--test/1930-monitor-info/src/art/Suspension.java30
-rw-r--r--test/1931-monitor-events/src/art/Monitors.java17
-rw-r--r--test/1931-monitor-events/src/art/Suspension.java30
-rw-r--r--test/1932-monitor-events-misc/src/art/Monitors.java17
-rw-r--r--test/1932-monitor-events-misc/src/art/Suspension.java30
-rw-r--r--test/1933-monitor-current-contended/expected.txt6
-rw-r--r--test/1933-monitor-current-contended/info.txt4
-rwxr-xr-xtest/1933-monitor-current-contended/run17
-rw-r--r--test/1933-monitor-current-contended/src/Main.java21
-rw-r--r--test/1933-monitor-current-contended/src/art/Monitors.java316
-rw-r--r--test/1933-monitor-current-contended/src/art/Suspension.java30
-rw-r--r--test/1933-monitor-current-contended/src/art/Test1933.java63
-rw-r--r--test/ti-agent/jvmti_helper.cc2
-rw-r--r--test/ti-agent/monitors_helper.cc7
19 files changed, 688 insertions, 5 deletions
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 277f611eb7..4339b2bdef 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -207,11 +207,11 @@ class JvmtiFunctions {
}
static jvmtiError GetCurrentContendedMonitor(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jobject* monitor_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jobject* monitor_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_get_current_contended_monitor);
- return ERR(NOT_IMPLEMENTED);
+ return MonitorUtil::GetCurrentContendedMonitor(env, thread, monitor_ptr);
}
static jvmtiError RunAgentThread(jvmtiEnv* env,
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 8e3cf21f19..10ddfc1fe4 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -225,7 +225,7 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_get_bytecodes = 1,
.can_get_synthetic_attribute = 1,
.can_get_owned_monitor_info = 1,
- .can_get_current_contended_monitor = 0,
+ .can_get_current_contended_monitor = 1,
.can_get_monitor_info = 1,
.can_pop_frame = 0,
.can_redefine_classes = 1,
diff --git a/openjdkjvmti/ti_monitor.cc b/openjdkjvmti/ti_monitor.cc
index adaa48c0cd..f92d81ef17 100644
--- a/openjdkjvmti/ti_monitor.cc
+++ b/openjdkjvmti/ti_monitor.cc
@@ -37,10 +37,13 @@
#include <mutex>
#include "art_jvmti.h"
+#include "monitor.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "ti_thread.h"
+#include "thread.h"
+#include "thread_pool.h"
namespace openjdkjvmti {
@@ -323,4 +326,77 @@ jvmtiError MonitorUtil::RawMonitorNotifyAll(jvmtiEnv* env ATTRIBUTE_UNUSED, jraw
return ERR(NONE);
}
+jvmtiError MonitorUtil::GetCurrentContendedMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jobject* monitor) {
+ if (monitor == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ struct GetContendedMonitorClosure : public art::Closure {
+ public:
+ explicit GetContendedMonitorClosure(art::Thread* current, jobject* out)
+ : result_thread_(current), out_(out) {}
+
+ void Run(art::Thread* target_thread) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ switch (target_thread->GetState()) {
+ // These three we are actually currently waiting on a monitor and have sent the appropriate
+ // events (if anyone is listening).
+ case art::kBlocked:
+ case art::kTimedWaiting:
+ case art::kWaiting: {
+ art::mirror::Object* mon = art::Monitor::GetContendedMonitor(target_thread);
+ *out_ = (mon == nullptr) ? nullptr
+ : result_thread_->GetJniEnv()->AddLocalReference<jobject>(mon);
+ return;
+ }
+ case art::kTerminated:
+ case art::kRunnable:
+ case art::kSleeping:
+ case art::kWaitingForLockInflation:
+ case art::kWaitingForTaskProcessor:
+ case art::kWaitingForGcToComplete:
+ case art::kWaitingForCheckPointsToRun:
+ case art::kWaitingPerformingGc:
+ case art::kWaitingForDebuggerSend:
+ case art::kWaitingForDebuggerToAttach:
+ case art::kWaitingInMainDebuggerLoop:
+ case art::kWaitingForDebuggerSuspension:
+ case art::kWaitingForJniOnLoad:
+ case art::kWaitingForSignalCatcherOutput:
+ case art::kWaitingInMainSignalCatcherLoop:
+ case art::kWaitingForDeoptimization:
+ case art::kWaitingForMethodTracingStart:
+ case art::kWaitingForVisitObjects:
+ case art::kWaitingForGetObjectsAllocated:
+ case art::kWaitingWeakGcRootRead:
+ case art::kWaitingForGcThreadFlip:
+ case art::kStarting:
+ case art::kNative:
+ case art::kSuspended: {
+ // We aren't currently (explicitly) waiting for a monitor anything so just return null.
+ *out_ = nullptr;
+ return;
+ }
+ }
+ }
+
+ private:
+ art::Thread* result_thread_;
+ jobject* out_;
+ };
+ GetContendedMonitorClosure closure(self, monitor);
+ target->RequestSynchronousCheckpoint(&closure);
+ return OK;
+}
+
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_monitor.h b/openjdkjvmti/ti_monitor.h
index add089c377..e0a865b9fa 100644
--- a/openjdkjvmti/ti_monitor.h
+++ b/openjdkjvmti/ti_monitor.h
@@ -52,6 +52,8 @@ class MonitorUtil {
static jvmtiError RawMonitorNotify(jvmtiEnv* env, jrawMonitorID monitor);
static jvmtiError RawMonitorNotifyAll(jvmtiEnv* env, jrawMonitorID monitor);
+
+ static jvmtiError GetCurrentContendedMonitor(jvmtiEnv* env, jthread thr, jobject* monitor);
};
} // namespace openjdkjvmti
diff --git a/test/1930-monitor-info/src/art/Monitors.java b/test/1930-monitor-info/src/art/Monitors.java
index f6a99fdae0..b28a3ee035 100644
--- a/test/1930-monitor-info/src/art/Monitors.java
+++ b/test/1930-monitor-info/src/art/Monitors.java
@@ -80,6 +80,7 @@ public class Monitors {
}
public static native MonitorUsage getObjectMonitorUsage(Object monitor);
+ public static native Object getCurrentContendedMonitor(Thread thr);
public static class TestException extends Error {
public TestException() { super(); }
@@ -132,6 +133,22 @@ public class Monitors {
}
}
+ public synchronized void suspendWorker() throws Exception {
+ checkException();
+ if (runner == null) {
+ throw new TestException("We don't have any runner holding " + lock);
+ }
+ Suspension.suspend(runner);
+ }
+
+ public Object getWorkerContendedMonitor() throws Exception {
+ checkException();
+ if (runner == null) {
+ return null;
+ }
+ return getCurrentContendedMonitor(runner);
+ }
+
public synchronized void DoLock() {
if (IsLocked()) {
throw new Error("lock is already acquired or being acquired.");
diff --git a/test/1930-monitor-info/src/art/Suspension.java b/test/1930-monitor-info/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1930-monitor-info/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1931-monitor-events/src/art/Monitors.java b/test/1931-monitor-events/src/art/Monitors.java
index f6a99fdae0..b28a3ee035 100644
--- a/test/1931-monitor-events/src/art/Monitors.java
+++ b/test/1931-monitor-events/src/art/Monitors.java
@@ -80,6 +80,7 @@ public class Monitors {
}
public static native MonitorUsage getObjectMonitorUsage(Object monitor);
+ public static native Object getCurrentContendedMonitor(Thread thr);
public static class TestException extends Error {
public TestException() { super(); }
@@ -132,6 +133,22 @@ public class Monitors {
}
}
+ public synchronized void suspendWorker() throws Exception {
+ checkException();
+ if (runner == null) {
+ throw new TestException("We don't have any runner holding " + lock);
+ }
+ Suspension.suspend(runner);
+ }
+
+ public Object getWorkerContendedMonitor() throws Exception {
+ checkException();
+ if (runner == null) {
+ return null;
+ }
+ return getCurrentContendedMonitor(runner);
+ }
+
public synchronized void DoLock() {
if (IsLocked()) {
throw new Error("lock is already acquired or being acquired.");
diff --git a/test/1931-monitor-events/src/art/Suspension.java b/test/1931-monitor-events/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1931-monitor-events/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1932-monitor-events-misc/src/art/Monitors.java b/test/1932-monitor-events-misc/src/art/Monitors.java
index f6a99fdae0..b28a3ee035 100644
--- a/test/1932-monitor-events-misc/src/art/Monitors.java
+++ b/test/1932-monitor-events-misc/src/art/Monitors.java
@@ -80,6 +80,7 @@ public class Monitors {
}
public static native MonitorUsage getObjectMonitorUsage(Object monitor);
+ public static native Object getCurrentContendedMonitor(Thread thr);
public static class TestException extends Error {
public TestException() { super(); }
@@ -132,6 +133,22 @@ public class Monitors {
}
}
+ public synchronized void suspendWorker() throws Exception {
+ checkException();
+ if (runner == null) {
+ throw new TestException("We don't have any runner holding " + lock);
+ }
+ Suspension.suspend(runner);
+ }
+
+ public Object getWorkerContendedMonitor() throws Exception {
+ checkException();
+ if (runner == null) {
+ return null;
+ }
+ return getCurrentContendedMonitor(runner);
+ }
+
public synchronized void DoLock() {
if (IsLocked()) {
throw new Error("lock is already acquired or being acquired.");
diff --git a/test/1932-monitor-events-misc/src/art/Suspension.java b/test/1932-monitor-events-misc/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1932-monitor-events-misc/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1933-monitor-current-contended/expected.txt b/test/1933-monitor-current-contended/expected.txt
new file mode 100644
index 0000000000..d27c21c115
--- /dev/null
+++ b/test/1933-monitor-current-contended/expected.txt
@@ -0,0 +1,6 @@
+No contention
+current thread is contending for monitor: null
+Normal contended monitor
+c2 is contending for monitor: NamedLock[test testNormalContendedMonitor]
+Waiting on a monitor
+c1 is contending for monitor: NamedLock[test testNormalWaitMonitor]
diff --git a/test/1933-monitor-current-contended/info.txt b/test/1933-monitor-current-contended/info.txt
new file mode 100644
index 0000000000..674ef56c70
--- /dev/null
+++ b/test/1933-monitor-current-contended/info.txt
@@ -0,0 +1,4 @@
+Tests jvmti monitor events in odd situations.
+
+Checks that the JVMTI monitor events are correctly dispatched and handled for
+many odd situations.
diff --git a/test/1933-monitor-current-contended/run b/test/1933-monitor-current-contended/run
new file mode 100755
index 0000000000..e92b873956
--- /dev/null
+++ b/test/1933-monitor-current-contended/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/1933-monitor-current-contended/src/Main.java b/test/1933-monitor-current-contended/src/Main.java
new file mode 100644
index 0000000000..3f2bbcd3a7
--- /dev/null
+++ b/test/1933-monitor-current-contended/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.Test1933.run();
+ }
+}
diff --git a/test/1933-monitor-current-contended/src/art/Monitors.java b/test/1933-monitor-current-contended/src/art/Monitors.java
new file mode 100644
index 0000000000..b28a3ee035
--- /dev/null
+++ b/test/1933-monitor-current-contended/src/art/Monitors.java
@@ -0,0 +1,316 @@
+/*
+ * 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.lang.reflect.Method;
+import java.util.concurrent.atomic.*;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.Arrays;
+import java.util.Objects;
+
+public class Monitors {
+ public native static void setupMonitorEvents(
+ Class<?> method_klass,
+ Method monitor_contended_enter_event,
+ Method monitor_contended_entered_event,
+ Method monitor_wait_event,
+ Method monitor_waited_event,
+ Class<?> lock_klass,
+ Thread thr);
+ public native static void stopMonitorEvents();
+
+ 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);
+ public static native Object getCurrentContendedMonitor(Thread thr);
+
+ public static class TestException extends Error {
+ public TestException() { super(); }
+ public TestException(String s) { super(s); }
+ public TestException(String s, Throwable c) { super(s, c); }
+ }
+
+ public static class LockController {
+ private static enum Action { HOLD, RELEASE, NOTIFY, NOTIFY_ALL, WAIT, TIMED_WAIT }
+
+ public final Object lock;
+ public final long timeout;
+ private final AtomicStampedReference<Action> action;
+ private volatile Thread runner = null;
+ private volatile boolean started = false;
+ private volatile boolean held = false;
+ private static final AtomicInteger cnt = new AtomicInteger(0);
+ private volatile Throwable exe;
+
+ public LockController(Object lock) {
+ this(lock, 10 * 1000);
+ }
+ public LockController(Object lock, long timeout) {
+ this.lock = lock;
+ this.timeout = timeout;
+ this.action = new AtomicStampedReference(Action.HOLD, 0);
+ this.exe = null;
+ }
+
+ public boolean IsWorkerThread(Thread thd) {
+ return Objects.equals(runner, thd);
+ }
+
+ public boolean IsLocked() {
+ checkException();
+ return held;
+ }
+
+ public void checkException() {
+ if (exe != null) {
+ throw new TestException("Exception thrown by other thread!", exe);
+ }
+ }
+
+ private void setAction(Action a) {
+ int stamp = action.getStamp();
+ // Wait for it to be HOLD before updating.
+ while (!action.compareAndSet(Action.HOLD, a, stamp, stamp + 1)) {
+ stamp = action.getStamp();
+ }
+ }
+
+ public synchronized void suspendWorker() throws Exception {
+ checkException();
+ if (runner == null) {
+ throw new TestException("We don't have any runner holding " + lock);
+ }
+ Suspension.suspend(runner);
+ }
+
+ public Object getWorkerContendedMonitor() throws Exception {
+ checkException();
+ if (runner == null) {
+ return null;
+ }
+ return getCurrentContendedMonitor(runner);
+ }
+
+ public synchronized void DoLock() {
+ if (IsLocked()) {
+ throw new Error("lock is already acquired or being acquired.");
+ }
+ if (runner != null) {
+ throw new Error("Already have thread!");
+ }
+ runner = new Thread(() -> {
+ started = true;
+ try {
+ synchronized (lock) {
+ held = true;
+ int[] stamp_h = new int[] { -1 };
+ Action cur_action = Action.HOLD;
+ try {
+ while (true) {
+ cur_action = action.get(stamp_h);
+ int stamp = stamp_h[0];
+ if (cur_action == Action.RELEASE) {
+ // The other thread will deal with reseting action.
+ break;
+ }
+ try {
+ switch (cur_action) {
+ case HOLD:
+ Thread.yield();
+ break;
+ case NOTIFY:
+ lock.notify();
+ break;
+ case NOTIFY_ALL:
+ lock.notifyAll();
+ break;
+ case TIMED_WAIT:
+ lock.wait(timeout);
+ break;
+ case WAIT:
+ lock.wait();
+ break;
+ default:
+ throw new Error("Unknown action " + action);
+ }
+ } finally {
+ // reset action back to hold if it isn't something else.
+ action.compareAndSet(cur_action, Action.HOLD, stamp, stamp+1);
+ }
+ }
+ } catch (Exception e) {
+ throw new TestException("Got an error while performing action " + cur_action, e);
+ }
+ }
+ } finally {
+ held = false;
+ started = false;
+ }
+ }, "Locker thread " + cnt.getAndIncrement() + " for " + lock);
+ // Make sure we can get any exceptions this throws.
+ runner.setUncaughtExceptionHandler((t, e) -> { exe = e; });
+ runner.start();
+ }
+
+ public void waitForLockToBeHeld() throws Exception {
+ while (true) {
+ if (IsLocked() && Objects.equals(runner, Monitors.getObjectMonitorUsage(lock).owner)) {
+ return;
+ }
+ }
+ }
+
+ public synchronized void waitForNotifySleep() throws Exception {
+ if (runner == null) {
+ throw new Error("No thread trying to lock!");
+ }
+ do {
+ checkException();
+ } while (!started ||
+ !Arrays.asList(Monitors.getObjectMonitorUsage(lock).notifyWaiters).contains(runner));
+ }
+
+ public synchronized void waitForContendedSleep() throws Exception {
+ if (runner == null) {
+ throw new Error("No thread trying to lock!");
+ }
+ do {
+ checkException();
+ } while (!started ||
+ runner.getState() != Thread.State.BLOCKED ||
+ !Arrays.asList(Monitors.getObjectMonitorUsage(lock).waiters).contains(runner));
+ }
+
+ public synchronized void DoNotify() {
+ if (!IsLocked()) {
+ throw new Error("Not locked");
+ }
+ setAction(Action.NOTIFY);
+ }
+
+ public synchronized void DoNotifyAll() {
+ if (!IsLocked()) {
+ throw new Error("Not locked");
+ }
+ setAction(Action.NOTIFY_ALL);
+ }
+
+ public synchronized void DoTimedWait() throws Exception {
+ if (!IsLocked()) {
+ throw new Error("Not locked");
+ }
+ setAction(Action.TIMED_WAIT);
+ }
+
+ public synchronized void DoWait() throws Exception {
+ if (!IsLocked()) {
+ throw new Error("Not locked");
+ }
+ setAction(Action.WAIT);
+ }
+
+ public synchronized void interruptWorker() throws Exception {
+ if (!IsLocked()) {
+ throw new Error("Not locked");
+ }
+ runner.interrupt();
+ }
+
+ public synchronized void waitForActionToFinish() throws Exception {
+ checkException();
+ while (action.getReference() != Action.HOLD) { checkException(); }
+ }
+
+ public synchronized void DoUnlock() throws Exception {
+ Error throwing = null;
+ if (!IsLocked()) {
+ // We might just be racing some exception that was thrown by the worker thread. Cache the
+ // exception, we will throw one from the worker before this one.
+ throwing = new Error("Not locked!");
+ }
+ setAction(Action.RELEASE);
+ Thread run = runner;
+ runner = null;
+ while (held) {}
+ run.join();
+ action.set(Action.HOLD, 0);
+ // Make sure to throw any exception that occurred since it might not have unlocked due to our
+ // request.
+ checkException();
+ DoCleanup();
+ if (throwing != null) {
+ throw throwing;
+ }
+ }
+
+ public synchronized void DoCleanup() throws Exception {
+ if (runner != null) {
+ Thread run = runner;
+ runner = null;
+ while (held) {}
+ run.join();
+ }
+ action.set(Action.HOLD, 0);
+ exe = null;
+ }
+ }
+}
+
diff --git a/test/1933-monitor-current-contended/src/art/Suspension.java b/test/1933-monitor-current-contended/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1933-monitor-current-contended/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1933-monitor-current-contended/src/art/Test1933.java b/test/1933-monitor-current-contended/src/art/Test1933.java
new file mode 100644
index 0000000000..e21c395196
--- /dev/null
+++ b/test/1933-monitor-current-contended/src/art/Test1933.java
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+public class Test1933 {
+ public static void run() throws Exception {
+ System.out.println("No contention");
+ testNoContention(new Monitors.NamedLock("test testNoContention"));
+
+ System.out.println("Normal contended monitor");
+ testNormalContendedMonitor(new Monitors.NamedLock("test testNormalContendedMonitor"));
+
+ System.out.println("Waiting on a monitor");
+ testNormalWaitMonitor(new Monitors.NamedLock("test testNormalWaitMonitor"));
+ }
+
+ public static void testNormalWaitMonitor(final Monitors.NamedLock lk) throws Exception {
+ final Monitors.LockController controller1 = new Monitors.LockController(lk);
+ controller1.DoLock();
+ controller1.waitForLockToBeHeld();
+ controller1.DoWait();
+ controller1.waitForNotifySleep();
+ System.out.println("c1 is contending for monitor: " + controller1.getWorkerContendedMonitor());
+ synchronized (lk) {
+ lk.notifyAll();
+ }
+ controller1.DoUnlock();
+ }
+
+ public static void testNormalContendedMonitor(final Monitors.NamedLock lk) throws Exception {
+ final Monitors.LockController controller1 = new Monitors.LockController(lk);
+ final Monitors.LockController controller2 = new Monitors.LockController(lk);
+ controller1.DoLock();
+ controller1.waitForLockToBeHeld();
+ controller2.DoLock();
+ controller2.waitForContendedSleep();
+ System.out.println("c2 is contending for monitor: " + controller2.getWorkerContendedMonitor());
+ controller1.DoUnlock();
+ controller2.waitForLockToBeHeld();
+ controller2.DoUnlock();
+ }
+
+ public static void testNoContention(final Monitors.NamedLock lk) throws Exception {
+ synchronized (lk) {
+ System.out.println("current thread is contending for monitor: " +
+ Monitors.getCurrentContendedMonitor(null));
+ }
+ }
+}
diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc
index c290e9b1ae..4ca2d5d9a0 100644
--- a/test/ti-agent/jvmti_helper.cc
+++ b/test/ti-agent/jvmti_helper.cc
@@ -49,7 +49,7 @@ static const jvmtiCapabilities standard_caps = {
.can_get_bytecodes = 1,
.can_get_synthetic_attribute = 1,
.can_get_owned_monitor_info = 0,
- .can_get_current_contended_monitor = 0,
+ .can_get_current_contended_monitor = 1,
.can_get_monitor_info = 1,
.can_pop_frame = 0,
.can_redefine_classes = 1,
diff --git a/test/ti-agent/monitors_helper.cc b/test/ti-agent/monitors_helper.cc
index 97d8427573..81d4cdc3ae 100644
--- a/test/ti-agent/monitors_helper.cc
+++ b/test/ti-agent/monitors_helper.cc
@@ -27,6 +27,13 @@
namespace art {
namespace common_monitors {
+extern "C" JNIEXPORT jobject JNICALL Java_art_Monitors_getCurrentContendedMonitor(
+ JNIEnv* env, jclass, jthread thr) {
+ jobject out = nullptr;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetCurrentContendedMonitor(thr, &out));
+ return out;
+}
+
extern "C" JNIEXPORT jobject JNICALL Java_art_Monitors_getObjectMonitorUsage(
JNIEnv* env, jclass, jobject obj) {
ScopedLocalRef<jclass> klass(env, env->FindClass("art/Monitors$MonitorUsage"));