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