Split ArtJvmtiEvent::kClassFileLoadHook in two.
Since the ClassFileLoadHook event is sent to different environments
based on when it is invoked we split the event in two behind the
scenes. The event dispatcher is responsible for making sure that
either or both of the appropriate underlying events are invoked when a
JVMTI_EVENT_CLASS_FILE_LOAD_HOOK is sent.
We also make sure to modify the EventHandler so it sends the correct
events in the correct places when an environment changes its
capabilities.
Bug: 32369913
Bug: 31684920
Test: mma -j40 test-art-host
Change-Id: I82567fc66debe0b658e8d7fced6284a8c4355b7a
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 966bd57..3cba817 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -903,11 +903,15 @@
ENSURE_NON_NULL(capabilities_ptr);
ArtJvmTiEnv* art_env = static_cast<ArtJvmTiEnv*>(env);
jvmtiError ret = OK;
+ jvmtiCapabilities changed;
#define ADD_CAPABILITY(e) \
do { \
if (capabilities_ptr->e == 1) { \
if (kPotentialCapabilities.e == 1) { \
- art_env->capabilities.e = 1;\
+ if (art_env->capabilities.e != 1) { \
+ art_env->capabilities.e = 1; \
+ changed.e = 1; \
+ }\
} else { \
ret = ERR(NOT_AVAILABLE); \
} \
@@ -956,6 +960,9 @@
ADD_CAPABILITY(can_generate_resource_exhaustion_heap_events);
ADD_CAPABILITY(can_generate_resource_exhaustion_threads_events);
#undef ADD_CAPABILITY
+ gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+ changed,
+ /*added*/true);
return ret;
}
@@ -964,10 +971,14 @@
ENSURE_VALID_ENV(env);
ENSURE_NON_NULL(capabilities_ptr);
ArtJvmTiEnv* art_env = reinterpret_cast<ArtJvmTiEnv*>(env);
+ jvmtiCapabilities changed;
#define DEL_CAPABILITY(e) \
do { \
if (capabilities_ptr->e == 1) { \
- art_env->capabilities.e = 0;\
+ if (art_env->capabilities.e == 1) { \
+ art_env->capabilities.e = 0;\
+ changed.e = 1; \
+ } \
} \
} while (false)
@@ -1013,6 +1024,9 @@
DEL_CAPABILITY(can_generate_resource_exhaustion_heap_events);
DEL_CAPABILITY(can_generate_resource_exhaustion_threads_events);
#undef DEL_CAPABILITY
+ gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+ changed,
+ /*added*/false);
return OK;
}
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index fb39db5..1e07bc6 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -17,15 +17,24 @@
#ifndef ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
#define ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
+#include <array>
+
#include "events.h"
#include "art_jvmti.h"
namespace openjdkjvmti {
-static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env ATTRIBUTE_UNUSED,
- jvmtiEvent e) {
- return static_cast<ArtJvmtiEvent>(e);
+static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) {
+ if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
+ if (env->capabilities.can_retransform_classes) {
+ return ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+ } else {
+ return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
+ }
+ } else {
+ return static_cast<ArtJvmtiEvent>(e);
+ }
}
template <typename FnType>
@@ -46,7 +55,8 @@
return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart);
case ArtJvmtiEvent::kThreadEnd:
return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd);
- case ArtJvmtiEvent::kClassFileLoadHook:
+ case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
+ case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook);
case ArtJvmtiEvent::kClassLoad:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad);
@@ -131,6 +141,40 @@
return dispatch;
}
+inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
+ bool union_value = false;
+ for (const ArtJvmTiEnv* stored_env : envs) {
+ union_value |= stored_env->event_masks.global_event_mask.Test(event);
+ union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
+ if (union_value) {
+ break;
+ }
+ }
+ global_mask.Set(event, union_value);
+}
+
+inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
+ const jvmtiCapabilities& caps,
+ bool added) {
+ ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
+ : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+ return caps.can_retransform_classes == 1 &&
+ IsEventEnabledAnywhere(event) &&
+ env->event_masks.IsEnabledAnywhere(event);
+}
+
+inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
+ const jvmtiCapabilities& caps,
+ bool added) {
+ if (UNLIKELY(NeedsEventUpdate(env, caps, added))) {
+ env->event_masks.HandleChangedCapabilities(caps, added);
+ if (caps.can_retransform_classes == 1) {
+ RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
+ RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ }
+ }
+}
+
} // namespace openjdkjvmti
#endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 66929cf..f38aa86 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -47,6 +47,10 @@
namespace openjdkjvmti {
+bool EventMasks::IsEnabledAnywhere(ArtJvmtiEvent event) {
+ return global_event_mask.Test(event) || unioned_thread_event_mask.Test(event);
+}
+
EventMask& EventMasks::GetEventMask(art::Thread* thread) {
if (thread == nullptr) {
return global_event_mask;
@@ -107,6 +111,35 @@
}
}
+void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added) {
+ if (UNLIKELY(caps.can_retransform_classes == 1)) {
+ // If we are giving this env the retransform classes cap we need to switch all events of
+ // NonTransformable to Transformable and vice versa.
+ ArtJvmtiEvent to_remove = caps_added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
+ : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+ ArtJvmtiEvent to_add = caps_added ? ArtJvmtiEvent::kClassFileLoadHookRetransformable
+ : ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
+ if (global_event_mask.Test(to_remove)) {
+ CHECK(!global_event_mask.Test(to_add));
+ global_event_mask.Set(to_remove, false);
+ global_event_mask.Set(to_add, true);
+ }
+
+ if (unioned_thread_event_mask.Test(to_remove)) {
+ CHECK(!unioned_thread_event_mask.Test(to_add));
+ unioned_thread_event_mask.Set(to_remove, false);
+ unioned_thread_event_mask.Set(to_add, true);
+ }
+ for (auto thread_mask : thread_event_masks) {
+ if (thread_mask.second.Test(to_remove)) {
+ CHECK(!thread_mask.second.Test(to_add));
+ thread_mask.second.Set(to_remove, false);
+ thread_mask.second.Set(to_add, true);
+ }
+ }
+ }
+}
+
void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) {
envs.push_back(env);
}
@@ -293,17 +326,7 @@
DCHECK_EQ(mode, JVMTI_DISABLE);
env->event_masks.DisableEvent(thread, event);
-
- // Gotta recompute the global mask.
- bool union_value = false;
- for (const ArtJvmTiEnv* stored_env : envs) {
- union_value |= stored_env->event_masks.global_event_mask.Test(event);
- union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
- if (union_value) {
- break;
- }
- }
- global_mask.Set(event, union_value);
+ RecalculateGlobalEventMask(event);
}
bool new_state = global_mask.Test(event);
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 8f56145..7990141 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -30,14 +30,15 @@
class JvmtiAllocationListener;
class JvmtiGcPauseListener;
-// an enum for ArtEvents.
+// an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between
+// retransformation capable and incapable loading
enum class ArtJvmtiEvent {
kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL,
kVmInit = JVMTI_EVENT_VM_INIT,
kVmDeath = JVMTI_EVENT_VM_DEATH,
kThreadStart = JVMTI_EVENT_THREAD_START,
kThreadEnd = JVMTI_EVENT_THREAD_END,
- kClassFileLoadHook = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+ kClassFileLoadHookNonRetransformable = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
kClassLoad = JVMTI_EVENT_CLASS_LOAD,
kClassPrepare = JVMTI_EVENT_CLASS_PREPARE,
kVmStart = JVMTI_EVENT_VM_START,
@@ -64,14 +65,19 @@
kGarbageCollectionFinish = JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
kObjectFree = JVMTI_EVENT_OBJECT_FREE,
kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC,
- kMaxEventTypeVal = JVMTI_MAX_EVENT_TYPE_VAL,
+ kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1,
+ kMaxEventTypeVal = kClassFileLoadHookRetransformable,
};
// Convert a jvmtiEvent into a ArtJvmtiEvent
ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e);
-ALWAYS_INLINE static inline jvmtiEvent GetJvmtiEvent(ArtJvmtiEvent e) {
- return static_cast<jvmtiEvent>(e);
+static inline jvmtiEvent GetJvmtiEvent(ArtJvmtiEvent e) {
+ if (UNLIKELY(e == ArtJvmtiEvent::kClassFileLoadHookRetransformable)) {
+ return JVMTI_EVENT_CLASS_FILE_LOAD_HOOK;
+ } else {
+ return static_cast<jvmtiEvent>(e);
+ }
}
struct EventMask {
@@ -118,6 +124,11 @@
EventMask* GetEventMaskOrNull(art::Thread* thread);
void EnableEvent(art::Thread* thread, ArtJvmtiEvent event);
void DisableEvent(art::Thread* thread, ArtJvmtiEvent event);
+ bool IsEnabledAnywhere(ArtJvmtiEvent event);
+ // Make any changes to event masks needed for the given capability changes. If caps_added is true
+ // then caps is all the newly set capabilities of the jvmtiEnv. If it is false then caps is the
+ // set of all capabilities that were removed from the jvmtiEnv.
+ void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added);
};
// Helper class for event handling.
@@ -146,10 +157,27 @@
ALWAYS_INLINE
inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, 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
+ // then caps is the set of all capabilities that were removed from the jvmtiEnv.
+ ALWAYS_INLINE
+ inline void HandleChangedCapabilities(ArtJvmTiEnv* env,
+ const jvmtiCapabilities& caps,
+ bool added);
+
private:
ALWAYS_INLINE
static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread);
+ ALWAYS_INLINE
+ inline bool NeedsEventUpdate(ArtJvmTiEnv* env,
+ const jvmtiCapabilities& caps,
+ bool added);
+
+ // Recalculates the event mask for the given event.
+ ALWAYS_INLINE
+ inline void RecalculateGlobalEventMask(ArtJvmtiEvent event);
+
void HandleEventType(ArtJvmtiEvent event, bool enable);
// List of all JvmTiEnv objects that have been created, in their creation order.