blob: 1ddbb869f993d4677cd5913fb329dd9a9e86c777 [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"
23
24#include "art_jvmti.h"
25
26namespace openjdkjvmti {
27
Alex Light73afd322017-01-18 11:17:47 -080028static 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 }
Alex Light40d87f42017-01-18 10:27:06 -080038}
39
Andreas Gampe983c1752017-01-23 19:46:56 -080040namespace impl {
Andreas Gampe77708d92016-10-07 11:48:21 -070041
Andreas Gampe983c1752017-01-23 19:46:56 -080042// Infrastructure to achieve type safety for event dispatch.
Andreas Gampe27fa96c2016-10-07 15:05:24 -070043
Andreas Gampe983c1752017-01-23 19:46:56 -080044#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
78template <ArtJvmtiEvent kEvent>
79struct EventFnType {
80};
81
82#define EVENT_FN_TYPE(name, enum_name) \
83template <> \
84struct EventFnType<enum_name> { \
85 using type = decltype(jvmtiEventCallbacks().name); \
86};
87
88FORALL_EVENT_TYPES(EVENT_FN_TYPE)
89
90#undef EVENT_FN_TYPE
91
92template <ArtJvmtiEvent kEvent>
93ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env);
94
95#define GET_CALLBACK(name, enum_name) \
96template <> \
97ALWAYS_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; \
Andreas Gampe77708d92016-10-07 11:48:21 -0700103}
104
Andreas Gampe983c1752017-01-23 19:46:56 -0800105FORALL_EVENT_TYPES(GET_CALLBACK)
Alex Light6ac57502017-01-19 15:05:06 -0800106
Andreas Gampe983c1752017-01-23 19:46:56 -0800107#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.
Alex Light6ac57502017-01-19 15:05:06 -0800115// TODO Locking of some type!
Andreas Gampe983c1752017-01-23 19:46:56 -0800116template <ArtJvmtiEvent kEvent>
Alex Light6ac57502017-01-19 15:05:06 -0800117inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
Alex Light6ac57502017-01-19 15:05:06 -0800118 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 {
Andreas Gampe983c1752017-01-23 19:46:56 -0800127 static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
128 kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event");
Alex Light51271782017-03-24 17:28:30 -0700129 DCHECK(*new_class_data == nullptr);
Alex Light6ac57502017-01-19 15:05:06 -0800130 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) {
Andreas Gampe983c1752017-01-23 19:46:56 -0800134 if (ShouldDispatch<kEvent>(env, thread)) {
Alex Light440b5d92017-01-24 15:32:25 -0800135 jint new_len = 0;
136 unsigned char* new_data = nullptr;
Andreas Gampe983c1752017-01-23 19:46:56 -0800137 auto callback = impl::GetCallback<kEvent>(env);
Alex Light6ac57502017-01-19 15:05:06 -0800138 callback(env,
139 jnienv,
140 class_being_redefined,
141 loader,
142 name,
143 protection_domain,
144 current_len,
145 current_class_data,
146 &new_len,
147 &new_data);
148 if (new_data != nullptr && new_data != current_class_data) {
149 // Destroy the data the last transformer made. We skip this if the previous state was the
150 // initial one since we don't know here which jvmtiEnv allocated it.
151 // NB Currently this doesn't matter since all allocations just go to malloc but in the
152 // future we might have jvmtiEnv's keep track of their allocations for leak-checking.
153 if (last_env != nullptr) {
154 last_env->Deallocate(current_class_data);
155 }
156 last_env = env;
157 current_class_data = new_data;
158 current_len = new_len;
159 }
160 }
161 }
162 if (last_env != nullptr) {
163 *new_class_data_len = current_len;
164 *new_class_data = current_class_data;
165 }
166}
167
Andreas Gampe983c1752017-01-23 19:46:56 -0800168// Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match
169// exactly the argument types of the corresponding Jvmti kEvent function pointer.
Alex Light6ac57502017-01-19 15:05:06 -0800170
Andreas Gampe983c1752017-01-23 19:46:56 -0800171template <ArtJvmtiEvent kEvent, typename ...Args>
Andreas Gampea1705ea2017-03-28 20:12:13 -0700172inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const {
Andreas Gampe77708d92016-10-07 11:48:21 -0700173 for (ArtJvmTiEnv* env : envs) {
Andreas Gampea1705ea2017-03-28 20:12:13 -0700174 DispatchEvent<kEvent, Args...>(env, thread, args...);
175 }
176}
177
178template <ArtJvmtiEvent kEvent, typename ...Args>
179inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const {
180 using FnType = void(jvmtiEnv*, Args...);
181 if (ShouldDispatch<kEvent>(env, thread)) {
182 FnType* callback = impl::GetCallback<kEvent>(env);
183 if (callback != nullptr) {
184 (*callback)(env, args...);
Andreas Gampe77708d92016-10-07 11:48:21 -0700185 }
186 }
187}
188
Andreas Gampe983c1752017-01-23 19:46:56 -0800189// C++ does not allow partial template function specialization. The dispatch for our separated
190// ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper.
191// The following two DispatchEvent specializations dispatch to it.
192template <>
193inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
194 art::Thread* thread,
195 JNIEnv* jnienv,
196 jclass class_being_redefined,
197 jobject loader,
198 const char* name,
199 jobject protection_domain,
200 jint class_data_len,
201 const unsigned char* class_data,
202 jint* new_class_data_len,
203 unsigned char** new_class_data) const {
204 return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
205 thread,
206 jnienv,
207 class_being_redefined,
208 loader,
209 name,
210 protection_domain,
211 class_data_len,
212 class_data,
213 new_class_data_len,
214 new_class_data);
215}
216template <>
217inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
218 art::Thread* thread,
219 JNIEnv* jnienv,
220 jclass class_being_redefined,
221 jobject loader,
222 const char* name,
223 jobject protection_domain,
224 jint class_data_len,
225 const unsigned char* class_data,
226 jint* new_class_data_len,
227 unsigned char** new_class_data) const {
228 return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
229 thread,
230 jnienv,
231 class_being_redefined,
232 loader,
233 name,
234 protection_domain,
235 class_data_len,
236 class_data,
237 new_class_data_len,
238 new_class_data);
239}
Alex Light40d87f42017-01-18 10:27:06 -0800240
Andreas Gampe983c1752017-01-23 19:46:56 -0800241template <ArtJvmtiEvent kEvent>
242inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env,
243 art::Thread* thread) {
244 bool dispatch = env->event_masks.global_event_mask.Test(kEvent);
245
246 if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) {
Alex Light40d87f42017-01-18 10:27:06 -0800247 EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
Andreas Gampe983c1752017-01-23 19:46:56 -0800248 dispatch = mask != nullptr && mask->Test(kEvent);
Alex Light40d87f42017-01-18 10:27:06 -0800249 }
250 return dispatch;
251}
252
Alex Light73afd322017-01-18 11:17:47 -0800253inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
254 bool union_value = false;
255 for (const ArtJvmTiEnv* stored_env : envs) {
256 union_value |= stored_env->event_masks.global_event_mask.Test(event);
257 union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
258 if (union_value) {
259 break;
260 }
261 }
262 global_mask.Set(event, union_value);
263}
264
265inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
266 const jvmtiCapabilities& caps,
267 bool added) {
268 ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
269 : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
270 return caps.can_retransform_classes == 1 &&
271 IsEventEnabledAnywhere(event) &&
272 env->event_masks.IsEnabledAnywhere(event);
273}
274
275inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
276 const jvmtiCapabilities& caps,
277 bool added) {
278 if (UNLIKELY(NeedsEventUpdate(env, caps, added))) {
279 env->event_masks.HandleChangedCapabilities(caps, added);
280 if (caps.can_retransform_classes == 1) {
281 RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
282 RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
283 }
284 }
285}
286
Andreas Gampe77708d92016-10-07 11:48:21 -0700287} // namespace openjdkjvmti
288
289#endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_