Add support for JVMTI monitor events.
Adds support for the JVMTI can_generate_monitor_events capability and
all associated events. This adds support for the
JVMTI_EVENT_MONITOR_WAIT, JVMTI_EVENT_MONITOR_WAITED,
JVMTI_EVENT_MONITOR_CONTENDED_ENTER, and
JVMTI_EVENT_MONITOR_CONTENDED_ENTERED events.
Bug: 65558434
Bug: 62821960
Bug: 34415266
Test: ./test.py --host -j50
Change-Id: I0fe8038e6c4249e77d37a67e5056b5d2a94b6f48
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index c41e15e..5911a03 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -31,6 +31,8 @@
#include "events-inl.h"
+#include <array>
+
#include "art_field-inl.h"
#include "art_jvmti.h"
#include "art_method-inl.h"
@@ -45,6 +47,7 @@
#include "jni_internal.h"
#include "mirror/class.h"
#include "mirror/object-inl.h"
+#include "monitor.h"
#include "nativehelper/ScopedLocalRef.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
@@ -247,6 +250,127 @@
}
}
+template<typename Type>
+static Type AddLocalRef(art::JNIEnvExt* e, art::mirror::Object* obj)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ return (obj == nullptr) ? nullptr : e->AddLocalReference<Type>(obj);
+}
+
+template<ArtJvmtiEvent kEvent, typename ...Args>
+static void RunEventCallback(EventHandler* handler,
+ art::Thread* self,
+ art::JNIEnvExt* jnienv,
+ Args... args)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ ScopedLocalRef<jthread> thread_jni(jnienv, AddLocalRef<jthread>(jnienv, self->GetPeer()));
+ art::StackHandleScope<1> hs(self);
+ art::Handle<art::mirror::Throwable> old_exception(hs.NewHandle(self->GetException()));
+ self->ClearException();
+ // Just give the event a good sized JNI frame. 100 should be fine.
+ jnienv->PushFrame(100);
+ {
+ // Need to do trampoline! :(
+ art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
+ handler->DispatchEvent<kEvent>(self,
+ static_cast<JNIEnv*>(jnienv),
+ thread_jni.get(),
+ args...);
+ }
+ jnienv->PopFrame();
+ if (!self->IsExceptionPending() && !old_exception.IsNull()) {
+ self->SetException(old_exception.Get());
+ }
+}
+
+class JvmtiMonitorListener : public art::MonitorCallback {
+ public:
+ explicit JvmtiMonitorListener(EventHandler* handler) : handler_(handler) {}
+
+ void MonitorContendedLocking(art::Monitor* m)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorContendedEnter)) {
+ art::Thread* self = art::Thread::Current();
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ ScopedLocalRef<jobject> mon(jnienv, AddLocalRef<jobject>(jnienv, m->GetObject()));
+ RunEventCallback<ArtJvmtiEvent::kMonitorContendedEnter>(
+ handler_,
+ self,
+ jnienv,
+ mon.get());
+ }
+ }
+
+ void MonitorContendedLocked(art::Monitor* m)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorContendedEntered)) {
+ art::Thread* self = art::Thread::Current();
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ ScopedLocalRef<jobject> mon(jnienv, AddLocalRef<jobject>(jnienv, m->GetObject()));
+ RunEventCallback<ArtJvmtiEvent::kMonitorContendedEntered>(
+ handler_,
+ self,
+ jnienv,
+ mon.get());
+ }
+ }
+
+ void ObjectWaitStart(art::Handle<art::mirror::Object> obj, int64_t timeout)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWait)) {
+ art::Thread* self = art::Thread::Current();
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ ScopedLocalRef<jobject> mon(jnienv, AddLocalRef<jobject>(jnienv, obj.Get()));
+ RunEventCallback<ArtJvmtiEvent::kMonitorWait>(
+ handler_,
+ self,
+ jnienv,
+ mon.get(),
+ static_cast<jlong>(timeout));
+ }
+ }
+
+
+ // Our interpretation of the spec is that the JVMTI_EVENT_MONITOR_WAITED will be sent immediately
+ // after a thread has woken up from a sleep caused by a call to Object#wait. If the thread will
+ // never go to sleep (due to not having the lock, having bad arguments, or having an exception
+ // propogated from JVMTI_EVENT_MONITOR_WAIT) we will not send this event.
+ //
+ // This does not fully match the RI semantics. Specifically, we will not send the
+ // JVMTI_EVENT_MONITOR_WAITED event in one situation where the RI would, there was an exception in
+ // the JVMTI_EVENT_MONITOR_WAIT event but otherwise the call was fine. In that case the RI would
+ // send this event and return without going to sleep.
+ //
+ // See b/65558434 for more discussion.
+ void MonitorWaitFinished(art::Monitor* m, bool timeout)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWaited)) {
+ art::Thread* self = art::Thread::Current();
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ ScopedLocalRef<jobject> mon(jnienv, AddLocalRef<jobject>(jnienv, m->GetObject()));
+ RunEventCallback<ArtJvmtiEvent::kMonitorWaited>(
+ handler_,
+ self,
+ jnienv,
+ mon.get(),
+ static_cast<jboolean>(timeout));
+ }
+ }
+
+ private:
+ EventHandler* handler_;
+};
+
+static void SetupMonitorListener(art::MonitorCallback* listener, bool enable) {
+ // We must not hold the mutator lock here, but if we're in FastJNI, for example, we might. For
+ // now, do a workaround: (possibly) acquire and release.
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ if (enable) {
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddMonitorCallback(listener);
+ } else {
+ art::Runtime::Current()->GetRuntimeCallbacks()->RemoveMonitorCallback(listener);
+ }
+}
+
// Report GC pauses (see spec) as GARBAGE_COLLECTION_START and GARBAGE_COLLECTION_END.
class JvmtiGcPauseListener : public art::gc::GcPauseListener {
public:
@@ -301,33 +425,10 @@
}
}
-template<typename Type>
-static Type AddLocalRef(art::JNIEnvExt* e, art::mirror::Object* obj)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- return (obj == nullptr) ? nullptr : e->AddLocalReference<Type>(obj);
-}
-
class JvmtiMethodTraceListener FINAL : public art::instrumentation::InstrumentationListener {
public:
explicit JvmtiMethodTraceListener(EventHandler* handler) : event_handler_(handler) {}
- template<ArtJvmtiEvent kEvent, typename ...Args>
- void RunEventCallback(art::Thread* self, art::JNIEnvExt* jnienv, Args... args)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- ScopedLocalRef<jthread> thread_jni(jnienv, AddLocalRef<jthread>(jnienv, self->GetPeer()));
- // Just give the event a good sized JNI frame. 100 should be fine.
- jnienv->PushFrame(100);
- {
- // Need to do trampoline! :(
- art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
- event_handler_->DispatchEvent<kEvent>(self,
- static_cast<JNIEnv*>(jnienv),
- thread_jni.get(),
- args...);
- }
- jnienv->PopFrame();
- }
-
// Call-back for when a method is entered.
void MethodEntered(art::Thread* self,
art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
@@ -337,7 +438,8 @@
if (!method->IsRuntimeMethod() &&
event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMethodEntry)) {
art::JNIEnvExt* jnienv = self->GetJniEnv();
- RunEventCallback<ArtJvmtiEvent::kMethodEntry>(self,
+ RunEventCallback<ArtJvmtiEvent::kMethodEntry>(event_handler_,
+ self,
jnienv,
art::jni::EncodeArtMethod(method));
}
@@ -360,6 +462,7 @@
ScopedLocalRef<jobject> return_jobj(jnienv, AddLocalRef<jobject>(jnienv, return_value.Get()));
val.l = return_jobj.get();
RunEventCallback<ArtJvmtiEvent::kMethodExit>(
+ event_handler_,
self,
jnienv,
art::jni::EncodeArtMethod(method),
@@ -386,6 +489,7 @@
// the union.
val.j = return_value.GetJ();
RunEventCallback<ArtJvmtiEvent::kMethodExit>(
+ event_handler_,
self,
jnienv,
art::jni::EncodeArtMethod(method),
@@ -412,6 +516,7 @@
CHECK(!old_exception.IsNull());
self->ClearException();
RunEventCallback<ArtJvmtiEvent::kMethodExit>(
+ event_handler_,
self,
jnienv,
art::jni::EncodeArtMethod(method),
@@ -442,11 +547,11 @@
jlocation location = static_cast<jlocation>(new_dex_pc);
// Step event is reported first according to the spec.
if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kSingleStep)) {
- RunEventCallback<ArtJvmtiEvent::kSingleStep>(self, jnienv, jmethod, location);
+ RunEventCallback<ArtJvmtiEvent::kSingleStep>(event_handler_, self, jnienv, jmethod, location);
}
// Next we do the Breakpoint events. The Dispatch code will filter the individual
if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kBreakpoint)) {
- RunEventCallback<ArtJvmtiEvent::kBreakpoint>(self, jnienv, jmethod, location);
+ RunEventCallback<ArtJvmtiEvent::kBreakpoint>(event_handler_, self, jnienv, jmethod, location);
}
}
@@ -464,7 +569,8 @@
ScopedLocalRef<jobject> fklass(jnienv,
AddLocalRef<jobject>(jnienv,
field->GetDeclaringClass().Ptr()));
- RunEventCallback<ArtJvmtiEvent::kFieldAccess>(self,
+ RunEventCallback<ArtJvmtiEvent::kFieldAccess>(event_handler_,
+ self,
jnienv,
art::jni::EncodeArtMethod(method),
static_cast<jlocation>(dex_pc),
@@ -492,6 +598,7 @@
jvalue val;
val.l = fval.get();
RunEventCallback<ArtJvmtiEvent::kFieldModification>(
+ event_handler_,
self,
jnienv,
art::jni::EncodeArtMethod(method),
@@ -525,6 +632,7 @@
// the union.
val.j = field_value.GetJ();
RunEventCallback<ArtJvmtiEvent::kFieldModification>(
+ event_handler_,
self,
jnienv,
art::jni::EncodeArtMethod(method),
@@ -545,6 +653,7 @@
art::JNIEnvExt* jnienv = self->GetJniEnv();
jboolean is_exception_pending = self->IsExceptionPending();
RunEventCallback<ArtJvmtiEvent::kFramePop>(
+ event_handler_,
self,
jnienv,
art::jni::EncodeArtMethod(frame.GetMethod()),
@@ -639,6 +748,7 @@
ScopedLocalRef<jobject> exception(jnienv,
AddLocalRef<jobject>(jnienv, exception_object.Get()));
RunEventCallback<ArtJvmtiEvent::kException>(
+ event_handler_,
self,
jnienv,
art::jni::EncodeArtMethod(method),
@@ -664,6 +774,7 @@
ScopedLocalRef<jobject> exception(jnienv,
AddLocalRef<jobject>(jnienv, exception_object.Get()));
RunEventCallback<ArtJvmtiEvent::kExceptionCatch>(
+ event_handler_,
self,
jnienv,
art::jni::EncodeArtMethod(method),
@@ -759,6 +870,23 @@
instr->DeoptimizeEverything("jvmti-local-variable-access");
}
+bool EventHandler::OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event) {
+ std::array<ArtJvmtiEvent, 4> events {
+ {
+ ArtJvmtiEvent::kMonitorContendedEnter,
+ ArtJvmtiEvent::kMonitorContendedEntered,
+ ArtJvmtiEvent::kMonitorWait,
+ ArtJvmtiEvent::kMonitorWaited
+ }
+ };
+ for (ArtJvmtiEvent e : events) {
+ if (e != event && IsEventEnabledAnywhere(e)) {
+ return true;
+ }
+ }
+ return false;
+}
+
// Handle special work for the given event type, if necessary.
void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
switch (event) {
@@ -799,7 +927,14 @@
case ArtJvmtiEvent::kExceptionCatch:
SetupTraceListener(method_trace_listener_.get(), event, enable);
return;
-
+ case ArtJvmtiEvent::kMonitorContendedEnter:
+ case ArtJvmtiEvent::kMonitorContendedEntered:
+ case ArtJvmtiEvent::kMonitorWait:
+ case ArtJvmtiEvent::kMonitorWaited:
+ if (!OtherMonitorEventsEnabledAnywhere(event)) {
+ SetupMonitorListener(monitor_listener_.get(), enable);
+ }
+ return;
default:
break;
}
@@ -928,6 +1063,7 @@
alloc_listener_.reset(new JvmtiAllocationListener(this));
gc_pause_listener_.reset(new JvmtiGcPauseListener(this));
method_trace_listener_.reset(new JvmtiMethodTraceListener(this));
+ monitor_listener_.reset(new JvmtiMonitorListener(this));
}
EventHandler::~EventHandler() {