| /* | 
 |  * 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_OPENJDKJVMTI_EVENTS_INL_H_ | 
 | #define ART_OPENJDKJVMTI_EVENTS_INL_H_ | 
 |  | 
 | #include <array> | 
 | #include <type_traits> | 
 | #include <tuple> | 
 |  | 
 | #include "base/mutex-inl.h" | 
 | #include "events.h" | 
 | #include "jni/jni_internal.h" | 
 | #include "nativehelper/scoped_local_ref.h" | 
 | #include "runtime-inl.h" | 
 | #include "scoped_thread_state_change-inl.h" | 
 | #include "stack.h" | 
 | #include "ti_breakpoint.h" | 
 | #include "ti_thread.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 { | 
 |  | 
 | // Helper for ensuring that the dispatch environment is suitably provisioned. Events with JNIEnvs | 
 | // need to stash pending exceptions since they can cause new ones to be thrown. In accordance with | 
 | // the JVMTI specification we allow exceptions originating from events to overwrite the current | 
 | // exception, including exceptions originating from earlier events. | 
 | class ScopedEventDispatchEnvironment final : public art::ValueObject { | 
 |  public: | 
 |   ScopedEventDispatchEnvironment() : env_(nullptr), throw_(nullptr, nullptr) { | 
 |     DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); | 
 |   } | 
 |  | 
 |   explicit ScopedEventDispatchEnvironment(JNIEnv* env) | 
 |       : env_(env), | 
 |         throw_(env_, env_->ExceptionOccurred()) { | 
 |     DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); | 
 |     // The spec doesn't say how much local data should be there, so we just give 128 which seems | 
 |     // likely to be enough for most cases. | 
 |     env_->PushLocalFrame(128); | 
 |     env_->ExceptionClear(); | 
 |   } | 
 |  | 
 |   ~ScopedEventDispatchEnvironment() { | 
 |     if (env_ != nullptr) { | 
 |       if (throw_.get() != nullptr && !env_->ExceptionCheck()) { | 
 |         // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list | 
 |         // of the newest exception. | 
 |         env_->Throw(throw_.get()); | 
 |       } | 
 |       env_->PopLocalFrame(nullptr); | 
 |     } | 
 |     DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); | 
 |   } | 
 |  | 
 |  private: | 
 |   JNIEnv* env_; | 
 |   ScopedLocalRef<jthrowable> throw_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment); | 
 | }; | 
 |  | 
 | // 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)                         \ | 
 |   fn(DdmPublishChunk,           ArtJvmtiEvent::kDdmPublishChunk)                       \ | 
 |   fn(ObsoleteObjectCreated,     ArtJvmtiEvent::kObsoleteObjectCreated)                 \ | 
 |   fn(StructuralDexFileLoadHook, ArtJvmtiEvent::kStructuralDexFileLoadHook) | 
 |  | 
 | template <ArtJvmtiEvent kEvent> | 
 | struct EventFnType { | 
 | }; | 
 |  | 
 | #define EVENT_FN_TYPE(name, enum_name)                    \ | 
 | template <>                                               \ | 
 | struct EventFnType<enum_name> {                           \ | 
 |   using type = decltype(ArtJvmtiEventCallbacks().name);   \ | 
 | }; | 
 |  | 
 | FORALL_EVENT_TYPES(EVENT_FN_TYPE) | 
 |  | 
 | #undef EVENT_FN_TYPE | 
 |  | 
 | #define MAKE_EVENT_HANDLER_FUNC(name, enum_name)                                          \ | 
 | template<>                                                                                \ | 
 | struct EventHandlerFunc<enum_name> {                                                      \ | 
 |   using EventFnType = typename impl::EventFnType<enum_name>::type;                        \ | 
 |   explicit EventHandlerFunc(ArtJvmTiEnv* env)                                             \ | 
 |       : env_(env),                                                                        \ | 
 |         fn_(env_->event_callbacks == nullptr ? nullptr : env_->event_callbacks->name) { } \ | 
 |                                                                                           \ | 
 |   template <typename ...Args>                                                             \ | 
 |   ALWAYS_INLINE                                                                           \ | 
 |   void ExecuteCallback(JNIEnv* jnienv, Args... args) const {                              \ | 
 |     if (fn_ != nullptr) {                                                                 \ | 
 |       ScopedEventDispatchEnvironment sede(jnienv);                                        \ | 
 |       DoExecute(jnienv, args...);                                                         \ | 
 |     }                                                                                     \ | 
 |   }                                                                                       \ | 
 |                                                                                           \ | 
 |   template <typename ...Args>                                                             \ | 
 |   ALWAYS_INLINE                                                                           \ | 
 |   void ExecuteCallback(Args... args) const {                                              \ | 
 |     if (fn_ != nullptr) {                                                                 \ | 
 |       ScopedEventDispatchEnvironment sede;                                                \ | 
 |       DoExecute(args...);                                                                 \ | 
 |     }                                                                                     \ | 
 |   }                                                                                       \ | 
 |                                                                                           \ | 
 |  private:                                                                                 \ | 
 |   template <typename ...Args>                                                             \ | 
 |   ALWAYS_INLINE                                                                           \ | 
 |   inline void DoExecute(Args... args) const {                                             \ | 
 |     static_assert(std::is_same<EventFnType, void(*)(jvmtiEnv*, Args...)>::value,          \ | 
 |           "Unexpected different type of ExecuteCallback");                                \ | 
 |     fn_(env_, args...);                                                                   \ | 
 |   }                                                                                       \ | 
 |                                                                                           \ | 
 |  public:                                                                                  \ | 
 |   ArtJvmTiEnv* env_;                                                                      \ | 
 |   EventFnType fn_;                                                                        \ | 
 | }; | 
 |  | 
 | FORALL_EVENT_TYPES(MAKE_EVENT_HANDLER_FUNC) | 
 |  | 
 | #undef MAKE_EVENT_HANDLER_FUNC | 
 |  | 
 | #undef FORALL_EVENT_TYPES | 
 |  | 
 | }  // namespace impl | 
 |  | 
 | template <ArtJvmtiEvent kEvent, typename ...Args> | 
 | inline std::vector<impl::EventHandlerFunc<kEvent>> EventHandler::CollectEvents(art::Thread* thread, | 
 |                                                                                Args... args) const { | 
 |   art::ReaderMutexLock mu(thread, envs_lock_); | 
 |   std::vector<impl::EventHandlerFunc<kEvent>> handlers; | 
 |   for (ArtJvmTiEnv* env : envs) { | 
 |     if (ShouldDispatch<kEvent>(env, thread, args...)) { | 
 |       impl::EventHandlerFunc<kEvent> h(env); | 
 |       handlers.push_back(h); | 
 |     } | 
 |   } | 
 |   return handlers; | 
 | } | 
 |  | 
 | // 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. | 
 | 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 { | 
 |   art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); | 
 |   static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable || | 
 |                 kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable || | 
 |                 kEvent == ArtJvmtiEvent::kStructuralDexFileLoadHook, "Unsupported event"); | 
 |   DCHECK(*new_class_data == nullptr); | 
 |   jint current_len = class_data_len; | 
 |   unsigned char* current_class_data = const_cast<unsigned char*>(class_data); | 
 |   std::vector<impl::EventHandlerFunc<kEvent>> handlers = | 
 |       CollectEvents<kEvent>(thread, | 
 |                             jnienv, | 
 |                             class_being_redefined, | 
 |                             loader, | 
 |                             name, | 
 |                             protection_domain, | 
 |                             class_data_len, | 
 |                             class_data, | 
 |                             new_class_data_len, | 
 |                             new_class_data); | 
 |   ArtJvmTiEnv* last_env = nullptr; | 
 |   for (const impl::EventHandlerFunc<kEvent>& event : handlers) { | 
 |     jint new_len = 0; | 
 |     unsigned char* new_data = nullptr; | 
 |     ExecuteCallback<kEvent>(event, | 
 |                             jnienv, | 
 |                             class_being_redefined, | 
 |                             loader, | 
 |                             name, | 
 |                             protection_domain, | 
 |                             current_len, | 
 |                             static_cast<const unsigned char*>(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 = event.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 { | 
 |   art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); | 
 |   static_assert(!std::is_same<JNIEnv*, | 
 |                               typename std::decay_t< | 
 |                                   std::tuple_element_t<0, std::tuple<Args..., nullptr_t>>>>::value, | 
 |                 "Should be calling DispatchEvent with explicit JNIEnv* argument!"); | 
 |   DCHECK(thread == nullptr || !thread->IsExceptionPending()); | 
 |   std::vector<impl::EventHandlerFunc<kEvent>> events = CollectEvents<kEvent>(thread, args...); | 
 |   for (auto event : events) { | 
 |     ExecuteCallback<kEvent>(event, args...); | 
 |   } | 
 | } | 
 |  | 
 | template <ArtJvmtiEvent kEvent, typename ...Args> | 
 | inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const { | 
 |   art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); | 
 |   std::vector<impl::EventHandlerFunc<kEvent>> events = CollectEvents<kEvent>(thread, | 
 |                                                                              jnienv, | 
 |                                                                              args...); | 
 |   for (auto event : events) { | 
 |     ExecuteCallback<kEvent>(event, jnienv, args...); | 
 |   } | 
 | } | 
 |  | 
 | template <ArtJvmtiEvent kEvent, typename ...Args> | 
 | inline void EventHandler::DispatchEventOnEnv( | 
 |     ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const { | 
 |   DCHECK(env != nullptr); | 
 |   if (ShouldDispatch<kEvent, JNIEnv*, Args...>(env, thread, jnienv, args...)) { | 
 |     art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); | 
 |     impl::EventHandlerFunc<kEvent> func(env); | 
 |     ExecuteCallback<kEvent>(func, jnienv, args...); | 
 |   } | 
 | } | 
 |  | 
 | template <ArtJvmtiEvent kEvent, typename ...Args> | 
 | inline void EventHandler::DispatchEventOnEnv( | 
 |     ArtJvmTiEnv* env, art::Thread* thread, Args... args) const { | 
 |   static_assert(!std::is_same<JNIEnv*, | 
 |                               typename std::decay_t< | 
 |                                   std::tuple_element_t<0, std::tuple<Args..., nullptr_t>>>>::value, | 
 |                 "Should be calling DispatchEventOnEnv with explicit JNIEnv* argument!"); | 
 |   DCHECK(env != nullptr); | 
 |   if (ShouldDispatch<kEvent, Args...>(env, thread, args...)) { | 
 |     art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); | 
 |     impl::EventHandlerFunc<kEvent> func(env); | 
 |     ExecuteCallback<kEvent>(func, args...); | 
 |   } | 
 | } | 
 |  | 
 | template <> | 
 | inline void EventHandler::DispatchEventOnEnv<ArtJvmtiEvent::kObsoleteObjectCreated>( | 
 |     ArtJvmTiEnv* env, art::Thread* thread, jlong* obsolete_tag, jlong* new_tag) const { | 
 |   static constexpr ArtJvmtiEvent kEvent = ArtJvmtiEvent::kObsoleteObjectCreated; | 
 |   DCHECK(env != nullptr); | 
 |   if (ShouldDispatch<kEvent>(env, thread, obsolete_tag, new_tag)) { | 
 |     art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); | 
 |     impl::EventHandlerFunc<kEvent> func(env); | 
 |     ExecuteCallback<kEvent>(func, obsolete_tag, new_tag); | 
 |   } else { | 
 |     // Unlike most others this has a default action to make sure that agents without knowledge of | 
 |     // this extension get reasonable behavior. | 
 |     jlong temp = *obsolete_tag; | 
 |     *obsolete_tag = *new_tag; | 
 |     *new_tag = temp; | 
 |   } | 
 | } | 
 |  | 
 | template <ArtJvmtiEvent kEvent, typename ...Args> | 
 | inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc<kEvent> handler, Args... args) { | 
 |   handler.ExecuteCallback(args...); | 
 | } | 
 |  | 
 | template <ArtJvmtiEvent kEvent, typename ...Args> | 
 | inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc<kEvent> handler, | 
 |                                           JNIEnv* jnienv, | 
 |                                           Args... args) { | 
 |   handler.ExecuteCallback(jnienv, args...); | 
 | } | 
 |  | 
 | // Events that need custom logic for if we send the event but are otherwise normal. This includes | 
 | // the kBreakpoint, kFramePop, kFieldAccess, and kFieldModification events. | 
 |  | 
 | // Need to give custom specializations for Breakpoint since it needs to filter out which particular | 
 | // methods/dex_pcs agents get notified on. | 
 | template <> | 
 | inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kBreakpoint>( | 
 |     ArtJvmTiEnv* env, | 
 |     art::Thread* thread, | 
 |     JNIEnv* jnienv ATTRIBUTE_UNUSED, | 
 |     jthread jni_thread ATTRIBUTE_UNUSED, | 
 |     jmethodID jmethod, | 
 |     jlocation location) const { | 
 |   art::ReaderMutexLock lk(art::Thread::Current(), env->event_info_mutex_); | 
 |   art::ArtMethod* method = art::jni::DecodeArtMethod(jmethod); | 
 |   return ShouldDispatchOnThread<ArtJvmtiEvent::kBreakpoint>(env, thread) && | 
 |       env->breakpoints.find({method, location}) != env->breakpoints.end(); | 
 | } | 
 |  | 
 | template <> | 
 | inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFramePop>( | 
 |     ArtJvmTiEnv* env, | 
 |     art::Thread* thread, | 
 |     JNIEnv* jnienv ATTRIBUTE_UNUSED, | 
 |     jthread jni_thread ATTRIBUTE_UNUSED, | 
 |     jmethodID jmethod ATTRIBUTE_UNUSED, | 
 |     jboolean is_exception ATTRIBUTE_UNUSED, | 
 |     const art::ShadowFrame* frame) const { | 
 |   // Search for the frame. Do this before checking if we need to send the event so that we don't | 
 |   // have to deal with use-after-free or the frames being reallocated later. | 
 |   art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_); | 
 |   return env->notify_frames.erase(frame) != 0 && | 
 |       !frame->GetSkipMethodExitEvents() && | 
 |       ShouldDispatchOnThread<ArtJvmtiEvent::kFramePop>(env, thread); | 
 | } | 
 |  | 
 | // Need to give custom specializations for FieldAccess and FieldModification since they need to | 
 | // filter out which particular fields agents want to get notified on. | 
 | // TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This | 
 | // could make the system more performant. | 
 | template <> | 
 | inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFieldModification>( | 
 |     ArtJvmTiEnv* env, | 
 |     art::Thread* thread, | 
 |     JNIEnv* jnienv ATTRIBUTE_UNUSED, | 
 |     jthread jni_thread ATTRIBUTE_UNUSED, | 
 |     jmethodID method ATTRIBUTE_UNUSED, | 
 |     jlocation location ATTRIBUTE_UNUSED, | 
 |     jclass field_klass ATTRIBUTE_UNUSED, | 
 |     jobject object ATTRIBUTE_UNUSED, | 
 |     jfieldID field, | 
 |     char type_char ATTRIBUTE_UNUSED, | 
 |     jvalue val ATTRIBUTE_UNUSED) const { | 
 |   art::ReaderMutexLock lk(art::Thread::Current(), env->event_info_mutex_); | 
 |   return ShouldDispatchOnThread<ArtJvmtiEvent::kFieldModification>(env, thread) && | 
 |       env->modify_watched_fields.find( | 
 |           art::jni::DecodeArtField(field)) != env->modify_watched_fields.end(); | 
 | } | 
 |  | 
 | template <> | 
 | inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFieldAccess>( | 
 |     ArtJvmTiEnv* env, | 
 |     art::Thread* thread, | 
 |     JNIEnv* jnienv ATTRIBUTE_UNUSED, | 
 |     jthread jni_thread ATTRIBUTE_UNUSED, | 
 |     jmethodID method ATTRIBUTE_UNUSED, | 
 |     jlocation location ATTRIBUTE_UNUSED, | 
 |     jclass field_klass ATTRIBUTE_UNUSED, | 
 |     jobject object ATTRIBUTE_UNUSED, | 
 |     jfieldID field) const { | 
 |   art::ReaderMutexLock lk(art::Thread::Current(), env->event_info_mutex_); | 
 |   return ShouldDispatchOnThread<ArtJvmtiEvent::kFieldAccess>(env, thread) && | 
 |       env->access_watched_fields.find( | 
 |           art::jni::DecodeArtField(field)) != env->access_watched_fields.end(); | 
 | } | 
 |  | 
 | // Need to give custom specializations for FramePop since it needs to filter out which particular | 
 | // agents get the event. This specialization gets an extra argument so we can determine which (if | 
 | // any) environments have the frame pop. | 
 | // TODO It might be useful to use more template magic to have this only define ShouldDispatch or | 
 | // something. | 
 | template <> | 
 | inline void EventHandler::ExecuteCallback<ArtJvmtiEvent::kFramePop>( | 
 |     impl::EventHandlerFunc<ArtJvmtiEvent::kFramePop> event, | 
 |     JNIEnv* jnienv, | 
 |     jthread jni_thread, | 
 |     jmethodID jmethod, | 
 |     jboolean is_exception, | 
 |     const art::ShadowFrame* frame ATTRIBUTE_UNUSED) { | 
 |   ExecuteCallback<ArtJvmtiEvent::kFramePop>(event, jnienv, jni_thread, jmethod, is_exception); | 
 | } | 
 |  | 
 | struct ScopedDisablePopFrame { | 
 |  public: | 
 |   explicit ScopedDisablePopFrame(art::Thread* thread) : thread_(thread) { | 
 |     art::Locks::mutator_lock_->AssertSharedHeld(thread_); | 
 |     art::MutexLock mu(thread_, *art::Locks::thread_list_lock_); | 
 |     JvmtiGlobalTLSData* data = ThreadUtil::GetOrCreateGlobalTLSData(thread_); | 
 |     current_top_frame_ = art::StackVisitor::ComputeNumFrames( | 
 |         thread_, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); | 
 |     old_disable_frame_pop_depth_ = data->disable_pop_frame_depth; | 
 |     data->disable_pop_frame_depth = current_top_frame_; | 
 |     // Check that we cleaned up any old disables. This should only increase (or be equals if we do | 
 |     // another ClassLoad/Prepare recursively). | 
 |     DCHECK(old_disable_frame_pop_depth_ == JvmtiGlobalTLSData::kNoDisallowedPopFrame || | 
 |            current_top_frame_ >= old_disable_frame_pop_depth_) | 
 |         << "old: " << old_disable_frame_pop_depth_ << " current: " << current_top_frame_; | 
 |   } | 
 |  | 
 |   ~ScopedDisablePopFrame() { | 
 |     art::Locks::mutator_lock_->AssertSharedHeld(thread_); | 
 |     art::MutexLock mu(thread_, *art::Locks::thread_list_lock_); | 
 |     JvmtiGlobalTLSData* data = ThreadUtil::GetGlobalTLSData(thread_); | 
 |     DCHECK_EQ(data->disable_pop_frame_depth, current_top_frame_); | 
 |     data->disable_pop_frame_depth = old_disable_frame_pop_depth_; | 
 |   } | 
 |  | 
 |  private: | 
 |   art::Thread* thread_; | 
 |   size_t current_top_frame_; | 
 |   size_t old_disable_frame_pop_depth_; | 
 | }; | 
 | // We want to prevent the use of PopFrame when reporting either of these events. | 
 | template <ArtJvmtiEvent kEvent> | 
 | inline void EventHandler::DispatchClassLoadOrPrepareEvent(art::Thread* thread, | 
 |                                                           JNIEnv* jnienv, | 
 |                                                           jthread jni_thread, | 
 |                                                           jclass klass) const { | 
 |   ScopedDisablePopFrame sdpf(thread); | 
 |   art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); | 
 |   std::vector<impl::EventHandlerFunc<kEvent>> events = CollectEvents<kEvent>(thread, | 
 |                                                                              jnienv, | 
 |                                                                              jni_thread, | 
 |                                                                              klass); | 
 |  | 
 |   for (auto event : events) { | 
 |     ExecuteCallback<kEvent>(event, jnienv, jni_thread, klass); | 
 |   } | 
 | } | 
 |  | 
 | template <> | 
 | inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassLoad>(art::Thread* thread, | 
 |                                                                    JNIEnv* jnienv, | 
 |                                                                    jthread jni_thread, | 
 |                                                                    jclass klass) const { | 
 |   DispatchClassLoadOrPrepareEvent<ArtJvmtiEvent::kClassLoad>(thread, jnienv, jni_thread, klass); | 
 | } | 
 | template <> | 
 | inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassPrepare>(art::Thread* thread, | 
 |                                                                       JNIEnv* jnienv, | 
 |                                                                       jthread jni_thread, | 
 |                                                                       jclass klass) const { | 
 |   DispatchClassLoadOrPrepareEvent<ArtJvmtiEvent::kClassPrepare>(thread, jnienv, jni_thread, klass); | 
 | } | 
 |  | 
 | // 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 { | 
 |   art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); | 
 |   std::vector<impl::EventHandlerFunc<ArtJvmtiEvent::kNativeMethodBind>> events = | 
 |       CollectEvents<ArtJvmtiEvent::kNativeMethodBind>(thread, | 
 |                                                       jnienv, | 
 |                                                       jni_thread, | 
 |                                                       method, | 
 |                                                       cur_method, | 
 |                                                       new_method); | 
 |   *new_method = cur_method; | 
 |   for (auto event : events) { | 
 |     *new_method = cur_method; | 
 |     ExecuteCallback<ArtJvmtiEvent::kNativeMethodBind>(event, | 
 |                                                       jnienv, | 
 |                                                       jni_thread, | 
 |                                                       method, | 
 |                                                       cur_method, | 
 |                                                       new_method); | 
 |     if (*new_method != nullptr) { | 
 |       cur_method = *new_method; | 
 |     } | 
 |   } | 
 |   *new_method = cur_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 <> | 
 | inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kStructuralDexFileLoadHook>( | 
 |     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::kStructuralDexFileLoadHook>( | 
 |       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::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const { | 
 |   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; | 
 | } | 
 |  | 
 | template <ArtJvmtiEvent kEvent, typename ...Args> | 
 | inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env, | 
 |                                          art::Thread* thread, | 
 |                                          Args... args ATTRIBUTE_UNUSED) const { | 
 |   static_assert(std::is_same<typename impl::EventFnType<kEvent>::type, | 
 |                              void(*)(jvmtiEnv*, Args...)>::value, | 
 |                 "Unexpected different type of shouldDispatch"); | 
 |  | 
 |   return ShouldDispatchOnThread<kEvent>(env, thread); | 
 | } | 
 |  | 
 | inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { | 
 |   art::WriterMutexLock mu(art::Thread::Current(), envs_lock_); | 
 |   RecalculateGlobalEventMaskLocked(event); | 
 | } | 
 |  | 
 | inline void EventHandler::RecalculateGlobalEventMaskLocked(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 (added && caps.can_access_local_variables == 1) || | 
 |       caps.can_generate_breakpoint_events == 1 || | 
 |       caps.can_pop_frame == 1 || | 
 |       caps.can_force_early_return == 1 || | 
 |       (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); | 
 |     } | 
 |     if (added && caps.can_access_local_variables == 1) { | 
 |       HandleLocalAccessCapabilityAdded(); | 
 |     } | 
 |     if (caps.can_generate_breakpoint_events == 1) { | 
 |       HandleBreakpointEventsChanged(added); | 
 |     } | 
 |     if ((caps.can_pop_frame == 1 || caps.can_force_early_return == 1) && added) { | 
 |       // TODO We should keep track of how many of these have been enabled and remove it if there are | 
 |       // no more possible users. This isn't expected to be too common. | 
 |       art::Runtime::Current()->SetNonStandardExitsEnabled(); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace openjdkjvmti | 
 |  | 
 | #endif  // ART_OPENJDKJVMTI_EVENTS_INL_H_ |