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
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 6c0d492..277f611 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -810,11 +810,11 @@
}
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 93eee28..d3f52f6 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -226,7 +226,7 @@
.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 2506aca..89ce352 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 @@
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 fa3bd0f..977ec39 100644
--- a/openjdkjvmti/ti_object.h
+++ b/openjdkjvmti/ti_object.h
@@ -42,6 +42,8 @@
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