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
GetArtJvmtiEvent(ArtJvmTiEnv * env,jvmtiEvent e)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:
ScopedEventDispatchEnvironment()55 ScopedEventDispatchEnvironment() : env_(nullptr), throw_(nullptr, nullptr) {
56 DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative);
57 }
58
ScopedEventDispatchEnvironment(JNIEnv * env)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
~ScopedEventDispatchEnvironment()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>
CollectEvents(art::Thread * thread,Args...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>
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)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>
DispatchEvent(art::Thread * thread,Args...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>
DispatchEvent(art::Thread * thread,JNIEnv * jnienv,Args...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>
DispatchEventOnEnv(ArtJvmTiEnv * env,art::Thread * thread,JNIEnv * jnienv,Args...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>
DispatchEventOnEnv(ArtJvmTiEnv * env,art::Thread * thread,Args...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>
ExecuteCallback(impl::EventHandlerFunc<kEvent> handler,Args...args)319 inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc<kEvent> handler, Args... args) {
320 handler.ExecuteCallback(args...);
321 }
322
323 template <ArtJvmtiEvent kEvent, typename ...Args>
ExecuteCallback(impl::EventHandlerFunc<kEvent> handler,JNIEnv * jnienv,Args...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>
ShouldDispatchOnThread(ArtJvmTiEnv * env,art::Thread * thread)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>
ShouldDispatch(ArtJvmTiEnv * env,art::Thread * thread,Args...args ATTRIBUTE_UNUSED)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
RecalculateGlobalEventMask(ArtJvmtiEvent event)529 inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
530 art::WriterMutexLock mu(art::Thread::Current(), envs_lock_);
531 RecalculateGlobalEventMaskLocked(event);
532 }
533
RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event)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
NeedsEventUpdate(ArtJvmTiEnv * env,const jvmtiCapabilities & caps,bool added)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
HandleChangedCapabilities(ArtJvmTiEnv * env,const jvmtiCapabilities & caps,bool added)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