diff options
Diffstat (limited to 'openjdkjvmti/events.cc')
| -rw-r--r-- | openjdkjvmti/events.cc | 221 |
1 files changed, 167 insertions, 54 deletions
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 00c81e2b3b..32dd69e0e6 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -31,10 +31,13 @@ #include "events-inl.h" +#include <array> + #include "art_field-inl.h" #include "art_jvmti.h" #include "art_method-inl.h" #include "base/logging.h" +#include "dex_file_types.h" #include "gc/allocation_listener.h" #include "gc/gc_pause_listener.h" #include "gc/heap.h" @@ -45,6 +48,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" @@ -189,6 +193,25 @@ static bool IsThreadControllable(ArtJvmtiEvent event) { } } +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())); + handler->DispatchEvent<kEvent>(self, + static_cast<JNIEnv*>(jnienv), + thread_jni.get(), + args...); +} + class JvmtiAllocationListener : public art::gc::AllocationListener { public: explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {} @@ -208,26 +231,17 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { // jclass object_klass, // jlong size art::JNIEnvExt* jni_env = self->GetJniEnv(); - - jthread thread_peer; - if (self->IsStillStarting()) { - thread_peer = nullptr; - } else { - thread_peer = jni_env->AddLocalReference<jthread>(self->GetPeer()); - } - - ScopedLocalRef<jthread> thread(jni_env, thread_peer); ScopedLocalRef<jobject> object( jni_env, jni_env->AddLocalReference<jobject>(*obj)); ScopedLocalRef<jclass> klass( jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass())); - handler_->DispatchEvent<ArtJvmtiEvent::kVmObjectAlloc>(self, - reinterpret_cast<JNIEnv*>(jni_env), - thread.get(), - object.get(), - klass.get(), - static_cast<jlong>(byte_count)); + RunEventCallback<ArtJvmtiEvent::kVmObjectAlloc>(handler_, + self, + jni_env, + object.get(), + klass.get(), + static_cast<jlong>(byte_count)); } } @@ -247,6 +261,95 @@ static void SetupObjectAllocationTracking(art::gc::AllocationListener* listener, } } +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 +404,10 @@ static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, ArtJvmtiEvent e } } -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 +417,8 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat 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 +441,7 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat 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 +468,7 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat // the union. val.j = return_value.GetJ(); RunEventCallback<ArtJvmtiEvent::kMethodExit>( + event_handler_, self, jnienv, art::jni::EncodeArtMethod(method), @@ -412,6 +495,7 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat CHECK(!old_exception.IsNull()); self->ClearException(); RunEventCallback<ArtJvmtiEvent::kMethodExit>( + event_handler_, self, jnienv, art::jni::EncodeArtMethod(method), @@ -442,11 +526,11 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat 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 +548,8 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat 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 +577,7 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat jvalue val; val.l = fval.get(); RunEventCallback<ArtJvmtiEvent::kFieldModification>( + event_handler_, self, jnienv, art::jni::EncodeArtMethod(method), @@ -525,6 +611,7 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat // the union. val.j = field_value.GetJ(); RunEventCallback<ArtJvmtiEvent::kFieldModification>( + event_handler_, self, jnienv, art::jni::EncodeArtMethod(method), @@ -541,16 +628,15 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat void WatchedFramePop(art::Thread* self, const art::ShadowFrame& frame) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { - if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kFramePop)) { art::JNIEnvExt* jnienv = self->GetJniEnv(); - jboolean is_exception_pending = self->IsExceptionPending(); - RunEventCallback<ArtJvmtiEvent::kFramePop>( - self, - jnienv, - art::jni::EncodeArtMethod(frame.GetMethod()), - is_exception_pending, - &frame); - } + jboolean is_exception_pending = self->IsExceptionPending(); + RunEventCallback<ArtJvmtiEvent::kFramePop>( + event_handler_, + self, + jnienv, + art::jni::EncodeArtMethod(frame.GetMethod()), + is_exception_pending, + &frame); } static void FindCatchMethodsFromThrow(art::Thread* self, @@ -583,14 +669,14 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat if (!method->IsNative()) { uint32_t cur_dex_pc = GetDexPc(); - if (cur_dex_pc == art::DexFile::kDexNoIndex) { + if (cur_dex_pc == art::dex::kDexNoIndex) { // This frame looks opaque. Just keep on going. return true; } bool has_no_move_exception = false; uint32_t found_dex_pc = method->FindCatchBlock( exception_class_, cur_dex_pc, &has_no_move_exception); - if (found_dex_pc != art::DexFile::kDexNoIndex) { + if (found_dex_pc != art::dex::kDexNoIndex) { // We found the catch. Store the result and return. *catch_method_ptr_ = method; *catch_dex_pc_ptr_ = found_dex_pc; @@ -639,6 +725,7 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat ScopedLocalRef<jobject> exception(jnienv, AddLocalRef<jobject>(jnienv, exception_object.Get())); RunEventCallback<ArtJvmtiEvent::kException>( + event_handler_, self, jnienv, art::jni::EncodeArtMethod(method), @@ -664,6 +751,7 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat ScopedLocalRef<jobject> exception(jnienv, AddLocalRef<jobject>(jnienv, exception_object.Get())); RunEventCallback<ArtJvmtiEvent::kExceptionCatch>( + event_handler_, self, jnienv, art::jni::EncodeArtMethod(method), @@ -774,6 +862,23 @@ void EventHandler::HandleLocalAccessCapabilityAdded() { art::Runtime::Current()->GetClassLinker()->VisitClasses(&visitor); } +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) { @@ -814,7 +919,14 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { 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; } @@ -943,6 +1055,7 @@ EventHandler::EventHandler() { 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() { |