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