blob: cb7e6a9ad04ef89d59a7f82c75503b89e9bf2387 [file] [log] [blame]
Andreas Gampe77708d92016-10-07 11:48:21 -07001/*
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
Alex Light73afd322017-01-18 11:17:47 -080020#include <array>
21
Andreas Gampe77708d92016-10-07 11:48:21 -070022#include "events.h"
Alex Lightb7edcda2017-04-27 13:20:31 -070023#include "ScopedLocalRef.h"
Andreas Gampe77708d92016-10-07 11:48:21 -070024
25#include "art_jvmti.h"
26
27namespace openjdkjvmti {
28
Alex Light73afd322017-01-18 11:17:47 -080029static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) {
30 if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
31 if (env->capabilities.can_retransform_classes) {
32 return ArtJvmtiEvent::kClassFileLoadHookRetransformable;
33 } else {
34 return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
35 }
36 } else {
37 return static_cast<ArtJvmtiEvent>(e);
38 }
Alex Light40d87f42017-01-18 10:27:06 -080039}
40
Andreas Gampe983c1752017-01-23 19:46:56 -080041namespace impl {
Andreas Gampe77708d92016-10-07 11:48:21 -070042
Andreas Gampe983c1752017-01-23 19:46:56 -080043// Infrastructure to achieve type safety for event dispatch.
Andreas Gampe27fa96c2016-10-07 15:05:24 -070044
Andreas Gampe983c1752017-01-23 19:46:56 -080045#define FORALL_EVENT_TYPES(fn) \
46 fn(VMInit, ArtJvmtiEvent::kVmInit) \
47 fn(VMDeath, ArtJvmtiEvent::kVmDeath) \
48 fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \
49 fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \
50 fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \
51 fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \
52 fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \
53 fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \
54 fn(VMStart, ArtJvmtiEvent::kVmStart) \
55 fn(Exception, ArtJvmtiEvent::kException) \
56 fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \
57 fn(SingleStep, ArtJvmtiEvent::kSingleStep) \
58 fn(FramePop, ArtJvmtiEvent::kFramePop) \
59 fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \
60 fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \
61 fn(FieldModification, ArtJvmtiEvent::kFieldModification) \
62 fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \
63 fn(MethodExit, ArtJvmtiEvent::kMethodExit) \
64 fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \
65 fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \
66 fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \
67 fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \
68 fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \
69 fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \
70 fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \
71 fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \
72 fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \
73 fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \
74 fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \
75 fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \
76 fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \
77 fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc)
78
79template <ArtJvmtiEvent kEvent>
80struct EventFnType {
81};
82
83#define EVENT_FN_TYPE(name, enum_name) \
84template <> \
85struct EventFnType<enum_name> { \
86 using type = decltype(jvmtiEventCallbacks().name); \
87};
88
89FORALL_EVENT_TYPES(EVENT_FN_TYPE)
90
91#undef EVENT_FN_TYPE
92
93template <ArtJvmtiEvent kEvent>
94ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env);
95
96#define GET_CALLBACK(name, enum_name) \
97template <> \
98ALWAYS_INLINE inline EventFnType<enum_name>::type GetCallback<enum_name>( \
99 ArtJvmTiEnv* env) { \
100 if (env->event_callbacks == nullptr) { \
101 return nullptr; \
102 } \
103 return env->event_callbacks->name; \
Andreas Gampe77708d92016-10-07 11:48:21 -0700104}
105
Andreas Gampe983c1752017-01-23 19:46:56 -0800106FORALL_EVENT_TYPES(GET_CALLBACK)
Alex Light6ac57502017-01-19 15:05:06 -0800107
Andreas Gampe983c1752017-01-23 19:46:56 -0800108#undef GET_CALLBACK
109
110#undef FORALL_EVENT_TYPES
111
112} // namespace impl
113
114// C++ does not allow partial template function specialization. The dispatch for our separated
115// ClassFileLoadHook event types is the same, so use this helper for code deduplication.
Alex Light6ac57502017-01-19 15:05:06 -0800116// TODO Locking of some type!
Andreas Gampe983c1752017-01-23 19:46:56 -0800117template <ArtJvmtiEvent kEvent>
Alex Light6ac57502017-01-19 15:05:06 -0800118inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
Alex Light6ac57502017-01-19 15:05:06 -0800119 JNIEnv* jnienv,
120 jclass class_being_redefined,
121 jobject loader,
122 const char* name,
123 jobject protection_domain,
124 jint class_data_len,
125 const unsigned char* class_data,
126 jint* new_class_data_len,
127 unsigned char** new_class_data) const {
Andreas Gampe983c1752017-01-23 19:46:56 -0800128 static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
129 kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event");
Alex Light51271782017-03-24 17:28:30 -0700130 DCHECK(*new_class_data == nullptr);
Alex Light6ac57502017-01-19 15:05:06 -0800131 jint current_len = class_data_len;
132 unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
133 ArtJvmTiEnv* last_env = nullptr;
134 for (ArtJvmTiEnv* env : envs) {
Alex Lightbb766462017-04-12 16:13:33 -0700135 if (env == nullptr) {
136 continue;
137 }
Andreas Gampe983c1752017-01-23 19:46:56 -0800138 if (ShouldDispatch<kEvent>(env, thread)) {
Alex Lightb7edcda2017-04-27 13:20:31 -0700139 ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
140 jnienv->ExceptionClear();
Alex Light440b5d92017-01-24 15:32:25 -0800141 jint new_len = 0;
142 unsigned char* new_data = nullptr;
Andreas Gampe983c1752017-01-23 19:46:56 -0800143 auto callback = impl::GetCallback<kEvent>(env);
Alex Light6ac57502017-01-19 15:05:06 -0800144 callback(env,
145 jnienv,
146 class_being_redefined,
147 loader,
148 name,
149 protection_domain,
150 current_len,
151 current_class_data,
152 &new_len,
153 &new_data);
Alex Lightb7edcda2017-04-27 13:20:31 -0700154 if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
155 jnienv->Throw(thr.get());
156 }
Alex Light6ac57502017-01-19 15:05:06 -0800157 if (new_data != nullptr && new_data != current_class_data) {
158 // Destroy the data the last transformer made. We skip this if the previous state was the
159 // initial one since we don't know here which jvmtiEnv allocated it.
160 // NB Currently this doesn't matter since all allocations just go to malloc but in the
161 // future we might have jvmtiEnv's keep track of their allocations for leak-checking.
162 if (last_env != nullptr) {
163 last_env->Deallocate(current_class_data);
164 }
165 last_env = env;
166 current_class_data = new_data;
167 current_len = new_len;
168 }
169 }
170 }
171 if (last_env != nullptr) {
172 *new_class_data_len = current_len;
173 *new_class_data = current_class_data;
174 }
175}
176
Andreas Gampe983c1752017-01-23 19:46:56 -0800177// Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match
178// exactly the argument types of the corresponding Jvmti kEvent function pointer.
Alex Light6ac57502017-01-19 15:05:06 -0800179
Andreas Gampe983c1752017-01-23 19:46:56 -0800180template <ArtJvmtiEvent kEvent, typename ...Args>
Andreas Gampea1705ea2017-03-28 20:12:13 -0700181inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const {
Andreas Gampe77708d92016-10-07 11:48:21 -0700182 for (ArtJvmTiEnv* env : envs) {
Alex Lightbb766462017-04-12 16:13:33 -0700183 if (env != nullptr) {
184 DispatchEvent<kEvent, Args...>(env, thread, args...);
185 }
Andreas Gampea1705ea2017-03-28 20:12:13 -0700186 }
187}
188
Alex Lightb7edcda2017-04-27 13:20:31 -0700189// Events with JNIEnvs need to stash pending exceptions since they can cause new ones to be thrown.
190// In accordance with the JVMTI specification we allow exceptions originating from events to
191// overwrite the current exception, including exceptions originating from earlier events.
192// TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list of the
193// newest exception.
194template <ArtJvmtiEvent kEvent, typename ...Args>
195inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const {
196 for (ArtJvmTiEnv* env : envs) {
197 if (env != nullptr) {
198 ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
199 jnienv->ExceptionClear();
200 DispatchEvent<kEvent, JNIEnv*, Args...>(env, thread, jnienv, args...);
201 if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
202 jnienv->Throw(thr.get());
203 }
204 }
205 }
206}
207
Andreas Gampea1705ea2017-03-28 20:12:13 -0700208template <ArtJvmtiEvent kEvent, typename ...Args>
209inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const {
210 using FnType = void(jvmtiEnv*, Args...);
211 if (ShouldDispatch<kEvent>(env, thread)) {
212 FnType* callback = impl::GetCallback<kEvent>(env);
213 if (callback != nullptr) {
214 (*callback)(env, args...);
Andreas Gampe77708d92016-10-07 11:48:21 -0700215 }
216 }
217}
218
Alex Lightd78ddec2017-04-18 15:20:38 -0700219// Need to give a custom specialization for NativeMethodBind since it has to deal with an out
220// variable.
221template <>
222inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(art::Thread* thread,
223 JNIEnv* jnienv,
224 jthread jni_thread,
225 jmethodID method,
226 void* cur_method,
227 void** new_method) const {
228 *new_method = cur_method;
229 for (ArtJvmTiEnv* env : envs) {
230 if (env != nullptr && ShouldDispatch<ArtJvmtiEvent::kNativeMethodBind>(env, thread)) {
231 auto callback = impl::GetCallback<ArtJvmtiEvent::kNativeMethodBind>(env);
232 (*callback)(env, jnienv, jni_thread, method, cur_method, new_method);
233 if (*new_method != nullptr) {
234 cur_method = *new_method;
235 }
236 }
237 }
238}
239
Andreas Gampe983c1752017-01-23 19:46:56 -0800240// C++ does not allow partial template function specialization. The dispatch for our separated
241// ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper.
242// The following two DispatchEvent specializations dispatch to it.
243template <>
244inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
245 art::Thread* thread,
246 JNIEnv* jnienv,
247 jclass class_being_redefined,
248 jobject loader,
249 const char* name,
250 jobject protection_domain,
251 jint class_data_len,
252 const unsigned char* class_data,
253 jint* new_class_data_len,
254 unsigned char** new_class_data) const {
255 return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
256 thread,
257 jnienv,
258 class_being_redefined,
259 loader,
260 name,
261 protection_domain,
262 class_data_len,
263 class_data,
264 new_class_data_len,
265 new_class_data);
266}
267template <>
268inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
269 art::Thread* thread,
270 JNIEnv* jnienv,
271 jclass class_being_redefined,
272 jobject loader,
273 const char* name,
274 jobject protection_domain,
275 jint class_data_len,
276 const unsigned char* class_data,
277 jint* new_class_data_len,
278 unsigned char** new_class_data) const {
279 return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
280 thread,
281 jnienv,
282 class_being_redefined,
283 loader,
284 name,
285 protection_domain,
286 class_data_len,
287 class_data,
288 new_class_data_len,
289 new_class_data);
290}
Alex Light40d87f42017-01-18 10:27:06 -0800291
Andreas Gampe983c1752017-01-23 19:46:56 -0800292template <ArtJvmtiEvent kEvent>
293inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env,
294 art::Thread* thread) {
295 bool dispatch = env->event_masks.global_event_mask.Test(kEvent);
296
297 if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) {
Alex Light40d87f42017-01-18 10:27:06 -0800298 EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
Andreas Gampe983c1752017-01-23 19:46:56 -0800299 dispatch = mask != nullptr && mask->Test(kEvent);
Alex Light40d87f42017-01-18 10:27:06 -0800300 }
301 return dispatch;
302}
303
Alex Light73afd322017-01-18 11:17:47 -0800304inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
305 bool union_value = false;
306 for (const ArtJvmTiEnv* stored_env : envs) {
Alex Lightbb766462017-04-12 16:13:33 -0700307 if (stored_env == nullptr) {
308 continue;
309 }
Alex Light73afd322017-01-18 11:17:47 -0800310 union_value |= stored_env->event_masks.global_event_mask.Test(event);
311 union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
312 if (union_value) {
313 break;
314 }
315 }
316 global_mask.Set(event, union_value);
317}
318
319inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
320 const jvmtiCapabilities& caps,
321 bool added) {
322 ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
323 : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
324 return caps.can_retransform_classes == 1 &&
325 IsEventEnabledAnywhere(event) &&
326 env->event_masks.IsEnabledAnywhere(event);
327}
328
329inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
330 const jvmtiCapabilities& caps,
331 bool added) {
332 if (UNLIKELY(NeedsEventUpdate(env, caps, added))) {
333 env->event_masks.HandleChangedCapabilities(caps, added);
334 if (caps.can_retransform_classes == 1) {
335 RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
336 RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
337 }
338 }
339}
340
Andreas Gampe77708d92016-10-07 11:48:21 -0700341} // namespace openjdkjvmti
342
343#endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_