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_OPENJDKJVMTI_EVENTS_INL_H_ 18 #define ART_OPENJDKJVMTI_EVENTS_INL_H_ 19 20 #include <array> 21 #include <type_traits> 22 #include <tuple> 23 24 #include "base/mutex-inl.h" 25 #include "events.h" 26 #include "jni_internal.h" 27 #include "nativehelper/scoped_local_ref.h" 28 #include "scoped_thread_state_change-inl.h" 29 #include "ti_breakpoint.h" 30 31 #include "art_jvmti.h" 32 33 namespace openjdkjvmti { 34 35 static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) { 36 if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { 37 if (env->capabilities.can_retransform_classes) { 38 return ArtJvmtiEvent::kClassFileLoadHookRetransformable; 39 } else { 40 return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable; 41 } 42 } else { 43 return static_cast<ArtJvmtiEvent>(e); 44 } 45 } 46 47 namespace impl { 48 49 // Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash 50 // pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI 51 // specification we allow exceptions originating from events to overwrite the current exception, 52 // including exceptions originating from earlier events. 53 class ScopedEventDispatchEnvironment FINAL : public art::ValueObject { 54 public: 55 ScopedEventDispatchEnvironment() : env_(nullptr), throw_(nullptr, nullptr) { 56 DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); 57 } 58 59 explicit ScopedEventDispatchEnvironment(JNIEnv* env) 60 : env_(env), 61 throw_(env_, env_->ExceptionOccurred()) { 62 DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); 63 // The spec doesn't say how much local data should be there, so we just give 128 which seems 64 // likely to be enough for most cases. 65 env_->PushLocalFrame(128); 66 env_->ExceptionClear(); 67 } 68 69 ~ScopedEventDispatchEnvironment() { 70 if (env_ != nullptr) { 71 if (throw_.get() != nullptr && !env_->ExceptionCheck()) { 72 // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list 73 // of the newest exception. 74 env_->Throw(throw_.get()); 75 } 76 env_->PopLocalFrame(nullptr); 77 } 78 DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); 79 } 80 81 private: 82 JNIEnv* env_; 83 ScopedLocalRef<jthrowable> throw_; 84 85 DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment); 86 }; 87 88 // Infrastructure to achieve type safety for event dispatch. 89 90 #define FORALL_EVENT_TYPES(fn) \ 91 fn(VMInit, ArtJvmtiEvent::kVmInit) \ 92 fn(VMDeath, ArtJvmtiEvent::kVmDeath) \ 93 fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \ 94 fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \ 95 fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \ 96 fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \ 97 fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \ 98 fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \ 99 fn(VMStart, ArtJvmtiEvent::kVmStart) \ 100 fn(Exception, ArtJvmtiEvent::kException) \ 101 fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \ 102 fn(SingleStep, ArtJvmtiEvent::kSingleStep) \ 103 fn(FramePop, ArtJvmtiEvent::kFramePop) \ 104 fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \ 105 fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \ 106 fn(FieldModification, ArtJvmtiEvent::kFieldModification) \ 107 fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \ 108 fn(MethodExit, ArtJvmtiEvent::kMethodExit) \ 109 fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \ 110 fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \ 111 fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \ 112 fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \ 113 fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \ 114 fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \ 115 fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \ 116 fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \ 117 fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \ 118 fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \ 119 fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \ 120 fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \ 121 fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \ 122 fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) \ 123 fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) 124 125 template <ArtJvmtiEvent kEvent> 126 struct EventFnType { 127 }; 128 129 #define EVENT_FN_TYPE(name, enum_name) \ 130 template <> \ 131 struct EventFnType<enum_name> { \ 132 using type = decltype(ArtJvmtiEventCallbacks().name); \ 133 }; 134 135 FORALL_EVENT_TYPES(EVENT_FN_TYPE) 136 137 #undef EVENT_FN_TYPE 138 139 #define MAKE_EVENT_HANDLER_FUNC(name, enum_name) \ 140 template<> \ 141 struct EventHandlerFunc<enum_name> { \ 142 using EventFnType = typename impl::EventFnType<enum_name>::type; \ 143 explicit EventHandlerFunc(ArtJvmTiEnv* env) \ 144 : env_(env), \ 145 fn_(env_->event_callbacks == nullptr ? nullptr : env_->event_callbacks->name) { } \ 146 \ 147 template <typename ...Args> \ 148 ALWAYS_INLINE \ 149 void ExecuteCallback(JNIEnv* jnienv, Args... args) const { \ 150 if (fn_ != nullptr) { \ 151 ScopedEventDispatchEnvironment sede(jnienv); \ 152 DoExecute(jnienv, args...); \ 153 } \ 154 } \ 155 \ 156 template <typename ...Args> \ 157 ALWAYS_INLINE \ 158 void ExecuteCallback(Args... args) const { \ 159 if (fn_ != nullptr) { \ 160 ScopedEventDispatchEnvironment sede; \ 161 DoExecute(args...); \ 162 } \ 163 } \ 164 \ 165 private: \ 166 template <typename ...Args> \ 167 ALWAYS_INLINE \ 168 inline void DoExecute(Args... args) const { \ 169 static_assert(std::is_same<EventFnType, void(*)(jvmtiEnv*, Args...)>::value, \ 170 "Unexpected different type of ExecuteCallback"); \ 171 fn_(env_, args...); \ 172 } \ 173 \ 174 public: \ 175 ArtJvmTiEnv* env_; \ 176 EventFnType fn_; \ 177 }; 178 179 FORALL_EVENT_TYPES(MAKE_EVENT_HANDLER_FUNC) 180 181 #undef MAKE_EVENT_HANDLER_FUNC 182 183 #undef FORALL_EVENT_TYPES 184 185 } // namespace impl 186 187 template <ArtJvmtiEvent kEvent, typename ...Args> 188 inline std::vector<impl::EventHandlerFunc<kEvent>> EventHandler::CollectEvents(art::Thread* thread, 189 Args... args) const { 190 art::ReaderMutexLock mu(thread, envs_lock_); 191 std::vector<impl::EventHandlerFunc<kEvent>> handlers; 192 for (ArtJvmTiEnv* env : envs) { 193 if (ShouldDispatch<kEvent>(env, thread, args...)) { 194 impl::EventHandlerFunc<kEvent> h(env); 195 handlers.push_back(h); 196 } 197 } 198 return handlers; 199 } 200 201 // C++ does not allow partial template function specialization. The dispatch for our separated 202 // ClassFileLoadHook event types is the same, so use this helper for code deduplication. 203 template <ArtJvmtiEvent kEvent> 204 inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, 205 JNIEnv* jnienv, 206 jclass class_being_redefined, 207 jobject loader, 208 const char* name, 209 jobject protection_domain, 210 jint class_data_len, 211 const unsigned char* class_data, 212 jint* new_class_data_len, 213 unsigned char** new_class_data) const { 214 art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); 215 static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable || 216 kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event"); 217 DCHECK(*new_class_data == nullptr); 218 jint current_len = class_data_len; 219 unsigned char* current_class_data = const_cast<unsigned char*>(class_data); 220 std::vector<impl::EventHandlerFunc<kEvent>> handlers = 221 CollectEvents<kEvent>(thread, 222 jnienv, 223 class_being_redefined, 224 loader, 225 name, 226 protection_domain, 227 class_data_len, 228 class_data, 229 new_class_data_len, 230 new_class_data); 231 ArtJvmTiEnv* last_env = nullptr; 232 for (const impl::EventHandlerFunc<kEvent>& event : handlers) { 233 jint new_len = 0; 234 unsigned char* new_data = nullptr; 235 ExecuteCallback<kEvent>(event, 236 jnienv, 237 class_being_redefined, 238 loader, 239 name, 240 protection_domain, 241 current_len, 242 static_cast<const unsigned char*>(current_class_data), 243 &new_len, 244 &new_data); 245 if (new_data != nullptr && new_data != current_class_data) { 246 // Destroy the data the last transformer made. We skip this if the previous state was the 247 // initial one since we don't know here which jvmtiEnv allocated it. 248 // NB Currently this doesn't matter since all allocations just go to malloc but in the 249 // future we might have jvmtiEnv's keep track of their allocations for leak-checking. 250 if (last_env != nullptr) { 251 last_env->Deallocate(current_class_data); 252 } 253 last_env = event.env_; 254 current_class_data = new_data; 255 current_len = new_len; 256 } 257 } 258 if (last_env != nullptr) { 259 *new_class_data_len = current_len; 260 *new_class_data = current_class_data; 261 } 262 } 263 264 // Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match 265 // exactly the argument types of the corresponding Jvmti kEvent function pointer. 266 267 template <ArtJvmtiEvent kEvent, typename ...Args> 268 inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const { 269 art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); 270 static_assert(!std::is_same<JNIEnv*, 271 typename std::decay_t< 272 std::tuple_element_t<0, std::tuple<Args..., nullptr_t>>>>::value, 273 "Should be calling DispatchEvent with explicit JNIEnv* argument!"); 274 DCHECK(thread == nullptr || !thread->IsExceptionPending()); 275 std::vector<impl::EventHandlerFunc<kEvent>> events = CollectEvents<kEvent>(thread, args...); 276 for (auto event : events) { 277 ExecuteCallback<kEvent>(event, args...); 278 } 279 } 280 281 template <ArtJvmtiEvent kEvent, typename ...Args> 282 inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const { 283 art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); 284 std::vector<impl::EventHandlerFunc<kEvent>> events = CollectEvents<kEvent>(thread, 285 jnienv, 286 args...); 287 for (auto event : events) { 288 ExecuteCallback<kEvent>(event, jnienv, args...); 289 } 290 } 291 292 template <ArtJvmtiEvent kEvent, typename ...Args> 293 inline void EventHandler::DispatchEventOnEnv( 294 ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const { 295 DCHECK(env != nullptr); 296 if (ShouldDispatch<kEvent, JNIEnv*, Args...>(env, thread, jnienv, args...)) { 297 art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); 298 impl::EventHandlerFunc<kEvent> func(env); 299 ExecuteCallback<kEvent>(func, jnienv, args...); 300 } 301 } 302 303 template <ArtJvmtiEvent kEvent, typename ...Args> 304 inline void EventHandler::DispatchEventOnEnv( 305 ArtJvmTiEnv* env, art::Thread* thread, Args... args) const { 306 static_assert(!std::is_same<JNIEnv*, 307 typename std::decay_t< 308 std::tuple_element_t<0, std::tuple<Args..., nullptr_t>>>>::value, 309 "Should be calling DispatchEventOnEnv with explicit JNIEnv* argument!"); 310 DCHECK(env != nullptr); 311 if (ShouldDispatch<kEvent, Args...>(env, thread, args...)) { 312 art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); 313 impl::EventHandlerFunc<kEvent> func(env); 314 ExecuteCallback<kEvent>(func, args...); 315 } 316 } 317 318 template <ArtJvmtiEvent kEvent, typename ...Args> 319 inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc<kEvent> handler, Args... args) { 320 handler.ExecuteCallback(args...); 321 } 322 323 template <ArtJvmtiEvent kEvent, typename ...Args> 324 inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc<kEvent> handler, 325 JNIEnv* jnienv, 326 Args... args) { 327 handler.ExecuteCallback(jnienv, args...); 328 } 329 330 // Events that need custom logic for if we send the event but are otherwise normal. This includes 331 // the kBreakpoint, kFramePop, kFieldAccess, and kFieldModification events. 332 333 // Need to give custom specializations for Breakpoint since it needs to filter out which particular 334 // methods/dex_pcs agents get notified on. 335 template <> 336 inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kBreakpoint>( 337 ArtJvmTiEnv* env, 338 art::Thread* thread, 339 JNIEnv* jnienv ATTRIBUTE_UNUSED, 340 jthread jni_thread ATTRIBUTE_UNUSED, 341 jmethodID jmethod, 342 jlocation location) const { 343 art::ReaderMutexLock lk(art::Thread::Current(), env->event_info_mutex_); 344 art::ArtMethod* method = art::jni::DecodeArtMethod(jmethod); 345 return ShouldDispatchOnThread<ArtJvmtiEvent::kBreakpoint>(env, thread) && 346 env->breakpoints.find({method, location}) != env->breakpoints.end(); 347 } 348 349 template <> 350 inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFramePop>( 351 ArtJvmTiEnv* env, 352 art::Thread* thread, 353 JNIEnv* jnienv ATTRIBUTE_UNUSED, 354 jthread jni_thread ATTRIBUTE_UNUSED, 355 jmethodID jmethod ATTRIBUTE_UNUSED, 356 jboolean is_exception ATTRIBUTE_UNUSED, 357 const art::ShadowFrame* frame) const { 358 // Search for the frame. Do this before checking if we need to send the event so that we don't 359 // have to deal with use-after-free or the frames being reallocated later. 360 art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_); 361 return env->notify_frames.erase(frame) != 0 && 362 ShouldDispatchOnThread<ArtJvmtiEvent::kFramePop>(env, thread); 363 } 364 365 // Need to give custom specializations for FieldAccess and FieldModification since they need to 366 // filter out which particular fields agents want to get notified on. 367 // TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This 368 // could make the system more performant. 369 template <> 370 inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFieldModification>( 371 ArtJvmTiEnv* env, 372 art::Thread* thread, 373 JNIEnv* jnienv ATTRIBUTE_UNUSED, 374 jthread jni_thread ATTRIBUTE_UNUSED, 375 jmethodID method ATTRIBUTE_UNUSED, 376 jlocation location ATTRIBUTE_UNUSED, 377 jclass field_klass ATTRIBUTE_UNUSED, 378 jobject object ATTRIBUTE_UNUSED, 379 jfieldID field, 380 char type_char ATTRIBUTE_UNUSED, 381 jvalue val ATTRIBUTE_UNUSED) const { 382 art::ReaderMutexLock lk(art::Thread::Current(), env->event_info_mutex_); 383 return ShouldDispatchOnThread<ArtJvmtiEvent::kFieldModification>(env, thread) && 384 env->modify_watched_fields.find( 385 art::jni::DecodeArtField(field)) != env->modify_watched_fields.end(); 386 } 387 388 template <> 389 inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFieldAccess>( 390 ArtJvmTiEnv* env, 391 art::Thread* thread, 392 JNIEnv* jnienv ATTRIBUTE_UNUSED, 393 jthread jni_thread ATTRIBUTE_UNUSED, 394 jmethodID method ATTRIBUTE_UNUSED, 395 jlocation location ATTRIBUTE_UNUSED, 396 jclass field_klass ATTRIBUTE_UNUSED, 397 jobject object ATTRIBUTE_UNUSED, 398 jfieldID field) const { 399 art::ReaderMutexLock lk(art::Thread::Current(), env->event_info_mutex_); 400 return ShouldDispatchOnThread<ArtJvmtiEvent::kFieldAccess>(env, thread) && 401 env->access_watched_fields.find( 402 art::jni::DecodeArtField(field)) != env->access_watched_fields.end(); 403 } 404 405 // Need to give custom specializations for FramePop since it needs to filter out which particular 406 // agents get the event. This specialization gets an extra argument so we can determine which (if 407 // any) environments have the frame pop. 408 // TODO It might be useful to use more template magic to have this only define ShouldDispatch or 409 // something. 410 template <> 411 inline void EventHandler::ExecuteCallback<ArtJvmtiEvent::kFramePop>( 412 impl::EventHandlerFunc<ArtJvmtiEvent::kFramePop> event, 413 JNIEnv* jnienv, 414 jthread jni_thread, 415 jmethodID jmethod, 416 jboolean is_exception, 417 const art::ShadowFrame* frame ATTRIBUTE_UNUSED) { 418 ExecuteCallback<ArtJvmtiEvent::kFramePop>(event, jnienv, jni_thread, jmethod, is_exception); 419 } 420 421 // Need to give a custom specialization for NativeMethodBind since it has to deal with an out 422 // variable. 423 template <> 424 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(art::Thread* thread, 425 JNIEnv* jnienv, 426 jthread jni_thread, 427 jmethodID method, 428 void* cur_method, 429 void** new_method) const { 430 art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); 431 std::vector<impl::EventHandlerFunc<ArtJvmtiEvent::kNativeMethodBind>> events = 432 CollectEvents<ArtJvmtiEvent::kNativeMethodBind>(thread, 433 jnienv, 434 jni_thread, 435 method, 436 cur_method, 437 new_method); 438 *new_method = cur_method; 439 for (auto event : events) { 440 *new_method = cur_method; 441 ExecuteCallback<ArtJvmtiEvent::kNativeMethodBind>(event, 442 jnienv, 443 jni_thread, 444 method, 445 cur_method, 446 new_method); 447 if (*new_method != nullptr) { 448 cur_method = *new_method; 449 } 450 } 451 *new_method = cur_method; 452 } 453 454 // C++ does not allow partial template function specialization. The dispatch for our separated 455 // ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper. 456 // The following two DispatchEvent specializations dispatch to it. 457 template <> 458 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( 459 art::Thread* thread, 460 JNIEnv* jnienv, 461 jclass class_being_redefined, 462 jobject loader, 463 const char* name, 464 jobject protection_domain, 465 jint class_data_len, 466 const unsigned char* class_data, 467 jint* new_class_data_len, 468 unsigned char** new_class_data) const { 469 return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( 470 thread, 471 jnienv, 472 class_being_redefined, 473 loader, 474 name, 475 protection_domain, 476 class_data_len, 477 class_data, 478 new_class_data_len, 479 new_class_data); 480 } 481 482 template <> 483 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( 484 art::Thread* thread, 485 JNIEnv* jnienv, 486 jclass class_being_redefined, 487 jobject loader, 488 const char* name, 489 jobject protection_domain, 490 jint class_data_len, 491 const unsigned char* class_data, 492 jint* new_class_data_len, 493 unsigned char** new_class_data) const { 494 return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( 495 thread, 496 jnienv, 497 class_being_redefined, 498 loader, 499 name, 500 protection_domain, 501 class_data_len, 502 class_data, 503 new_class_data_len, 504 new_class_data); 505 } 506 507 template <ArtJvmtiEvent kEvent> 508 inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const { 509 bool dispatch = env->event_masks.global_event_mask.Test(kEvent); 510 511 if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) { 512 EventMask* mask = env->event_masks.GetEventMaskOrNull(thread); 513 dispatch = mask != nullptr && mask->Test(kEvent); 514 } 515 return dispatch; 516 } 517 518 template <ArtJvmtiEvent kEvent, typename ...Args> 519 inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env, 520 art::Thread* thread, 521 Args... args ATTRIBUTE_UNUSED) const { 522 static_assert(std::is_same<typename impl::EventFnType<kEvent>::type, 523 void(*)(jvmtiEnv*, Args...)>::value, 524 "Unexpected different type of shouldDispatch"); 525 526 return ShouldDispatchOnThread<kEvent>(env, thread); 527 } 528 529 inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { 530 art::WriterMutexLock mu(art::Thread::Current(), envs_lock_); 531 RecalculateGlobalEventMaskLocked(event); 532 } 533 534 inline void EventHandler::RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) { 535 bool union_value = false; 536 for (const ArtJvmTiEnv* stored_env : envs) { 537 if (stored_env == nullptr) { 538 continue; 539 } 540 union_value |= stored_env->event_masks.global_event_mask.Test(event); 541 union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event); 542 if (union_value) { 543 break; 544 } 545 } 546 global_mask.Set(event, union_value); 547 } 548 549 inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env, 550 const jvmtiCapabilities& caps, 551 bool added) { 552 ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable 553 : ArtJvmtiEvent::kClassFileLoadHookRetransformable; 554 return (added && caps.can_access_local_variables == 1) || 555 caps.can_generate_breakpoint_events == 1 || 556 (caps.can_retransform_classes == 1 && 557 IsEventEnabledAnywhere(event) && 558 env->event_masks.IsEnabledAnywhere(event)); 559 } 560 561 inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env, 562 const jvmtiCapabilities& caps, 563 bool added) { 564 if (UNLIKELY(NeedsEventUpdate(env, caps, added))) { 565 env->event_masks.HandleChangedCapabilities(caps, added); 566 if (caps.can_retransform_classes == 1) { 567 RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable); 568 RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable); 569 } 570 if (added && caps.can_access_local_variables == 1) { 571 HandleLocalAccessCapabilityAdded(); 572 } 573 if (caps.can_generate_breakpoint_events == 1) { 574 HandleBreakpointEventsChanged(added); 575 } 576 } 577 } 578 579 } // namespace openjdkjvmti 580 581 #endif // ART_OPENJDKJVMTI_EVENTS_INL_H_ 582