1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_ 18 #define ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_ 19 20 #include <array> 21 22 #include "events.h" 23 24 #include "art_jvmti.h" 25 26 namespace openjdkjvmti { 27 28 static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) { 29 if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { 30 if (env->capabilities.can_retransform_classes) { 31 return ArtJvmtiEvent::kClassFileLoadHookRetransformable; 32 } else { 33 return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable; 34 } 35 } else { 36 return static_cast<ArtJvmtiEvent>(e); 37 } 38 } 39 40 namespace impl { 41 42 // Infrastructure to achieve type safety for event dispatch. 43 44 #define FORALL_EVENT_TYPES(fn) \ 45 fn(VMInit, ArtJvmtiEvent::kVmInit) \ 46 fn(VMDeath, ArtJvmtiEvent::kVmDeath) \ 47 fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \ 48 fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \ 49 fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \ 50 fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \ 51 fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \ 52 fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \ 53 fn(VMStart, ArtJvmtiEvent::kVmStart) \ 54 fn(Exception, ArtJvmtiEvent::kException) \ 55 fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \ 56 fn(SingleStep, ArtJvmtiEvent::kSingleStep) \ 57 fn(FramePop, ArtJvmtiEvent::kFramePop) \ 58 fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \ 59 fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \ 60 fn(FieldModification, ArtJvmtiEvent::kFieldModification) \ 61 fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \ 62 fn(MethodExit, ArtJvmtiEvent::kMethodExit) \ 63 fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \ 64 fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \ 65 fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \ 66 fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \ 67 fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \ 68 fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \ 69 fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \ 70 fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \ 71 fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \ 72 fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \ 73 fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \ 74 fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \ 75 fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \ 76 fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) 77 78 template <ArtJvmtiEvent kEvent> 79 struct EventFnType { 80 }; 81 82 #define EVENT_FN_TYPE(name, enum_name) \ 83 template <> \ 84 struct EventFnType<enum_name> { \ 85 using type = decltype(jvmtiEventCallbacks().name); \ 86 }; 87 88 FORALL_EVENT_TYPES(EVENT_FN_TYPE) 89 90 #undef EVENT_FN_TYPE 91 92 template <ArtJvmtiEvent kEvent> 93 ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env); 94 95 #define GET_CALLBACK(name, enum_name) \ 96 template <> \ 97 ALWAYS_INLINE inline EventFnType<enum_name>::type GetCallback<enum_name>( \ 98 ArtJvmTiEnv* env) { \ 99 if (env->event_callbacks == nullptr) { \ 100 return nullptr; \ 101 } \ 102 return env->event_callbacks->name; \ 103 } 104 105 FORALL_EVENT_TYPES(GET_CALLBACK) 106 107 #undef GET_CALLBACK 108 109 #undef FORALL_EVENT_TYPES 110 111 } // namespace impl 112 113 // C++ does not allow partial template function specialization. The dispatch for our separated 114 // ClassFileLoadHook event types is the same, so use this helper for code deduplication. 115 // TODO Locking of some type! 116 template <ArtJvmtiEvent kEvent> 117 inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, 118 JNIEnv* jnienv, 119 jclass class_being_redefined, 120 jobject loader, 121 const char* name, 122 jobject protection_domain, 123 jint class_data_len, 124 const unsigned char* class_data, 125 jint* new_class_data_len, 126 unsigned char** new_class_data) const { 127 static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable || 128 kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event"); 129 DCHECK(*new_class_data == nullptr); 130 jint current_len = class_data_len; 131 unsigned char* current_class_data = const_cast<unsigned char*>(class_data); 132 ArtJvmTiEnv* last_env = nullptr; 133 for (ArtJvmTiEnv* env : envs) { 134 if (env == nullptr) { 135 continue; 136 } 137 if (ShouldDispatch<kEvent>(env, thread)) { 138 jint new_len = 0; 139 unsigned char* new_data = nullptr; 140 auto callback = impl::GetCallback<kEvent>(env); 141 callback(env, 142 jnienv, 143 class_being_redefined, 144 loader, 145 name, 146 protection_domain, 147 current_len, 148 current_class_data, 149 &new_len, 150 &new_data); 151 if (new_data != nullptr && new_data != current_class_data) { 152 // Destroy the data the last transformer made. We skip this if the previous state was the 153 // initial one since we don't know here which jvmtiEnv allocated it. 154 // NB Currently this doesn't matter since all allocations just go to malloc but in the 155 // future we might have jvmtiEnv's keep track of their allocations for leak-checking. 156 if (last_env != nullptr) { 157 last_env->Deallocate(current_class_data); 158 } 159 last_env = env; 160 current_class_data = new_data; 161 current_len = new_len; 162 } 163 } 164 } 165 if (last_env != nullptr) { 166 *new_class_data_len = current_len; 167 *new_class_data = current_class_data; 168 } 169 } 170 171 // Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match 172 // exactly the argument types of the corresponding Jvmti kEvent function pointer. 173 174 template <ArtJvmtiEvent kEvent, typename ...Args> 175 inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const { 176 for (ArtJvmTiEnv* env : envs) { 177 if (env != nullptr) { 178 DispatchEvent<kEvent, Args...>(env, thread, args...); 179 } 180 } 181 } 182 183 template <ArtJvmtiEvent kEvent, typename ...Args> 184 inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const { 185 using FnType = void(jvmtiEnv*, Args...); 186 if (ShouldDispatch<kEvent>(env, thread)) { 187 FnType* callback = impl::GetCallback<kEvent>(env); 188 if (callback != nullptr) { 189 (*callback)(env, args...); 190 } 191 } 192 } 193 194 // Need to give a custom specialization for NativeMethodBind since it has to deal with an out 195 // variable. 196 template <> 197 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(art::Thread* thread, 198 JNIEnv* jnienv, 199 jthread jni_thread, 200 jmethodID method, 201 void* cur_method, 202 void** new_method) const { 203 *new_method = cur_method; 204 for (ArtJvmTiEnv* env : envs) { 205 if (env != nullptr && ShouldDispatch<ArtJvmtiEvent::kNativeMethodBind>(env, thread)) { 206 auto callback = impl::GetCallback<ArtJvmtiEvent::kNativeMethodBind>(env); 207 (*callback)(env, jnienv, jni_thread, method, cur_method, new_method); 208 if (*new_method != nullptr) { 209 cur_method = *new_method; 210 } 211 } 212 } 213 } 214 215 // C++ does not allow partial template function specialization. The dispatch for our separated 216 // ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper. 217 // The following two DispatchEvent specializations dispatch to it. 218 template <> 219 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( 220 art::Thread* thread, 221 JNIEnv* jnienv, 222 jclass class_being_redefined, 223 jobject loader, 224 const char* name, 225 jobject protection_domain, 226 jint class_data_len, 227 const unsigned char* class_data, 228 jint* new_class_data_len, 229 unsigned char** new_class_data) const { 230 return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( 231 thread, 232 jnienv, 233 class_being_redefined, 234 loader, 235 name, 236 protection_domain, 237 class_data_len, 238 class_data, 239 new_class_data_len, 240 new_class_data); 241 } 242 template <> 243 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( 244 art::Thread* thread, 245 JNIEnv* jnienv, 246 jclass class_being_redefined, 247 jobject loader, 248 const char* name, 249 jobject protection_domain, 250 jint class_data_len, 251 const unsigned char* class_data, 252 jint* new_class_data_len, 253 unsigned char** new_class_data) const { 254 return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( 255 thread, 256 jnienv, 257 class_being_redefined, 258 loader, 259 name, 260 protection_domain, 261 class_data_len, 262 class_data, 263 new_class_data_len, 264 new_class_data); 265 } 266 267 template <ArtJvmtiEvent kEvent> 268 inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env, 269 art::Thread* thread) { 270 bool dispatch = env->event_masks.global_event_mask.Test(kEvent); 271 272 if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) { 273 EventMask* mask = env->event_masks.GetEventMaskOrNull(thread); 274 dispatch = mask != nullptr && mask->Test(kEvent); 275 } 276 return dispatch; 277 } 278 279 inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { 280 bool union_value = false; 281 for (const ArtJvmTiEnv* stored_env : envs) { 282 if (stored_env == nullptr) { 283 continue; 284 } 285 union_value |= stored_env->event_masks.global_event_mask.Test(event); 286 union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event); 287 if (union_value) { 288 break; 289 } 290 } 291 global_mask.Set(event, union_value); 292 } 293 294 inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env, 295 const jvmtiCapabilities& caps, 296 bool added) { 297 ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable 298 : ArtJvmtiEvent::kClassFileLoadHookRetransformable; 299 return caps.can_retransform_classes == 1 && 300 IsEventEnabledAnywhere(event) && 301 env->event_masks.IsEnabledAnywhere(event); 302 } 303 304 inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env, 305 const jvmtiCapabilities& caps, 306 bool added) { 307 if (UNLIKELY(NeedsEventUpdate(env, caps, added))) { 308 env->event_masks.HandleChangedCapabilities(caps, added); 309 if (caps.can_retransform_classes == 1) { 310 RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable); 311 RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable); 312 } 313 } 314 } 315 316 } // namespace openjdkjvmti 317 318 #endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_ 319