summaryrefslogtreecommitdiff
path: root/openjdkjvmti/events.cc
diff options
context:
space:
mode:
Diffstat (limited to 'openjdkjvmti/events.cc')
-rw-r--r--openjdkjvmti/events.cc100
1 files changed, 96 insertions, 4 deletions
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 48df53a143..a96436e95a 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -32,6 +32,7 @@
#include "events-inl.h"
#include <array>
+#include <sys/time.h>
#include "art_field-inl.h"
#include "art_jvmti.h"
@@ -56,6 +57,7 @@
#include "thread-inl.h"
#include "thread_list.h"
#include "ti_phase.h"
+#include "well_known_classes.h"
namespace openjdkjvmti {
@@ -410,14 +412,103 @@ class JvmtiMonitorListener : public art::MonitorCallback {
EventHandler* handler_;
};
-static void SetupMonitorListener(art::MonitorCallback* listener, bool enable) {
+class JvmtiParkListener : public art::ParkCallback {
+ public:
+ explicit JvmtiParkListener(EventHandler* handler) : handler_(handler) {}
+
+ void ThreadParkStart(bool is_absolute, 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();
+ art::ArtField* parkBlockerField = art::jni::DecodeArtField(
+ art::WellKnownClasses::java_lang_Thread_parkBlocker);
+ art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+ if (blocker_obj.IsNull()) {
+ blocker_obj = self->GetPeer();
+ }
+ int64_t timeout_ms;
+ if (!is_absolute) {
+ if (timeout == 0) {
+ timeout_ms = 0;
+ } else {
+ timeout_ms = timeout / 1000000;
+ if (timeout_ms == 0) {
+ // If we were instructed to park for a nonzero number of nanoseconds, but not enough
+ // to be a full millisecond, round up to 1 ms. A nonzero park() call will return
+ // soon, but a 0 wait or park call will wait indefinitely.
+ timeout_ms = 1;
+ }
+ }
+ } else {
+ struct timeval tv;
+ gettimeofday(&tv, (struct timezone *) nullptr);
+ int64_t now = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
+ if (now < timeout) {
+ timeout_ms = timeout - now;
+ } else {
+ // Waiting for 0 ms is an indefinite wait; parking until a time in
+ // the past or the current time will return immediately, so emulate
+ // the shortest possible wait event.
+ timeout_ms = 1;
+ }
+ }
+ ScopedLocalRef<jobject> blocker(jnienv, AddLocalRef<jobject>(jnienv, blocker_obj.Ptr()));
+ RunEventCallback<ArtJvmtiEvent::kMonitorWait>(
+ handler_,
+ self,
+ jnienv,
+ blocker.get(),
+ static_cast<jlong>(timeout_ms));
+ }
+ }
+
+
+ // 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 ThreadParkFinished(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();
+ art::ArtField* parkBlockerField = art::jni::DecodeArtField(
+ art::WellKnownClasses::java_lang_Thread_parkBlocker);
+ art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+ if (blocker_obj.IsNull()) {
+ blocker_obj = self->GetPeer();
+ }
+ ScopedLocalRef<jobject> blocker(jnienv, AddLocalRef<jobject>(jnienv, blocker_obj.Ptr()));
+ RunEventCallback<ArtJvmtiEvent::kMonitorWaited>(
+ handler_,
+ self,
+ jnienv,
+ blocker.get(),
+ static_cast<jboolean>(timeout));
+ }
+ }
+
+ private:
+ EventHandler* handler_;
+};
+
+static void SetupMonitorListener(art::MonitorCallback* monitor_listener, art::ParkCallback* park_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);
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddMonitorCallback(monitor_listener);
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddParkCallback(park_listener);
} else {
- art::Runtime::Current()->GetRuntimeCallbacks()->RemoveMonitorCallback(listener);
+ art::Runtime::Current()->GetRuntimeCallbacks()->RemoveMonitorCallback(monitor_listener);
+ art::Runtime::Current()->GetRuntimeCallbacks()->RemoveParkCallback(park_listener);
}
}
@@ -1053,7 +1144,7 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
case ArtJvmtiEvent::kMonitorWait:
case ArtJvmtiEvent::kMonitorWaited:
if (!OtherMonitorEventsEnabledAnywhere(event)) {
- SetupMonitorListener(monitor_listener_.get(), enable);
+ SetupMonitorListener(monitor_listener_.get(), park_listener_.get(), enable);
}
return;
default:
@@ -1204,6 +1295,7 @@ EventHandler::EventHandler()
gc_pause_listener_.reset(new JvmtiGcPauseListener(this));
method_trace_listener_.reset(new JvmtiMethodTraceListener(this));
monitor_listener_.reset(new JvmtiMonitorListener(this));
+ park_listener_.reset(new JvmtiParkListener(this));
}
EventHandler::~EventHandler() {