diff options
Diffstat (limited to 'runtime/openjdkjvmti/events-inl.h')
| -rw-r--r-- | runtime/openjdkjvmti/events-inl.h | 222 |
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_ |