summaryrefslogtreecommitdiff
path: root/runtime/openjdkjvmti/events-inl.h
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/openjdkjvmti/events-inl.h')
-rw-r--r--runtime/openjdkjvmti/events-inl.h222
1 files changed, 182 insertions, 40 deletions
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index d0272010d4..21ec731ba5 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -17,14 +17,28 @@
#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, 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>
-ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent event) {
+ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, ArtJvmtiEvent event) {
if (env->event_callbacks == nullptr) {
return nullptr;
}
@@ -33,84 +47,166 @@ ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent eve
// function.
switch (event) {
- case JVMTI_EVENT_VM_INIT:
+ case ArtJvmtiEvent::kVmInit:
return reinterpret_cast<FnType*>(env->event_callbacks->VMInit);
- case JVMTI_EVENT_VM_DEATH:
+ case ArtJvmtiEvent::kVmDeath:
return reinterpret_cast<FnType*>(env->event_callbacks->VMDeath);
- case JVMTI_EVENT_THREAD_START:
+ case ArtJvmtiEvent::kThreadStart:
return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart);
- case JVMTI_EVENT_THREAD_END:
+ case ArtJvmtiEvent::kThreadEnd:
return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd);
- case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
+ case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
+ case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook);
- case JVMTI_EVENT_CLASS_LOAD:
+ case ArtJvmtiEvent::kClassLoad:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad);
- case JVMTI_EVENT_CLASS_PREPARE:
+ case ArtJvmtiEvent::kClassPrepare:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassPrepare);
- case JVMTI_EVENT_VM_START:
+ case ArtJvmtiEvent::kVmStart:
return reinterpret_cast<FnType*>(env->event_callbacks->VMStart);
- case JVMTI_EVENT_EXCEPTION:
+ case ArtJvmtiEvent::kException:
return reinterpret_cast<FnType*>(env->event_callbacks->Exception);
- case JVMTI_EVENT_EXCEPTION_CATCH:
+ case ArtJvmtiEvent::kExceptionCatch:
return reinterpret_cast<FnType*>(env->event_callbacks->ExceptionCatch);
- case JVMTI_EVENT_SINGLE_STEP:
+ case ArtJvmtiEvent::kSingleStep:
return reinterpret_cast<FnType*>(env->event_callbacks->SingleStep);
- case JVMTI_EVENT_FRAME_POP:
+ case ArtJvmtiEvent::kFramePop:
return reinterpret_cast<FnType*>(env->event_callbacks->FramePop);
- case JVMTI_EVENT_BREAKPOINT:
+ case ArtJvmtiEvent::kBreakpoint:
return reinterpret_cast<FnType*>(env->event_callbacks->Breakpoint);
- case JVMTI_EVENT_FIELD_ACCESS:
+ case ArtJvmtiEvent::kFieldAccess:
return reinterpret_cast<FnType*>(env->event_callbacks->FieldAccess);
- case JVMTI_EVENT_FIELD_MODIFICATION:
+ case ArtJvmtiEvent::kFieldModification:
return reinterpret_cast<FnType*>(env->event_callbacks->FieldModification);
- case JVMTI_EVENT_METHOD_ENTRY:
+ case ArtJvmtiEvent::kMethodEntry:
return reinterpret_cast<FnType*>(env->event_callbacks->MethodEntry);
- case JVMTI_EVENT_METHOD_EXIT:
+ case ArtJvmtiEvent::kMethodExit:
return reinterpret_cast<FnType*>(env->event_callbacks->MethodExit);
- case JVMTI_EVENT_NATIVE_METHOD_BIND:
+ case ArtJvmtiEvent::kNativeMethodBind:
return reinterpret_cast<FnType*>(env->event_callbacks->NativeMethodBind);
- case JVMTI_EVENT_COMPILED_METHOD_LOAD:
+ case ArtJvmtiEvent::kCompiledMethodLoad:
return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodLoad);
- case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
+ case ArtJvmtiEvent::kCompiledMethodUnload:
return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodUnload);
- case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
+ case ArtJvmtiEvent::kDynamicCodeGenerated:
return reinterpret_cast<FnType*>(env->event_callbacks->DynamicCodeGenerated);
- case JVMTI_EVENT_DATA_DUMP_REQUEST:
+ case ArtJvmtiEvent::kDataDumpRequest:
return reinterpret_cast<FnType*>(env->event_callbacks->DataDumpRequest);
- case JVMTI_EVENT_MONITOR_WAIT:
+ case ArtJvmtiEvent::kMonitorWait:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWait);
- case JVMTI_EVENT_MONITOR_WAITED:
+ case ArtJvmtiEvent::kMonitorWaited:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWaited);
- case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
+ case ArtJvmtiEvent::kMonitorContendedEnter:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEnter);
- case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
+ case ArtJvmtiEvent::kMonitorContendedEntered:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEntered);
- case JVMTI_EVENT_RESOURCE_EXHAUSTED:
+ case ArtJvmtiEvent::kResourceExhausted:
return reinterpret_cast<FnType*>(env->event_callbacks->ResourceExhausted);
- case JVMTI_EVENT_GARBAGE_COLLECTION_START:
+ case ArtJvmtiEvent::kGarbageCollectionStart:
return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionStart);
- case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+ case ArtJvmtiEvent::kGarbageCollectionFinish:
return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionFinish);
- case JVMTI_EVENT_OBJECT_FREE:
+ case ArtJvmtiEvent::kObjectFree:
return reinterpret_cast<FnType*>(env->event_callbacks->ObjectFree);
- case JVMTI_EVENT_VM_OBJECT_ALLOC:
+ case ArtJvmtiEvent::kVmObjectAlloc:
return reinterpret_cast<FnType*>(env->event_callbacks->VMObjectAlloc);
}
return nullptr;
}
template <typename ...Args>
-inline void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args) {
- using FnType = void(jvmtiEnv*, Args...);
- for (ArtJvmTiEnv* env : envs) {
- bool dispatch = env->event_masks.global_event_mask.Test(event);
+inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread*,
+ ArtJvmtiEvent event,
+ Args... args ATTRIBUTE_UNUSED) const {
+ CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ LOG(FATAL) << "Incorrect arguments to ClassFileLoadHook!";
+}
- if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
- EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
- dispatch = mask != nullptr && mask->Test(event);
+// TODO Locking of some type!
+template <>
+inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ JNIEnv* jnienv,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) const {
+ CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ using FnType = void(jvmtiEnv* /* jvmti_env */,
+ JNIEnv* /* jnienv */,
+ jclass /* class_being_redefined */,
+ jobject /* loader */,
+ const char* /* name */,
+ jobject /* protection_domain */,
+ jint /* class_data_len */,
+ const unsigned char* /* class_data */,
+ jint* /* new_class_data_len */,
+ unsigned char** /* new_class_data */);
+ jint current_len = class_data_len;
+ unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
+ ArtJvmTiEnv* last_env = nullptr;
+ for (ArtJvmTiEnv* env : envs) {
+ if (ShouldDispatch(event, env, thread)) {
+ jint new_len;
+ unsigned char* new_data;
+ FnType* callback = GetCallback<FnType>(env, event);
+ callback(env,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ current_len,
+ 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);
+ }
+ last_env = env;
+ current_class_data = new_data;
+ current_len = new_len;
+ }
}
+ }
+ if (last_env != nullptr) {
+ *new_class_data_len = current_len;
+ *new_class_data = current_class_data;
+ }
+}
+
+template <typename ...Args>
+inline void EventHandler::DispatchEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ Args... args) const {
+ switch (event) {
+ case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
+ case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
+ return DispatchClassFileLoadHookEvent(thread, event, args...);
+ default:
+ return GenericDispatchEvent(thread, event, args...);
+ }
+}
- if (dispatch) {
+// TODO Locking of some type!
+template <typename ...Args>
+inline void EventHandler::GenericDispatchEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ Args... args) const {
+ using FnType = void(jvmtiEnv*, Args...);
+ for (ArtJvmTiEnv* env : envs) {
+ if (ShouldDispatch(event, env, thread)) {
FnType* callback = GetCallback<FnType>(env, event);
if (callback != nullptr) {
(*callback)(env, args...);
@@ -119,6 +215,52 @@ inline void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, A
}
}
+inline bool EventHandler::ShouldDispatch(ArtJvmtiEvent event,
+ ArtJvmTiEnv* env,
+ art::Thread* thread) {
+ bool dispatch = env->event_masks.global_event_mask.Test(event);
+
+ if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
+ EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
+ dispatch = mask != nullptr && mask->Test(event);
+ }
+ 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_