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
26namespace openjdkjvmti {
27
28static 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
40namespace 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
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;                                      \
103}
104
105FORALL_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!
116template <ArtJvmtiEvent kEvent>
117inline 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
174template <ArtJvmtiEvent kEvent, typename ...Args>
175inline 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
183template <ArtJvmtiEvent kEvent, typename ...Args>
184inline 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.
196template <>
197inline 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.
218template <>
219inline 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}
242template <>
243inline 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
267template <ArtJvmtiEvent kEvent>
268inline 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
279inline 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
294inline 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
304inline 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