Cleanup and consolidate JVMTI event code.
Various cleanups around JVMTI event code.
Ensure that we always store and restore exceptions.
Ensure we always give agents a local frame.
Ensure that we have static_asserts to verify that we are calling
events with appropriate types.
Various other improvements.
Test: ./test.py --host -j50
Change-Id: I71937d1575efca5096c9d5218203dc8201e3bb79
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
index 31edc73..ab8e6de 100644
--- a/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -18,10 +18,13 @@
#define ART_OPENJDKJVMTI_EVENTS_INL_H_
#include <array>
+#include <type_traits>
+#include <tuple>
#include "events.h"
#include "jni_internal.h"
#include "nativehelper/ScopedLocalRef.h"
+#include "scoped_thread_state_change-inl.h"
#include "ti_breakpoint.h"
#include "art_jvmti.h"
@@ -115,7 +118,6 @@
// C++ does not allow partial template function specialization. The dispatch for our separated
// ClassFileLoadHook event types is the same, so use this helper for code deduplication.
-// TODO Locking of some type!
template <ArtJvmtiEvent kEvent>
inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
JNIEnv* jnienv,
@@ -137,37 +139,30 @@
if (env == nullptr) {
continue;
}
- if (ShouldDispatch<kEvent>(env, thread)) {
- ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
- jnienv->ExceptionClear();
- jint new_len = 0;
- unsigned char* new_data = nullptr;
- auto callback = impl::GetCallback<kEvent>(env);
- callback(env,
- jnienv,
- class_being_redefined,
- loader,
- name,
- protection_domain,
- current_len,
- current_class_data,
- &new_len,
- &new_data);
- if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
- jnienv->Throw(thr.get());
+ jint new_len = 0;
+ unsigned char* new_data = nullptr;
+ DispatchEventOnEnv<kEvent>(env,
+ thread,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ current_len,
+ static_cast<const unsigned char*>(current_class_data),
+ &new_len,
+ &new_data);
+ if (new_data != nullptr && new_data != current_class_data) {
+ // Destroy the data the last transformer made. We skip this if the previous state was the
+ // initial one since we don't know here which jvmtiEnv allocated it.
+ // NB Currently this doesn't matter since all allocations just go to malloc but in the
+ // future we might have jvmtiEnv's keep track of their allocations for leak-checking.
+ if (last_env != nullptr) {
+ last_env->Deallocate(current_class_data);
}
- if (new_data != nullptr && new_data != current_class_data) {
- // Destroy the data the last transformer made. We skip this if the previous state was the
- // initial one since we don't know here which jvmtiEnv allocated it.
- // NB Currently this doesn't matter since all allocations just go to malloc but in the
- // future we might have jvmtiEnv's keep track of their allocations for leak-checking.
- if (last_env != nullptr) {
- last_env->Deallocate(current_class_data);
- }
- last_env = env;
- current_class_data = new_data;
- current_len = new_len;
- }
+ last_env = env;
+ current_class_data = new_data;
+ current_len = new_len;
}
}
if (last_env != nullptr) {
@@ -180,97 +175,125 @@
// exactly the argument types of the corresponding Jvmti kEvent function pointer.
template <ArtJvmtiEvent kEvent, typename ...Args>
+inline void EventHandler::ExecuteCallback(ArtJvmTiEnv* env, Args... args) {
+ using FnType = typename impl::EventFnType<kEvent>::type;
+ FnType callback = impl::GetCallback<kEvent>(env);
+ if (callback != nullptr) {
+ (*callback)(env, args...);
+ }
+}
+
+template <ArtJvmtiEvent kEvent, typename ...Args>
inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const {
+ static_assert(!std::is_same<JNIEnv*,
+ typename std::decay_t<
+ std::tuple_element_t<0, std::tuple<Args..., nullptr_t>>>>::value,
+ "Should be calling DispatchEvent with explicit JNIEnv* argument!");
+ DCHECK(thread == nullptr || !thread->IsExceptionPending());
for (ArtJvmTiEnv* env : envs) {
if (env != nullptr) {
- DispatchEvent<kEvent, Args...>(env, thread, args...);
+ DispatchEventOnEnv<kEvent, Args...>(env, thread, args...);
}
}
}
-// Events with JNIEnvs need to stash pending exceptions since they can cause new ones to be thrown.
-// In accordance with the JVMTI specification we allow exceptions originating from events to
-// overwrite the current exception, including exceptions originating from earlier events.
-// TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list of the
-// newest exception.
+// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash
+// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI
+// specification we allow exceptions originating from events to overwrite the current exception,
+// including exceptions originating from earlier events.
+class ScopedEventDispatchEnvironment FINAL : public art::ValueObject {
+ public:
+ explicit ScopedEventDispatchEnvironment(JNIEnv* env)
+ : env_(env),
+ thr_(env_, env_->ExceptionOccurred()),
+ suspend_(art::Thread::Current(), art::kNative) {
+ // The spec doesn't say how much local data should be there, so we just give 128 which seems
+ // likely to be enough for most cases.
+ env_->PushLocalFrame(128);
+ env_->ExceptionClear();
+ UNUSED(suspend_);
+ }
+
+ ~ScopedEventDispatchEnvironment() {
+ if (thr_.get() != nullptr && !env_->ExceptionCheck()) {
+ // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list
+ // of the newest exception.
+ env_->Throw(thr_.get());
+ }
+ env_->PopLocalFrame(nullptr);
+ }
+
+ private:
+ JNIEnv* env_;
+ ScopedLocalRef<jthrowable> thr_;
+ // Not actually unused. The destructor/constructor does important work.
+ art::ScopedThreadStateChange suspend_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment);
+};
+
template <ArtJvmtiEvent kEvent, typename ...Args>
inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const {
for (ArtJvmTiEnv* env : envs) {
if (env != nullptr) {
- ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
- jnienv->ExceptionClear();
- DispatchEvent<kEvent, JNIEnv*, Args...>(env, thread, jnienv, args...);
- if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
- jnienv->Throw(thr.get());
- }
+ DispatchEventOnEnv<kEvent, Args...>(env, thread, jnienv, args...);
}
}
}
template <ArtJvmtiEvent kEvent, typename ...Args>
-inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const {
- using FnType = void(jvmtiEnv*, Args...);
- if (ShouldDispatch<kEvent>(env, thread)) {
- FnType* callback = impl::GetCallback<kEvent>(env);
- if (callback != nullptr) {
- (*callback)(env, args...);
- }
+inline void EventHandler::DispatchEventOnEnv(
+ ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const {
+ DCHECK(env != nullptr);
+ if (ShouldDispatch<kEvent, JNIEnv*, Args...>(env, thread, jnienv, args...)) {
+ ScopedEventDispatchEnvironment sede(jnienv);
+ ExecuteCallback<kEvent, JNIEnv*, Args...>(env, jnienv, args...);
}
}
+template <ArtJvmtiEvent kEvent, typename ...Args>
+inline void EventHandler::DispatchEventOnEnv(
+ ArtJvmTiEnv* env, art::Thread* thread, Args... args) const {
+ static_assert(!std::is_same<JNIEnv*,
+ typename std::decay_t<
+ std::tuple_element_t<0, std::tuple<Args..., nullptr_t>>>>::value,
+ "Should be calling DispatchEventOnEnv with explicit JNIEnv* argument!");
+ if (ShouldDispatch<kEvent>(env, thread, args...)) {
+ ExecuteCallback<kEvent, Args...>(env, args...);
+ }
+}
+
+// Events that need custom logic for if we send the event but are otherwise normal. This includes
+// the kBreakpoint, kFramePop, kFieldAccess, and kFieldModification events.
+
// Need to give custom specializations for Breakpoint since it needs to filter out which particular
// methods/dex_pcs agents get notified on.
template <>
-inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kBreakpoint>(art::Thread* thread,
- JNIEnv* jnienv,
- jthread jni_thread,
- jmethodID jmethod,
- jlocation location) const {
+inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kBreakpoint>(
+ ArtJvmTiEnv* env,
+ art::Thread* thread,
+ JNIEnv* jnienv ATTRIBUTE_UNUSED,
+ jthread jni_thread ATTRIBUTE_UNUSED,
+ jmethodID jmethod,
+ jlocation location) const {
art::ArtMethod* method = art::jni::DecodeArtMethod(jmethod);
- for (ArtJvmTiEnv* env : envs) {
- // Search for a breakpoint on this particular method and location.
- if (env != nullptr &&
- ShouldDispatch<ArtJvmtiEvent::kBreakpoint>(env, thread) &&
- env->breakpoints.find({method, location}) != env->breakpoints.end()) {
- // We temporarily clear any pending exceptions so the event can call back into java code.
- ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
- jnienv->ExceptionClear();
- auto callback = impl::GetCallback<ArtJvmtiEvent::kBreakpoint>(env);
- (*callback)(env, jnienv, jni_thread, jmethod, location);
- if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
- jnienv->Throw(thr.get());
- }
- }
- }
+ return ShouldDispatchOnThread<ArtJvmtiEvent::kBreakpoint>(env, thread) &&
+ env->breakpoints.find({method, location}) != env->breakpoints.end();
}
-// Need to give custom specializations for FramePop since it needs to filter out which particular
-// agents get the event. This specialization gets an extra argument so we can determine which (if
-// any) environments have the frame pop.
template <>
-inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFramePop>(
+inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFramePop>(
+ ArtJvmTiEnv* env,
art::Thread* thread,
- JNIEnv* jnienv,
- jthread jni_thread,
- jmethodID jmethod,
- jboolean is_exception,
+ JNIEnv* jnienv ATTRIBUTE_UNUSED,
+ jthread jni_thread ATTRIBUTE_UNUSED,
+ jmethodID jmethod ATTRIBUTE_UNUSED,
+ jboolean is_exception ATTRIBUTE_UNUSED,
const art::ShadowFrame* frame) const {
- for (ArtJvmTiEnv* env : envs) {
- // Search for the frame. Do this before checking if we need to send the event so that we don't
- // have to deal with use-after-free or the frames being reallocated later.
- if (env != nullptr && env->notify_frames.erase(frame) != 0) {
- if (ShouldDispatch<ArtJvmtiEvent::kFramePop>(env, thread)) {
- // We temporarily clear any pending exceptions so the event can call back into java code.
- ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
- jnienv->ExceptionClear();
- auto callback = impl::GetCallback<ArtJvmtiEvent::kFramePop>(env);
- (*callback)(env, jnienv, jni_thread, jmethod, is_exception);
- if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
- jnienv->Throw(thr.get());
- }
- }
- }
- }
+ // Search for the frame. Do this before checking if we need to send the event so that we don't
+ // have to deal with use-after-free or the frames being reallocated later.
+ return env->notify_frames.erase(frame) != 0 &&
+ ShouldDispatchOnThread<ArtJvmtiEvent::kFramePop>(env, thread);
}
// Need to give custom specializations for FieldAccess and FieldModification since they need to
@@ -278,64 +301,54 @@
// TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This
// could make the system more performant.
template <>
-inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFieldModification>(art::Thread* thread,
- JNIEnv* jnienv,
- jthread jni_thread,
- jmethodID method,
- jlocation location,
- jclass field_klass,
- jobject object,
- jfieldID field,
- char type_char,
- jvalue val) const {
- for (ArtJvmTiEnv* env : envs) {
- if (env != nullptr &&
- ShouldDispatch<ArtJvmtiEvent::kFieldModification>(env, thread) &&
- env->modify_watched_fields.find(
- art::jni::DecodeArtField(field)) != env->modify_watched_fields.end()) {
- ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
- jnienv->ExceptionClear();
- auto callback = impl::GetCallback<ArtJvmtiEvent::kFieldModification>(env);
- (*callback)(env,
- jnienv,
- jni_thread,
- method,
- location,
- field_klass,
- object,
- field,
- type_char,
- val);
- if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
- jnienv->Throw(thr.get());
- }
- }
- }
+inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFieldModification>(
+ ArtJvmTiEnv* env,
+ art::Thread* thread,
+ JNIEnv* jnienv ATTRIBUTE_UNUSED,
+ jthread jni_thread ATTRIBUTE_UNUSED,
+ jmethodID method ATTRIBUTE_UNUSED,
+ jlocation location ATTRIBUTE_UNUSED,
+ jclass field_klass ATTRIBUTE_UNUSED,
+ jobject object ATTRIBUTE_UNUSED,
+ jfieldID field,
+ char type_char ATTRIBUTE_UNUSED,
+ jvalue val ATTRIBUTE_UNUSED) const {
+ return ShouldDispatchOnThread<ArtJvmtiEvent::kFieldModification>(env, thread) &&
+ env->modify_watched_fields.find(
+ art::jni::DecodeArtField(field)) != env->modify_watched_fields.end();
}
template <>
-inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFieldAccess>(art::Thread* thread,
- JNIEnv* jnienv,
- jthread jni_thread,
- jmethodID method,
- jlocation location,
- jclass field_klass,
- jobject object,
- jfieldID field) const {
- for (ArtJvmTiEnv* env : envs) {
- if (env != nullptr &&
- ShouldDispatch<ArtJvmtiEvent::kFieldAccess>(env, thread) &&
- env->access_watched_fields.find(
- art::jni::DecodeArtField(field)) != env->access_watched_fields.end()) {
- ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
- jnienv->ExceptionClear();
- auto callback = impl::GetCallback<ArtJvmtiEvent::kFieldAccess>(env);
- (*callback)(env, jnienv, jni_thread, method, location, field_klass, object, field);
- if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
- jnienv->Throw(thr.get());
- }
- }
- }
+inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFieldAccess>(
+ ArtJvmTiEnv* env,
+ art::Thread* thread,
+ JNIEnv* jnienv ATTRIBUTE_UNUSED,
+ jthread jni_thread ATTRIBUTE_UNUSED,
+ jmethodID method ATTRIBUTE_UNUSED,
+ jlocation location ATTRIBUTE_UNUSED,
+ jclass field_klass ATTRIBUTE_UNUSED,
+ jobject object ATTRIBUTE_UNUSED,
+ jfieldID field) const {
+ return ShouldDispatchOnThread<ArtJvmtiEvent::kFieldAccess>(env, thread) &&
+ env->access_watched_fields.find(
+ art::jni::DecodeArtField(field)) != env->access_watched_fields.end();
+}
+
+// Need to give custom specializations for FramePop since it needs to filter out which particular
+// agents get the event. This specialization gets an extra argument so we can determine which (if
+// any) environments have the frame pop.
+// TODO It might be useful to use more template magic to have this only define ShouldDispatch or
+// something.
+template <>
+inline void EventHandler::ExecuteCallback<ArtJvmtiEvent::kFramePop>(
+ ArtJvmTiEnv* env,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID jmethod,
+ jboolean is_exception,
+ const art::ShadowFrame* frame ATTRIBUTE_UNUSED) {
+ ExecuteCallback<ArtJvmtiEvent::kFramePop>(
+ env, jnienv, jni_thread, jmethod, is_exception);
}
// Need to give a custom specialization for NativeMethodBind since it has to deal with an out
@@ -349,14 +362,21 @@
void** new_method) const {
*new_method = cur_method;
for (ArtJvmTiEnv* env : envs) {
- if (env != nullptr && ShouldDispatch<ArtJvmtiEvent::kNativeMethodBind>(env, thread)) {
- auto callback = impl::GetCallback<ArtJvmtiEvent::kNativeMethodBind>(env);
- (*callback)(env, jnienv, jni_thread, method, cur_method, new_method);
+ if (env != nullptr) {
+ *new_method = cur_method;
+ DispatchEventOnEnv<ArtJvmtiEvent::kNativeMethodBind>(env,
+ thread,
+ jnienv,
+ jni_thread,
+ method,
+ cur_method,
+ new_method);
if (*new_method != nullptr) {
cur_method = *new_method;
}
}
}
+ *new_method = cur_method;
}
// C++ does not allow partial template function specialization. The dispatch for our separated
@@ -386,6 +406,7 @@
new_class_data_len,
new_class_data);
}
+
template <>
inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
art::Thread* thread,
@@ -412,8 +433,7 @@
}
template <ArtJvmtiEvent kEvent>
-inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env,
- art::Thread* thread) {
+inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) {
bool dispatch = env->event_masks.global_event_mask.Test(kEvent);
if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) {
@@ -423,6 +443,17 @@
return dispatch;
}
+template <ArtJvmtiEvent kEvent, typename ...Args>
+inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env,
+ art::Thread* thread,
+ Args... args ATTRIBUTE_UNUSED) const {
+ static_assert(std::is_same<typename impl::EventFnType<kEvent>::type,
+ void(*)(jvmtiEnv*, Args...)>::value,
+ "Unexpected different type of shouldDispatch");
+
+ return ShouldDispatchOnThread<kEvent>(env, thread);
+}
+
inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
bool union_value = false;
for (const ArtJvmTiEnv* stored_env : envs) {
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 5911a03..1cc82b7 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -192,6 +192,25 @@
}
}
+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) {}
@@ -211,26 +230,17 @@
// 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));
}
}
@@ -250,38 +260,6 @@
}
}
-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) {}
@@ -649,17 +627,15 @@
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>(
- event_handler_,
- 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,
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index 0d36c46..a062e15 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -161,20 +161,21 @@
ArtJvmtiEvent event,
jvmtiEventMode mode);
- // Dispatch event to all registered environments.
+ // Dispatch event to all registered environments. Since this one doesn't have a JNIEnv* it doesn't
+ // matter if it has the mutator_lock.
template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
inline void DispatchEvent(art::Thread* thread, Args... args) const;
+
// Dispatch event to all registered environments stashing exceptions as needed. This works since
// JNIEnv* is always the second argument if it is passed to an event. Needed since C++ does not
// allow partial template function specialization.
+ //
+ // We need both of these since we want to make sure to push a stack frame when it is possible for
+ // the event to allocate local references.
template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
- void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const;
- // Dispatch event to the given environment, only.
- template <ArtJvmtiEvent kEvent, typename ...Args>
- ALWAYS_INLINE
- inline void DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const;
+ inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const;
// Tell the event handler capabilities were added/lost so it can adjust the sent events.If
// caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false
@@ -184,10 +185,29 @@
const jvmtiCapabilities& caps,
bool added);
+ // Dispatch event to the given environment, only.
+ template <ArtJvmtiEvent kEvent, typename ...Args>
+ ALWAYS_INLINE
+ inline void DispatchEventOnEnv(
+ ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const;
+
+ // Dispatch event to the given environment, only.
+ template <ArtJvmtiEvent kEvent, typename ...Args>
+ ALWAYS_INLINE
+ inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const;
+
private:
template <ArtJvmtiEvent kEvent>
ALWAYS_INLINE
- static inline bool ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread);
+ static inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread);
+
+ template <ArtJvmtiEvent kEvent, typename ...Args>
+ ALWAYS_INLINE
+ static inline void ExecuteCallback(ArtJvmTiEnv* env, Args... args);
+
+ template <ArtJvmtiEvent kEvent, typename ...Args>
+ ALWAYS_INLINE
+ inline bool ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const;
ALWAYS_INLINE
inline bool NeedsEventUpdate(ArtJvmTiEnv* env,
diff --git a/openjdkjvmti/object_tagging.cc b/openjdkjvmti/object_tagging.cc
index dcdd3ed..6ba7165 100644
--- a/openjdkjvmti/object_tagging.cc
+++ b/openjdkjvmti/object_tagging.cc
@@ -61,7 +61,7 @@
return event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree);
}
void ObjectTagTable::HandleNullSweep(jlong tag) {
- event_handler_->DispatchEvent<ArtJvmtiEvent::kObjectFree>(jvmti_env_, nullptr, tag);
+ event_handler_->DispatchEventOnEnv<ArtJvmtiEvent::kObjectFree>(jvmti_env_, nullptr, tag);
}
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 954b5d1..daf4a8b 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -318,14 +318,11 @@
ScopedLocalRef<jthread> thread_jni(
thread->GetJniEnv(),
peer.IsNull() ? nullptr : thread->GetJniEnv()->AddLocalReference<jthread>(peer));
- {
- art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
- event_handler->DispatchEvent<ArtJvmtiEvent::kClassLoad>(
- thread,
- static_cast<JNIEnv*>(thread->GetJniEnv()),
- thread_jni.get(),
- jklass.get());
- }
+ event_handler->DispatchEvent<ArtJvmtiEvent::kClassLoad>(
+ thread,
+ static_cast<JNIEnv*>(thread->GetJniEnv()),
+ thread_jni.get(),
+ jklass.get());
if (klass->IsTemp()) {
AddTempClass(thread, jklass.get());
}
@@ -348,7 +345,6 @@
ScopedLocalRef<jthread> thread_jni(
thread->GetJniEnv(),
peer.IsNull() ? nullptr : thread->GetJniEnv()->AddLocalReference<jthread>(peer));
- art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
event_handler->DispatchEvent<ArtJvmtiEvent::kClassPrepare>(
thread,
static_cast<JNIEnv*>(thread->GetJniEnv()),