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 #include "jni_internal.h" 24 #include "nativehelper/ScopedLocalRef.h" 25 #include "ti_breakpoint.h" 26 27 #include "art_jvmti.h" 28 29 namespace openjdkjvmti { 30 31 static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) { 32 if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { 33 if (env->capabilities.can_retransform_classes) { 34 return ArtJvmtiEvent::kClassFileLoadHookRetransformable; 35 } else { 36 return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable; 37 } 38 } else { 39 return static_cast<ArtJvmtiEvent>(e); 40 } 41 } 42 43 namespace impl { 44 45 // Infrastructure to achieve type safety for event dispatch. 46 47 #define FORALL_EVENT_TYPES(fn) \ 48 fn(VMInit, ArtJvmtiEvent::kVmInit) \ 49 fn(VMDeath, ArtJvmtiEvent::kVmDeath) \ 50 fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \ 51 fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \ 52 fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \ 53 fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \ 54 fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \ 55 fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \ 56 fn(VMStart, ArtJvmtiEvent::kVmStart) \ 57 fn(Exception, ArtJvmtiEvent::kException) \ 58 fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \ 59 fn(SingleStep, ArtJvmtiEvent::kSingleStep) \ 60 fn(FramePop, ArtJvmtiEvent::kFramePop) \ 61 fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \ 62 fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \ 63 fn(FieldModification, ArtJvmtiEvent::kFieldModification) \ 64 fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \ 65 fn(MethodExit, ArtJvmtiEvent::kMethodExit) \ 66 fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \ 67 fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \ 68 fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \ 69 fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \ 70 fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \ 71 fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \ 72 fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \ 73 fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \ 74 fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \ 75 fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \ 76 fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \ 77 fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \ 78 fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \ 79 fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) 80 81 template <ArtJvmtiEvent kEvent> 82 struct EventFnType { 83 }; 84 85 #define EVENT_FN_TYPE(name, enum_name) \ 86 template <> \ 87 struct EventFnType<enum_name> { \ 88 using type = decltype(jvmtiEventCallbacks().name); \ 89 }; 90 91 FORALL_EVENT_TYPES(EVENT_FN_TYPE) 92 93 #undef EVENT_FN_TYPE 94 95 template <ArtJvmtiEvent kEvent> 96 ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env); 97 98 #define GET_CALLBACK(name, enum_name) \ 99 template <> \ 100 ALWAYS_INLINE inline EventFnType<enum_name>::type GetCallback<enum_name>( \ 101 ArtJvmTiEnv* env) { \ 102 if (env->event_callbacks == nullptr) { \ 103 return nullptr; \ 104 } \ 105 return env->event_callbacks->name; \ 106 } 107 108 FORALL_EVENT_TYPES(GET_CALLBACK) 109 110 #undef GET_CALLBACK 111 112 #undef FORALL_EVENT_TYPES 113 114 } // namespace impl 115 116 // C++ does not allow partial template function specialization. The dispatch for our separated 117 // ClassFileLoadHook event types is the same, so use this helper for code deduplication. 118 // TODO Locking of some type! 119 template <ArtJvmtiEvent kEvent> 120 inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, 121 JNIEnv* jnienv, 122 jclass class_being_redefined, 123 jobject loader, 124 const char* name, 125 jobject protection_domain, 126 jint class_data_len, 127 const unsigned char* class_data, 128 jint* new_class_data_len, 129 unsigned char** new_class_data) const { 130 static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable || 131 kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event"); 132 DCHECK(*new_class_data == nullptr); 133 jint current_len = class_data_len; 134 unsigned char* current_class_data = const_cast<unsigned char*>(class_data); 135 ArtJvmTiEnv* last_env = nullptr; 136 for (ArtJvmTiEnv* env : envs) { 137 if (env == nullptr) { 138 continue; 139 } 140 if (ShouldDispatch<kEvent>(env, thread)) { 141 ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred()); 142 jnienv->ExceptionClear(); 143 jint new_len = 0; 144 unsigned char* new_data = nullptr; 145 auto callback = impl::GetCallback<kEvent>(env); 146 callback(env, 147 jnienv, 148 class_being_redefined, 149 loader, 150 name, 151 protection_domain, 152 current_len, 153 current_class_data, 154 &new_len, 155 &new_data); 156 if (thr.get() != nullptr && !jnienv->ExceptionCheck()) { 157 jnienv->Throw(thr.get()); 158 } 159 if (new_data != nullptr && new_data != current_class_data) { 160 // Destroy the data the last transformer made. We skip this if the previous state was the 161 // initial one since we don't know here which jvmtiEnv allocated it. 162 // NB Currently this doesn't matter since all allocations just go to malloc but in the 163 // future we might have jvmtiEnv's keep track of their allocations for leak-checking. 164 if (last_env != nullptr) { 165 last_env->Deallocate(current_class_data); 166 } 167 last_env = env; 168 current_class_data = new_data; 169 current_len = new_len; 170 } 171 } 172 } 173 if (last_env != nullptr) { 174 *new_class_data_len = current_len; 175 *new_class_data = current_class_data; 176 } 177 } 178 179 // Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match 180 // exactly the argument types of the corresponding Jvmti kEvent function pointer. 181 182 template <ArtJvmtiEvent kEvent, typename ...Args> 183 inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const { 184 for (ArtJvmTiEnv* env : envs) { 185 if (env != nullptr) { 186 DispatchEvent<kEvent, Args...>(env, thread, args...); 187 } 188 } 189 } 190 191 // Events with JNIEnvs need to stash pending exceptions since they can cause new ones to be thrown. 192 // In accordance with the JVMTI specification we allow exceptions originating from events to 193 // overwrite the current exception, including exceptions originating from earlier events. 194 // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list of the 195 // newest exception. 196 template <ArtJvmtiEvent kEvent, typename ...Args> 197 inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const { 198 for (ArtJvmTiEnv* env : envs) { 199 if (env != nullptr) { 200 ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred()); 201 jnienv->ExceptionClear(); 202 DispatchEvent<kEvent, JNIEnv*, Args...>(env, thread, jnienv, args...); 203 if (thr.get() != nullptr && !jnienv->ExceptionCheck()) { 204 jnienv->Throw(thr.get()); 205 } 206 } 207 } 208 } 209 210 template <ArtJvmtiEvent kEvent, typename ...Args> 211 inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const { 212 using FnType = void(jvmtiEnv*, Args...); 213 if (ShouldDispatch<kEvent>(env, thread)) { 214 FnType* callback = impl::GetCallback<kEvent>(env); 215 if (callback != nullptr) { 216 (*callback)(env, args...); 217 } 218 } 219 } 220 221 // Need to give custom specializations for Breakpoint since it needs to filter out which particular 222 // methods/dex_pcs agents get notified on. 223 template <> 224 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kBreakpoint>(art::Thread* thread, 225 JNIEnv* jnienv, 226 jthread jni_thread, 227 jmethodID jmethod, 228 jlocation location) const { 229 art::ArtMethod* method = art::jni::DecodeArtMethod(jmethod); 230 for (ArtJvmTiEnv* env : envs) { 231 // Search for a breakpoint on this particular method and location. 232 if (env != nullptr && 233 ShouldDispatch<ArtJvmtiEvent::kBreakpoint>(env, thread) && 234 env->breakpoints.find({method, location}) != env->breakpoints.end()) { 235 // We temporarily clear any pending exceptions so the event can call back into java code. 236 ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred()); 237 jnienv->ExceptionClear(); 238 auto callback = impl::GetCallback<ArtJvmtiEvent::kBreakpoint>(env); 239 (*callback)(env, jnienv, jni_thread, jmethod, location); 240 if (thr.get() != nullptr && !jnienv->ExceptionCheck()) { 241 jnienv->Throw(thr.get()); 242 } 243 } 244 } 245 } 246 247 // Need to give custom specializations for FieldAccess and FieldModification since they need to 248 // filter out which particular fields agents want to get notified on. 249 // TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This 250 // could make the system more performant. 251 template <> 252 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFieldModification>(art::Thread* thread, 253 JNIEnv* jnienv, 254 jthread jni_thread, 255 jmethodID method, 256 jlocation location, 257 jclass field_klass, 258 jobject object, 259 jfieldID field, 260 char type_char, 261 jvalue val) const { 262 for (ArtJvmTiEnv* env : envs) { 263 if (env != nullptr && 264 ShouldDispatch<ArtJvmtiEvent::kFieldModification>(env, thread) && 265 env->modify_watched_fields.find( 266 art::jni::DecodeArtField(field)) != env->modify_watched_fields.end()) { 267 ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred()); 268 jnienv->ExceptionClear(); 269 auto callback = impl::GetCallback<ArtJvmtiEvent::kFieldModification>(env); 270 (*callback)(env, 271 jnienv, 272 jni_thread, 273 method, 274 location, 275 field_klass, 276 object, 277 field, 278 type_char, 279 val); 280 if (thr.get() != nullptr && !jnienv->ExceptionCheck()) { 281 jnienv->Throw(thr.get()); 282 } 283 } 284 } 285 } 286 287 template <> 288 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFieldAccess>(art::Thread* thread, 289 JNIEnv* jnienv, 290 jthread jni_thread, 291 jmethodID method, 292 jlocation location, 293 jclass field_klass, 294 jobject object, 295 jfieldID field) const { 296 for (ArtJvmTiEnv* env : envs) { 297 if (env != nullptr && 298 ShouldDispatch<ArtJvmtiEvent::kFieldAccess>(env, thread) && 299 env->access_watched_fields.find( 300 art::jni::DecodeArtField(field)) != env->access_watched_fields.end()) { 301 ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred()); 302 jnienv->ExceptionClear(); 303 auto callback = impl::GetCallback<ArtJvmtiEvent::kFieldAccess>(env); 304 (*callback)(env, jnienv, jni_thread, method, location, field_klass, object, field); 305 if (thr.get() != nullptr && !jnienv->ExceptionCheck()) { 306 jnienv->Throw(thr.get()); 307 } 308 } 309 } 310 } 311 312 // Need to give a custom specialization for NativeMethodBind since it has to deal with an out 313 // variable. 314 template <> 315 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(art::Thread* thread, 316 JNIEnv* jnienv, 317 jthread jni_thread, 318 jmethodID method, 319 void* cur_method, 320 void** new_method) const { 321 *new_method = cur_method; 322 for (ArtJvmTiEnv* env : envs) { 323 if (env != nullptr && ShouldDispatch<ArtJvmtiEvent::kNativeMethodBind>(env, thread)) { 324 auto callback = impl::GetCallback<ArtJvmtiEvent::kNativeMethodBind>(env); 325 (*callback)(env, jnienv, jni_thread, method, cur_method, new_method); 326 if (*new_method != nullptr) { 327 cur_method = *new_method; 328 } 329 } 330 } 331 } 332 333 // C++ does not allow partial template function specialization. The dispatch for our separated 334 // ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper. 335 // The following two DispatchEvent specializations dispatch to it. 336 template <> 337 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( 338 art::Thread* thread, 339 JNIEnv* jnienv, 340 jclass class_being_redefined, 341 jobject loader, 342 const char* name, 343 jobject protection_domain, 344 jint class_data_len, 345 const unsigned char* class_data, 346 jint* new_class_data_len, 347 unsigned char** new_class_data) const { 348 return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( 349 thread, 350 jnienv, 351 class_being_redefined, 352 loader, 353 name, 354 protection_domain, 355 class_data_len, 356 class_data, 357 new_class_data_len, 358 new_class_data); 359 } 360 template <> 361 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( 362 art::Thread* thread, 363 JNIEnv* jnienv, 364 jclass class_being_redefined, 365 jobject loader, 366 const char* name, 367 jobject protection_domain, 368 jint class_data_len, 369 const unsigned char* class_data, 370 jint* new_class_data_len, 371 unsigned char** new_class_data) const { 372 return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( 373 thread, 374 jnienv, 375 class_being_redefined, 376 loader, 377 name, 378 protection_domain, 379 class_data_len, 380 class_data, 381 new_class_data_len, 382 new_class_data); 383 } 384 385 template <ArtJvmtiEvent kEvent> 386 inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env, 387 art::Thread* thread) { 388 bool dispatch = env->event_masks.global_event_mask.Test(kEvent); 389 390 if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) { 391 EventMask* mask = env->event_masks.GetEventMaskOrNull(thread); 392 dispatch = mask != nullptr && mask->Test(kEvent); 393 } 394 return dispatch; 395 } 396 397 inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { 398 bool union_value = false; 399 for (const ArtJvmTiEnv* stored_env : envs) { 400 if (stored_env == nullptr) { 401 continue; 402 } 403 union_value |= stored_env->event_masks.global_event_mask.Test(event); 404 union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event); 405 if (union_value) { 406 break; 407 } 408 } 409 global_mask.Set(event, union_value); 410 } 411 412 inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env, 413 const jvmtiCapabilities& caps, 414 bool added) { 415 ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable 416 : ArtJvmtiEvent::kClassFileLoadHookRetransformable; 417 return caps.can_retransform_classes == 1 && 418 IsEventEnabledAnywhere(event) && 419 env->event_masks.IsEnabledAnywhere(event); 420 } 421 422 inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env, 423 const jvmtiCapabilities& caps, 424 bool added) { 425 if (UNLIKELY(NeedsEventUpdate(env, caps, added))) { 426 env->event_masks.HandleChangedCapabilities(caps, added); 427 if (caps.can_retransform_classes == 1) { 428 RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable); 429 RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable); 430 } 431 } 432 } 433 434 } // namespace openjdkjvmti 435 436 #endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_ 437