ART: Move openjdkjvmti to art/
Move libopenjdkjvmti out of the runtime directory. Let's not
pollute the runtime library.
Test: m test-art-host
Change-Id: Idb6b9cebcd61777bd3200437a2ae584a63a4a341
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
new file mode 100644
index 0000000..32dba3e
--- /dev/null
+++ b/openjdkjvmti/events-inl.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_OPENJDKJVMTI_EVENTS_INL_H_
+#define ART_OPENJDKJVMTI_EVENTS_INL_H_
+
+#include <array>
+
+#include "events.h"
+#include "jni_internal.h"
+#include "nativehelper/ScopedLocalRef.h"
+#include "ti_breakpoint.h"
+
+#include "art_jvmti.h"
+
+namespace openjdkjvmti {
+
+static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) {
+ if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
+ if (env->capabilities.can_retransform_classes) {
+ return ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+ } else {
+ return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
+ }
+ } else {
+ return static_cast<ArtJvmtiEvent>(e);
+ }
+}
+
+namespace impl {
+
+// Infrastructure to achieve type safety for event dispatch.
+
+#define FORALL_EVENT_TYPES(fn) \
+ fn(VMInit, ArtJvmtiEvent::kVmInit) \
+ fn(VMDeath, ArtJvmtiEvent::kVmDeath) \
+ fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \
+ fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \
+ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \
+ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \
+ fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \
+ fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \
+ fn(VMStart, ArtJvmtiEvent::kVmStart) \
+ fn(Exception, ArtJvmtiEvent::kException) \
+ fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \
+ fn(SingleStep, ArtJvmtiEvent::kSingleStep) \
+ fn(FramePop, ArtJvmtiEvent::kFramePop) \
+ fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \
+ fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \
+ fn(FieldModification, ArtJvmtiEvent::kFieldModification) \
+ fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \
+ fn(MethodExit, ArtJvmtiEvent::kMethodExit) \
+ fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \
+ fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \
+ fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \
+ fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \
+ fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \
+ fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \
+ fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \
+ fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \
+ fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \
+ fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \
+ fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \
+ fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \
+ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \
+ fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc)
+
+template <ArtJvmtiEvent kEvent>
+struct EventFnType {
+};
+
+#define EVENT_FN_TYPE(name, enum_name) \
+template <> \
+struct EventFnType<enum_name> { \
+ using type = decltype(jvmtiEventCallbacks().name); \
+};
+
+FORALL_EVENT_TYPES(EVENT_FN_TYPE)
+
+#undef EVENT_FN_TYPE
+
+template <ArtJvmtiEvent kEvent>
+ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env);
+
+#define GET_CALLBACK(name, enum_name) \
+template <> \
+ALWAYS_INLINE inline EventFnType<enum_name>::type GetCallback<enum_name>( \
+ ArtJvmTiEnv* env) { \
+ if (env->event_callbacks == nullptr) { \
+ return nullptr; \
+ } \
+ return env->event_callbacks->name; \
+}
+
+FORALL_EVENT_TYPES(GET_CALLBACK)
+
+#undef GET_CALLBACK
+
+#undef FORALL_EVENT_TYPES
+
+} // namespace impl
+
+// C++ does not allow partial template function specialization. The dispatch for our separated
+// ClassFileLoadHook event types is the same, so use this helper for code deduplication.
+// TODO Locking of some type!
+template <ArtJvmtiEvent kEvent>
+inline void EventHandler::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) const {
+ static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event");
+ DCHECK(*new_class_data == nullptr);
+ jint current_len = class_data_len;
+ unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
+ ArtJvmTiEnv* last_env = nullptr;
+ for (ArtJvmTiEnv* env : envs) {
+ if (env == nullptr) {
+ continue;
+ }
+ if (ShouldDispatch<kEvent>(env, thread)) {
+ ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+ jnienv->ExceptionClear();
+ jint new_len = 0;
+ unsigned char* new_data = nullptr;
+ auto callback = impl::GetCallback<kEvent>(env);
+ callback(env,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ current_len,
+ current_class_data,
+ &new_len,
+ &new_data);
+ if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+ jnienv->Throw(thr.get());
+ }
+ if (new_data != nullptr && new_data != current_class_data) {
+ // Destroy the data the last transformer made. We skip this if the previous state was the
+ // initial one since we don't know here which jvmtiEnv allocated it.
+ // NB Currently this doesn't matter since all allocations just go to malloc but in the
+ // future we might have jvmtiEnv's keep track of their allocations for leak-checking.
+ if (last_env != nullptr) {
+ last_env->Deallocate(current_class_data);
+ }
+ last_env = env;
+ current_class_data = new_data;
+ current_len = new_len;
+ }
+ }
+ }
+ if (last_env != nullptr) {
+ *new_class_data_len = current_len;
+ *new_class_data = current_class_data;
+ }
+}
+
+// Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match
+// exactly the argument types of the corresponding Jvmti kEvent function pointer.
+
+template <ArtJvmtiEvent kEvent, typename ...Args>
+inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const {
+ for (ArtJvmTiEnv* env : envs) {
+ if (env != nullptr) {
+ DispatchEvent<kEvent, Args...>(env, thread, args...);
+ }
+ }
+}
+
+// Events with JNIEnvs need to stash pending exceptions since they can cause new ones to be thrown.
+// In accordance with the JVMTI specification we allow exceptions originating from events to
+// overwrite the current exception, including exceptions originating from earlier events.
+// TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list of the
+// newest exception.
+template <ArtJvmtiEvent kEvent, typename ...Args>
+inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const {
+ for (ArtJvmTiEnv* env : envs) {
+ if (env != nullptr) {
+ ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+ jnienv->ExceptionClear();
+ DispatchEvent<kEvent, JNIEnv*, Args...>(env, thread, jnienv, args...);
+ if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+ jnienv->Throw(thr.get());
+ }
+ }
+ }
+}
+
+template <ArtJvmtiEvent kEvent, typename ...Args>
+inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const {
+ using FnType = void(jvmtiEnv*, Args...);
+ if (ShouldDispatch<kEvent>(env, thread)) {
+ FnType* callback = impl::GetCallback<kEvent>(env);
+ if (callback != nullptr) {
+ (*callback)(env, args...);
+ }
+ }
+}
+
+// Need to give custom specializations for Breakpoint since it needs to filter out which particular
+// methods/dex_pcs agents get notified on.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kBreakpoint>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID jmethod,
+ jlocation location) const {
+ art::ArtMethod* method = art::jni::DecodeArtMethod(jmethod);
+ for (ArtJvmTiEnv* env : envs) {
+ // Search for a breakpoint on this particular method and location.
+ if (env != nullptr &&
+ ShouldDispatch<ArtJvmtiEvent::kBreakpoint>(env, thread) &&
+ env->breakpoints.find({method, location}) != env->breakpoints.end()) {
+ // We temporarily clear any pending exceptions so the event can call back into java code.
+ ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+ jnienv->ExceptionClear();
+ auto callback = impl::GetCallback<ArtJvmtiEvent::kBreakpoint>(env);
+ (*callback)(env, jnienv, jni_thread, jmethod, location);
+ if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+ jnienv->Throw(thr.get());
+ }
+ }
+ }
+}
+
+// Need to give custom specializations for FieldAccess and FieldModification since they need to
+// filter out which particular fields agents want to get notified on.
+// TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This
+// could make the system more performant.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFieldModification>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID method,
+ jlocation location,
+ jclass field_klass,
+ jobject object,
+ jfieldID field,
+ char type_char,
+ jvalue val) const {
+ for (ArtJvmTiEnv* env : envs) {
+ if (env != nullptr &&
+ ShouldDispatch<ArtJvmtiEvent::kFieldModification>(env, thread) &&
+ env->modify_watched_fields.find(
+ art::jni::DecodeArtField(field)) != env->modify_watched_fields.end()) {
+ ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+ jnienv->ExceptionClear();
+ auto callback = impl::GetCallback<ArtJvmtiEvent::kFieldModification>(env);
+ (*callback)(env,
+ jnienv,
+ jni_thread,
+ method,
+ location,
+ field_klass,
+ object,
+ field,
+ type_char,
+ val);
+ if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+ jnienv->Throw(thr.get());
+ }
+ }
+ }
+}
+
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFieldAccess>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID method,
+ jlocation location,
+ jclass field_klass,
+ jobject object,
+ jfieldID field) const {
+ for (ArtJvmTiEnv* env : envs) {
+ if (env != nullptr &&
+ ShouldDispatch<ArtJvmtiEvent::kFieldAccess>(env, thread) &&
+ env->access_watched_fields.find(
+ art::jni::DecodeArtField(field)) != env->access_watched_fields.end()) {
+ ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+ jnienv->ExceptionClear();
+ auto callback = impl::GetCallback<ArtJvmtiEvent::kFieldAccess>(env);
+ (*callback)(env, jnienv, jni_thread, method, location, field_klass, object, field);
+ if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+ jnienv->Throw(thr.get());
+ }
+ }
+ }
+}
+
+// Need to give a custom specialization for NativeMethodBind since it has to deal with an out
+// variable.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID method,
+ void* cur_method,
+ void** new_method) const {
+ *new_method = cur_method;
+ for (ArtJvmTiEnv* env : envs) {
+ if (env != nullptr && ShouldDispatch<ArtJvmtiEvent::kNativeMethodBind>(env, thread)) {
+ auto callback = impl::GetCallback<ArtJvmtiEvent::kNativeMethodBind>(env);
+ (*callback)(env, jnienv, jni_thread, method, cur_method, new_method);
+ if (*new_method != nullptr) {
+ cur_method = *new_method;
+ }
+ }
+ }
+}
+
+// C++ does not allow partial template function specialization. The dispatch for our separated
+// ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper.
+// The following two DispatchEvent specializations dispatch to it.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ 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) const {
+ return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ thread,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ class_data_len,
+ class_data,
+ new_class_data_len,
+ new_class_data);
+}
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+ 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) const {
+ return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+ thread,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ class_data_len,
+ class_data,
+ new_class_data_len,
+ new_class_data);
+}
+
+template <ArtJvmtiEvent kEvent>
+inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env,
+ art::Thread* thread) {
+ bool dispatch = env->event_masks.global_event_mask.Test(kEvent);
+
+ if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) {
+ EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
+ dispatch = mask != nullptr && mask->Test(kEvent);
+ }
+ return dispatch;
+}
+
+inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
+ bool union_value = false;
+ for (const ArtJvmTiEnv* stored_env : envs) {
+ if (stored_env == nullptr) {
+ continue;
+ }
+ union_value |= stored_env->event_masks.global_event_mask.Test(event);
+ union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
+ if (union_value) {
+ break;
+ }
+ }
+ global_mask.Set(event, union_value);
+}
+
+inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
+ const jvmtiCapabilities& caps,
+ bool added) {
+ ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
+ : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+ return (added && caps.can_access_local_variables == 1) ||
+ (caps.can_retransform_classes == 1 &&
+ IsEventEnabledAnywhere(event) &&
+ env->event_masks.IsEnabledAnywhere(event));
+}
+
+inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
+ const jvmtiCapabilities& caps,
+ bool added) {
+ if (UNLIKELY(NeedsEventUpdate(env, caps, added))) {
+ env->event_masks.HandleChangedCapabilities(caps, added);
+ if (caps.can_retransform_classes == 1) {
+ RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
+ RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ }
+ if (added && caps.can_access_local_variables == 1) {
+ HandleLocalAccessCapabilityAdded();
+ }
+ }
+}
+
+} // namespace openjdkjvmti
+
+#endif // ART_OPENJDKJVMTI_EVENTS_INL_H_