diff options
author | 2017-09-14 09:51:14 -0700 | |
---|---|---|
committer | 2017-09-15 09:59:31 -0700 | |
commit | 41006c6e8c0c5132a22bb7e100b6cd545dbb55a6 (patch) | |
tree | 3cdfe6aeed8f5e6bd7c865bdc3911191577af402 | |
parent | 77fee87b262e969b29a9ac121a8bcbf87b68d9ce (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
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")); |