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 
24 #include "art_jvmti.h"
25 
26 namespace openjdkjvmti {
27 
GetArtJvmtiEvent(ArtJvmTiEnv * env,jvmtiEvent e)28 static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) {
29   if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
30     if (env->capabilities.can_retransform_classes) {
31       return ArtJvmtiEvent::kClassFileLoadHookRetransformable;
32     } else {
33       return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
34     }
35   } else {
36     return static_cast<ArtJvmtiEvent>(e);
37   }
38 }
39 
40 namespace impl {
41 
42 // Infrastructure to achieve type safety for event dispatch.
43 
44 #define FORALL_EVENT_TYPES(fn)                                                       \
45   fn(VMInit,                  ArtJvmtiEvent::kVmInit)                                \
46   fn(VMDeath,                 ArtJvmtiEvent::kVmDeath)                               \
47   fn(ThreadStart,             ArtJvmtiEvent::kThreadStart)                           \
48   fn(ThreadEnd,               ArtJvmtiEvent::kThreadEnd)                             \
49   fn(ClassFileLoadHook,       ArtJvmtiEvent::kClassFileLoadHookRetransformable)      \
50   fn(ClassFileLoadHook,       ArtJvmtiEvent::kClassFileLoadHookNonRetransformable)   \
51   fn(ClassLoad,               ArtJvmtiEvent::kClassLoad)                             \
52   fn(ClassPrepare,            ArtJvmtiEvent::kClassPrepare)                          \
53   fn(VMStart,                 ArtJvmtiEvent::kVmStart)                               \
54   fn(Exception,               ArtJvmtiEvent::kException)                             \
55   fn(ExceptionCatch,          ArtJvmtiEvent::kExceptionCatch)                        \
56   fn(SingleStep,              ArtJvmtiEvent::kSingleStep)                            \
57   fn(FramePop,                ArtJvmtiEvent::kFramePop)                              \
58   fn(Breakpoint,              ArtJvmtiEvent::kBreakpoint)                            \
59   fn(FieldAccess,             ArtJvmtiEvent::kFieldAccess)                           \
60   fn(FieldModification,       ArtJvmtiEvent::kFieldModification)                     \
61   fn(MethodEntry,             ArtJvmtiEvent::kMethodEntry)                           \
62   fn(MethodExit,              ArtJvmtiEvent::kMethodExit)                            \
63   fn(NativeMethodBind,        ArtJvmtiEvent::kNativeMethodBind)                      \
64   fn(CompiledMethodLoad,      ArtJvmtiEvent::kCompiledMethodLoad)                    \
65   fn(CompiledMethodUnload,    ArtJvmtiEvent::kCompiledMethodUnload)                  \
66   fn(DynamicCodeGenerated,    ArtJvmtiEvent::kDynamicCodeGenerated)                  \
67   fn(DataDumpRequest,         ArtJvmtiEvent::kDataDumpRequest)                       \
68   fn(MonitorWait,             ArtJvmtiEvent::kMonitorWait)                           \
69   fn(MonitorWaited,           ArtJvmtiEvent::kMonitorWaited)                         \
70   fn(MonitorContendedEnter,   ArtJvmtiEvent::kMonitorContendedEnter)                 \
71   fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered)               \
72   fn(ResourceExhausted,       ArtJvmtiEvent::kResourceExhausted)                     \
73   fn(GarbageCollectionStart,  ArtJvmtiEvent::kGarbageCollectionStart)                \
74   fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish)               \
75   fn(ObjectFree,              ArtJvmtiEvent::kObjectFree)                            \
76   fn(VMObjectAlloc,           ArtJvmtiEvent::kVmObjectAlloc)
77 
78 template <ArtJvmtiEvent kEvent>
79 struct EventFnType {
80 };
81 
82 #define EVENT_FN_TYPE(name, enum_name)               \
83 template <>                                          \
84 struct EventFnType<enum_name> {                      \
85   using type = decltype(jvmtiEventCallbacks().name); \
86 };
87 
88 FORALL_EVENT_TYPES(EVENT_FN_TYPE)
89 
90 #undef EVENT_FN_TYPE
91 
92 template <ArtJvmtiEvent kEvent>
93 ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env);
94 
95 #define GET_CALLBACK(name, enum_name)                                     \
96 template <>                                                               \
97 ALWAYS_INLINE inline EventFnType<enum_name>::type GetCallback<enum_name>( \
98     ArtJvmTiEnv* env) {                                                   \
99   if (env->event_callbacks == nullptr) {                                  \
100     return nullptr;                                                       \
101   }                                                                       \
102   return env->event_callbacks->name;                                      \
103 }
104 
105 FORALL_EVENT_TYPES(GET_CALLBACK)
106 
107 #undef GET_CALLBACK
108 
109 #undef FORALL_EVENT_TYPES
110 
111 }  // namespace impl
112 
113 // C++ does not allow partial template function specialization. The dispatch for our separated
114 // ClassFileLoadHook event types is the same, so use this helper for code deduplication.
115 // TODO Locking of some type!
116 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)117 inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
118                                                          JNIEnv* jnienv,
119                                                          jclass class_being_redefined,
120                                                          jobject loader,
121                                                          const char* name,
122                                                          jobject protection_domain,
123                                                          jint class_data_len,
124                                                          const unsigned char* class_data,
125                                                          jint* new_class_data_len,
126                                                          unsigned char** new_class_data) const {
127   static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
128                 kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event");
129   DCHECK(*new_class_data == nullptr);
130   jint current_len = class_data_len;
131   unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
132   ArtJvmTiEnv* last_env = nullptr;
133   for (ArtJvmTiEnv* env : envs) {
134     if (env == nullptr) {
135       continue;
136     }
137     if (ShouldDispatch<kEvent>(env, thread)) {
138       jint new_len = 0;
139       unsigned char* new_data = nullptr;
140       auto callback = impl::GetCallback<kEvent>(env);
141       callback(env,
142                jnienv,
143                class_being_redefined,
144                loader,
145                name,
146                protection_domain,
147                current_len,
148                current_class_data,
149                &new_len,
150                &new_data);
151       if (new_data != nullptr && new_data != current_class_data) {
152         // Destroy the data the last transformer made. We skip this if the previous state was the
153         // initial one since we don't know here which jvmtiEnv allocated it.
154         // NB Currently this doesn't matter since all allocations just go to malloc but in the
155         // future we might have jvmtiEnv's keep track of their allocations for leak-checking.
156         if (last_env != nullptr) {
157           last_env->Deallocate(current_class_data);
158         }
159         last_env = env;
160         current_class_data = new_data;
161         current_len = new_len;
162       }
163     }
164   }
165   if (last_env != nullptr) {
166     *new_class_data_len = current_len;
167     *new_class_data = current_class_data;
168   }
169 }
170 
171 // Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match
172 // exactly the argument types of the corresponding Jvmti kEvent function pointer.
173 
174 template <ArtJvmtiEvent kEvent, typename ...Args>
DispatchEvent(art::Thread * thread,Args...args)175 inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const {
176   for (ArtJvmTiEnv* env : envs) {
177     if (env != nullptr) {
178       DispatchEvent<kEvent, Args...>(env, thread, args...);
179     }
180   }
181 }
182 
183 template <ArtJvmtiEvent kEvent, typename ...Args>
DispatchEvent(ArtJvmTiEnv * env,art::Thread * thread,Args...args)184 inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const {
185   using FnType = void(jvmtiEnv*, Args...);
186   if (ShouldDispatch<kEvent>(env, thread)) {
187     FnType* callback = impl::GetCallback<kEvent>(env);
188     if (callback != nullptr) {
189       (*callback)(env, args...);
190     }
191   }
192 }
193 
194 // Need to give a custom specialization for NativeMethodBind since it has to deal with an out
195 // variable.
196 template <>
197 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(art::Thread* thread,
198                                                                           JNIEnv* jnienv,
199                                                                           jthread jni_thread,
200                                                                           jmethodID method,
201                                                                           void* cur_method,
202                                                                           void** new_method) const {
203   *new_method = cur_method;
204   for (ArtJvmTiEnv* env : envs) {
205     if (env != nullptr && ShouldDispatch<ArtJvmtiEvent::kNativeMethodBind>(env, thread)) {
206       auto callback = impl::GetCallback<ArtJvmtiEvent::kNativeMethodBind>(env);
207       (*callback)(env, jnienv, jni_thread, method, cur_method, new_method);
208       if (*new_method != nullptr) {
209         cur_method = *new_method;
210       }
211     }
212   }
213 }
214 
215 // C++ does not allow partial template function specialization. The dispatch for our separated
216 // ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper.
217 // The following two DispatchEvent specializations dispatch to it.
218 template <>
219 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
220     art::Thread* thread,
221     JNIEnv* jnienv,
222     jclass class_being_redefined,
223     jobject loader,
224     const char* name,
225     jobject protection_domain,
226     jint class_data_len,
227     const unsigned char* class_data,
228     jint* new_class_data_len,
229     unsigned char** new_class_data) const {
230   return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
231       thread,
232       jnienv,
233       class_being_redefined,
234       loader,
235       name,
236       protection_domain,
237       class_data_len,
238       class_data,
239       new_class_data_len,
240       new_class_data);
241 }
242 template <>
243 inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
244     art::Thread* thread,
245     JNIEnv* jnienv,
246     jclass class_being_redefined,
247     jobject loader,
248     const char* name,
249     jobject protection_domain,
250     jint class_data_len,
251     const unsigned char* class_data,
252     jint* new_class_data_len,
253     unsigned char** new_class_data) const {
254   return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
255       thread,
256       jnienv,
257       class_being_redefined,
258       loader,
259       name,
260       protection_domain,
261       class_data_len,
262       class_data,
263       new_class_data_len,
264       new_class_data);
265 }
266 
267 template <ArtJvmtiEvent kEvent>
ShouldDispatch(ArtJvmTiEnv * env,art::Thread * thread)268 inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env,
269                                          art::Thread* thread) {
270   bool dispatch = env->event_masks.global_event_mask.Test(kEvent);
271 
272   if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) {
273     EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
274     dispatch = mask != nullptr && mask->Test(kEvent);
275   }
276   return dispatch;
277 }
278 
RecalculateGlobalEventMask(ArtJvmtiEvent event)279 inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
280   bool union_value = false;
281   for (const ArtJvmTiEnv* stored_env : envs) {
282     if (stored_env == nullptr) {
283       continue;
284     }
285     union_value |= stored_env->event_masks.global_event_mask.Test(event);
286     union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
287     if (union_value) {
288       break;
289     }
290   }
291   global_mask.Set(event, union_value);
292 }
293 
NeedsEventUpdate(ArtJvmTiEnv * env,const jvmtiCapabilities & caps,bool added)294 inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
295                                            const jvmtiCapabilities& caps,
296                                            bool added) {
297   ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
298                               : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
299   return caps.can_retransform_classes == 1 &&
300       IsEventEnabledAnywhere(event) &&
301       env->event_masks.IsEnabledAnywhere(event);
302 }
303 
HandleChangedCapabilities(ArtJvmTiEnv * env,const jvmtiCapabilities & caps,bool added)304 inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
305                                                     const jvmtiCapabilities& caps,
306                                                     bool added) {
307   if (UNLIKELY(NeedsEventUpdate(env, caps, added))) {
308     env->event_masks.HandleChangedCapabilities(caps, added);
309     if (caps.can_retransform_classes == 1) {
310       RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
311       RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
312     }
313   }
314 }
315 
316 }  // namespace openjdkjvmti
317 
318 #endif  // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
319