diff options
author | 2017-09-05 16:54:25 -0700 | |
---|---|---|
committer | 2017-09-06 17:50:27 -0700 | |
commit | ce56864b347983155a6b810c19eaa8297d77be96 (patch) | |
tree | c572198fee2fe2cc4b2291b7af124c5429e63f7e | |
parent | 82629c9182dffb823f05dec30f7ac72cf8fb3ba5 (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.cc | 6 | ||||
-rw-r--r-- | openjdkjvmti/art_jvmti.h | 2 | ||||
-rw-r--r-- | openjdkjvmti/ti_object.cc | 57 | ||||
-rw-r--r-- | openjdkjvmti/ti_object.h | 2 | ||||
-rw-r--r-- | runtime/monitor.cc | 10 | ||||
-rw-r--r-- | test/1930-monitor-info/expected.txt | 31 | ||||
-rw-r--r-- | test/1930-monitor-info/info.txt | 3 | ||||
-rw-r--r-- | test/1930-monitor-info/monitor.cc | 67 | ||||
-rwxr-xr-x | test/1930-monitor-info/run | 17 | ||||
-rw-r--r-- | test/1930-monitor-info/src/Main.java | 21 | ||||
-rw-r--r-- | test/1930-monitor-info/src/art/Monitors.java | 72 | ||||
-rw-r--r-- | test/1930-monitor-info/src/art/Test1930.java | 155 | ||||
-rw-r--r-- | test/Android.bp | 4 | ||||
-rw-r--r-- | test/ti-agent/jvmti_helper.cc | 2 | ||||
-rw-r--r-- | test/ti-agent/monitors_helper.cc | 62 |
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 |