• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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