/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #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); } } namespace impl { // Infrastructure to achieve type safety for event dispatch. #define FORALL_EVENT_TYPES(fn) \ fn(VMInit, ArtJvmtiEvent::kVmInit) \ fn(VMDeath, ArtJvmtiEvent::kVmDeath) \ fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \ fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \ fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \ fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \ fn(VMStart, ArtJvmtiEvent::kVmStart) \ fn(Exception, ArtJvmtiEvent::kException) \ fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \ fn(SingleStep, ArtJvmtiEvent::kSingleStep) \ fn(FramePop, ArtJvmtiEvent::kFramePop) \ fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \ fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \ fn(FieldModification, ArtJvmtiEvent::kFieldModification) \ fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \ fn(MethodExit, ArtJvmtiEvent::kMethodExit) \ fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \ fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \ fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \ fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \ fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \ fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \ fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \ fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \ fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \ fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \ fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \ fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \ fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) template <ArtJvmtiEvent kEvent> struct EventFnType { }; #define EVENT_FN_TYPE(name, enum_name) \ template <> \ struct EventFnType<enum_name> { \ using type = decltype(jvmtiEventCallbacks().name); \ }; FORALL_EVENT_TYPES(EVENT_FN_TYPE) #undef EVENT_FN_TYPE template <ArtJvmtiEvent kEvent> ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env); #define GET_CALLBACK(name, enum_name) \ template <> \ ALWAYS_INLINE inline EventFnType<enum_name>::type GetCallback<enum_name>( \ ArtJvmTiEnv* env) { \ if (env->event_callbacks == nullptr) { \ return nullptr; \ } \ return env->event_callbacks->name; \ } FORALL_EVENT_TYPES(GET_CALLBACK) #undef GET_CALLBACK #undef FORALL_EVENT_TYPES } // namespace impl // 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, 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 { static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable || kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event"); DCHECK(*new_class_data == nullptr); 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 (env == nullptr) { continue; } if (ShouldDispatch<kEvent>(env, thread)) { 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 (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; } } // Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match // exactly the argument types of the corresponding Jvmti kEvent function pointer. template <ArtJvmtiEvent kEvent, typename ...Args> inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const { for (ArtJvmTiEnv* env : envs) { if (env != nullptr) { DispatchEvent<kEvent, Args...>(env, thread, 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...); } } } // Need to give a custom specialization for NativeMethodBind since it has to deal with an out // variable. template <> inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(art::Thread* thread, JNIEnv* jnienv, jthread jni_thread, jmethodID method, void* cur_method, 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 (*new_method != nullptr) { cur_method = *new_method; } } } } // C++ does not allow partial template function specialization. The dispatch for our separated // ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper. // The following two DispatchEvent specializations dispatch to it. template <> inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( art::Thread* thread, 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 { return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( thread, jnienv, class_being_redefined, loader, name, protection_domain, class_data_len, class_data, new_class_data_len, new_class_data); } template <> inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( art::Thread* thread, 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 { return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( thread, jnienv, class_being_redefined, loader, name, protection_domain, class_data_len, class_data, new_class_data_len, new_class_data); } template <ArtJvmtiEvent kEvent> inline bool EventHandler::ShouldDispatch(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)) { EventMask* mask = env->event_masks.GetEventMaskOrNull(thread); dispatch = mask != nullptr && mask->Test(kEvent); } return dispatch; } inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { bool union_value = false; for (const ArtJvmTiEnv* stored_env : envs) { if (stored_env == nullptr) { continue; } 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_