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