diff options
Diffstat (limited to 'runtime/openjdkjvmti')
24 files changed, 2307 insertions, 956 deletions
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index c01e3f4152..e38f265c5a 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -13,11 +13,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +cc_library_headers { + name: "libopenjdkjvmti_headers", + host_supported: true, + export_include_dirs: ["include"], +} + cc_defaults { name: "libopenjdkjvmti_defaults", defaults: ["art_defaults"], host_supported: true, srcs: ["events.cc", + "fixed_up_dex_file.cc", "object_tagging.cc", "OpenjdkJvmTi.cc", "ti_class.cc", @@ -40,6 +47,7 @@ cc_defaults { "ti_timers.cc", "transform.cc"], include_dirs: ["art/runtime"], + header_libs: ["libopenjdkjvmti_headers"], shared_libs: [ "libbase", "libnativehelper", @@ -49,7 +57,10 @@ cc_defaults { art_cc_library { name: "libopenjdkjvmti", defaults: ["libopenjdkjvmti_defaults"], - shared_libs: ["libart"], + shared_libs: [ + "libart", + "libart-compiler", + ], } art_cc_library { @@ -58,5 +69,8 @@ art_cc_library { "art_debug_defaults", "libopenjdkjvmti_defaults", ], - shared_libs: ["libartd"], + shared_libs: [ + "libartd", + "libartd-compiler", + ], } diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 450b6b6bcf..39e603e1e7 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -35,7 +35,7 @@ #include <jni.h> -#include "openjdkjvmti/jvmti.h" +#include "jvmti.h" #include "art_jvmti.h" #include "base/logging.h" @@ -66,10 +66,6 @@ #include "ti_timers.h" #include "transform.h" -// TODO Remove this at some point by annotating all the methods. It was put in to make the skeleton -// easier to create. -#pragma GCC diagnostic ignored "-Wunused-parameter" - namespace openjdkjvmti { EventHandler gEventHandler; @@ -83,20 +79,26 @@ EventHandler gEventHandler; class JvmtiFunctions { private: - static bool IsValidEnv(jvmtiEnv* env) { - return env != nullptr; + static jvmtiError getEnvironmentError(jvmtiEnv* env) { + if (env == nullptr) { + return ERR(INVALID_ENVIRONMENT); + } else if (art::Thread::Current() == nullptr) { + return ERR(UNATTACHED_THREAD); + } else { + return OK; + } } -#define ENSURE_VALID_ENV(env) \ - do { \ - if (!IsValidEnv(env)) { \ - return ERR(INVALID_ENVIRONMENT); \ - } \ +#define ENSURE_VALID_ENV(env) \ + do { \ + jvmtiError ensure_valid_env_ ## __LINE__ = getEnvironmentError(env); \ + if (ensure_valid_env_ ## __LINE__ != OK) { \ + return ensure_valid_env_ ## __LINE__ ; \ + } \ } while (false) #define ENSURE_HAS_CAP(env, cap) \ do { \ - ENSURE_VALID_ENV(env); \ if (ArtJvmTiEnv::AsArtJvmTiEnv(env)->capabilities.cap != 1) { \ return ERR(MUST_POSSESS_CAPABILITY); \ } \ @@ -125,76 +127,92 @@ class JvmtiFunctions { } static jvmtiError GetThreadState(jvmtiEnv* env, jthread thread, jint* thread_state_ptr) { + ENSURE_VALID_ENV(env); return ThreadUtil::GetThreadState(env, thread, thread_state_ptr); } static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr) { + ENSURE_VALID_ENV(env); return ThreadUtil::GetCurrentThread(env, thread_ptr); } static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr) { + ENSURE_VALID_ENV(env); return ThreadUtil::GetAllThreads(env, threads_count_ptr, threads_ptr); } - static jvmtiError SuspendThread(jvmtiEnv* env, jthread thread) { + static jvmtiError SuspendThread(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_suspend); return ERR(NOT_IMPLEMENTED); } static jvmtiError SuspendThreadList(jvmtiEnv* env, - jint request_count, - const jthread* request_list, - jvmtiError* results) { + jint request_count ATTRIBUTE_UNUSED, + const jthread* request_list ATTRIBUTE_UNUSED, + jvmtiError* results ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_suspend); return ERR(NOT_IMPLEMENTED); } - static jvmtiError ResumeThread(jvmtiEnv* env, jthread thread) { + static jvmtiError ResumeThread(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_suspend); return ERR(NOT_IMPLEMENTED); } static jvmtiError ResumeThreadList(jvmtiEnv* env, - jint request_count, - const jthread* request_list, - jvmtiError* results) { + jint request_count ATTRIBUTE_UNUSED, + const jthread* request_list ATTRIBUTE_UNUSED, + jvmtiError* results ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_suspend); return ERR(NOT_IMPLEMENTED); } - static jvmtiError StopThread(jvmtiEnv* env, jthread thread, jobject exception) { + static jvmtiError StopThread(jvmtiEnv* env, + jthread thread ATTRIBUTE_UNUSED, + jobject exception ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_signal_thread); return ERR(NOT_IMPLEMENTED); } - static jvmtiError InterruptThread(jvmtiEnv* env, jthread thread) { + static jvmtiError InterruptThread(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_signal_thread); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr) { + ENSURE_VALID_ENV(env); return ThreadUtil::GetThreadInfo(env, thread, info_ptr); } static jvmtiError GetOwnedMonitorInfo(jvmtiEnv* env, - jthread thread, - jint* owned_monitor_count_ptr, - jobject** owned_monitors_ptr) { + jthread thread ATTRIBUTE_UNUSED, + jint* owned_monitor_count_ptr ATTRIBUTE_UNUSED, + jobject** owned_monitors_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_owned_monitor_info); return ERR(NOT_IMPLEMENTED); } - static jvmtiError GetOwnedMonitorStackDepthInfo(jvmtiEnv* env, - jthread thread, - jint* monitor_info_count_ptr, - jvmtiMonitorStackDepthInfo** monitor_info_ptr) { + static jvmtiError GetOwnedMonitorStackDepthInfo( + jvmtiEnv* env, + jthread thread ATTRIBUTE_UNUSED, + jint* monitor_info_count_ptr ATTRIBUTE_UNUSED, + jvmtiMonitorStackDepthInfo** monitor_info_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_owned_monitor_stack_depth_info); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetCurrentContendedMonitor(jvmtiEnv* env, - jthread thread, - jobject* monitor_ptr) { + jthread thread ATTRIBUTE_UNUSED, + jobject* monitor_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_current_contended_monitor); return ERR(NOT_IMPLEMENTED); } @@ -204,26 +222,31 @@ class JvmtiFunctions { jvmtiStartFunction proc, const void* arg, jint priority) { + ENSURE_VALID_ENV(env); return ThreadUtil::RunAgentThread(env, thread, proc, arg, priority); } static jvmtiError SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data) { + ENSURE_VALID_ENV(env); return ThreadUtil::SetThreadLocalStorage(env, thread, data); } static jvmtiError GetThreadLocalStorage(jvmtiEnv* env, jthread thread, void** data_ptr) { + ENSURE_VALID_ENV(env); return ThreadUtil::GetThreadLocalStorage(env, thread, data_ptr); } static jvmtiError GetTopThreadGroups(jvmtiEnv* env, jint* group_count_ptr, jthreadGroup** groups_ptr) { + ENSURE_VALID_ENV(env); return ThreadGroupUtil::GetTopThreadGroups(env, group_count_ptr, groups_ptr); } static jvmtiError GetThreadGroupInfo(jvmtiEnv* env, jthreadGroup group, jvmtiThreadGroupInfo* info_ptr) { + ENSURE_VALID_ENV(env); return ThreadGroupUtil::GetThreadGroupInfo(env, group, info_ptr); } @@ -233,6 +256,7 @@ class JvmtiFunctions { jthread** threads_ptr, jint* group_count_ptr, jthreadGroup** groups_ptr) { + ENSURE_VALID_ENV(env); return ThreadGroupUtil::GetThreadGroupChildren(env, group, thread_count_ptr, @@ -247,6 +271,7 @@ class JvmtiFunctions { jint max_frame_count, jvmtiFrameInfo* frame_buffer, jint* count_ptr) { + ENSURE_VALID_ENV(env); return StackUtil::GetStackTrace(env, thread, start_depth, @@ -259,6 +284,7 @@ class JvmtiFunctions { jint max_frame_count, jvmtiStackInfo** stack_info_ptr, jint* thread_count_ptr) { + ENSURE_VALID_ENV(env); return StackUtil::GetAllStackTraces(env, max_frame_count, stack_info_ptr, thread_count_ptr); } @@ -267,6 +293,7 @@ class JvmtiFunctions { const jthread* thread_list, jint max_frame_count, jvmtiStackInfo** stack_info_ptr) { + ENSURE_VALID_ENV(env); return StackUtil::GetThreadListStackTraces(env, thread_count, thread_list, @@ -275,10 +302,12 @@ class JvmtiFunctions { } static jvmtiError GetFrameCount(jvmtiEnv* env, jthread thread, jint* count_ptr) { + ENSURE_VALID_ENV(env); return StackUtil::GetFrameCount(env, thread, count_ptr); } - static jvmtiError PopFrame(jvmtiEnv* env, jthread thread) { + static jvmtiError PopFrame(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_pop_frame); return ERR(NOT_IMPLEMENTED); } @@ -288,40 +317,60 @@ class JvmtiFunctions { jint depth, jmethodID* method_ptr, jlocation* location_ptr) { + ENSURE_VALID_ENV(env); return StackUtil::GetFrameLocation(env, thread, depth, method_ptr, location_ptr); } - static jvmtiError NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) { + static jvmtiError NotifyFramePop(jvmtiEnv* env, + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_frame_pop_events); return ERR(NOT_IMPLEMENTED); } - static jvmtiError ForceEarlyReturnObject(jvmtiEnv* env, jthread thread, jobject value) { + static jvmtiError ForceEarlyReturnObject(jvmtiEnv* env, + jthread thread ATTRIBUTE_UNUSED, + jobject value ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_force_early_return); return ERR(NOT_IMPLEMENTED); } - static jvmtiError ForceEarlyReturnInt(jvmtiEnv* env, jthread thread, jint value) { + static jvmtiError ForceEarlyReturnInt(jvmtiEnv* env, + jthread thread ATTRIBUTE_UNUSED, + jint value ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_force_early_return); return ERR(NOT_IMPLEMENTED); } - static jvmtiError ForceEarlyReturnLong(jvmtiEnv* env, jthread thread, jlong value) { + static jvmtiError ForceEarlyReturnLong(jvmtiEnv* env, + jthread thread ATTRIBUTE_UNUSED, + jlong value ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_force_early_return); return ERR(NOT_IMPLEMENTED); } - static jvmtiError ForceEarlyReturnFloat(jvmtiEnv* env, jthread thread, jfloat value) { + static jvmtiError ForceEarlyReturnFloat(jvmtiEnv* env, + jthread thread ATTRIBUTE_UNUSED, + jfloat value ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_force_early_return); return ERR(NOT_IMPLEMENTED); } - static jvmtiError ForceEarlyReturnDouble(jvmtiEnv* env, jthread thread, jdouble value) { + static jvmtiError ForceEarlyReturnDouble(jvmtiEnv* env, + jthread thread ATTRIBUTE_UNUSED, + jdouble value ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_force_early_return); return ERR(NOT_IMPLEMENTED); } - static jvmtiError ForceEarlyReturnVoid(jvmtiEnv* env, jthread thread) { + static jvmtiError ForceEarlyReturnVoid(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_force_early_return); return ERR(NOT_IMPLEMENTED); } @@ -332,6 +381,7 @@ class JvmtiFunctions { jobject initial_object, const jvmtiHeapCallbacks* callbacks, const void* user_data) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_tag_objects); HeapUtil heap_util(ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get()); return heap_util.FollowReferences(env, @@ -347,12 +397,14 @@ class JvmtiFunctions { jclass klass, const jvmtiHeapCallbacks* callbacks, const void* user_data) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_tag_objects); HeapUtil heap_util(ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get()); return heap_util.IterateThroughHeap(env, heap_filter, klass, callbacks, user_data); } static jvmtiError GetTag(jvmtiEnv* env, jobject object, jlong* tag_ptr) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_tag_objects); JNIEnv* jni_env = GetJniEnv(env); @@ -370,6 +422,7 @@ class JvmtiFunctions { } static jvmtiError SetTag(jvmtiEnv* env, jobject object, jlong tag) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_tag_objects); if (object == nullptr) { @@ -394,6 +447,7 @@ class JvmtiFunctions { jint* count_ptr, jobject** object_result_ptr, jlong** tag_result_ptr) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_tag_objects); JNIEnv* jni_env = GetJniEnv(env); @@ -411,173 +465,210 @@ class JvmtiFunctions { } static jvmtiError ForceGarbageCollection(jvmtiEnv* env) { + ENSURE_VALID_ENV(env); return HeapUtil::ForceGarbageCollection(env); } static jvmtiError IterateOverObjectsReachableFromObject( jvmtiEnv* env, - jobject object, - jvmtiObjectReferenceCallback object_reference_callback, - const void* user_data) { + jobject object ATTRIBUTE_UNUSED, + jvmtiObjectReferenceCallback object_reference_callback ATTRIBUTE_UNUSED, + const void* user_data ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_tag_objects); return ERR(NOT_IMPLEMENTED); } - static jvmtiError IterateOverReachableObjects(jvmtiEnv* env, - jvmtiHeapRootCallback heap_root_callback, - jvmtiStackReferenceCallback stack_ref_callback, - jvmtiObjectReferenceCallback object_ref_callback, - const void* user_data) { + static jvmtiError IterateOverReachableObjects( + jvmtiEnv* env, + jvmtiHeapRootCallback heap_root_callback ATTRIBUTE_UNUSED, + jvmtiStackReferenceCallback stack_ref_callback ATTRIBUTE_UNUSED, + jvmtiObjectReferenceCallback object_ref_callback ATTRIBUTE_UNUSED, + const void* user_data ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_tag_objects); return ERR(NOT_IMPLEMENTED); } static jvmtiError IterateOverHeap(jvmtiEnv* env, - jvmtiHeapObjectFilter object_filter, - jvmtiHeapObjectCallback heap_object_callback, - const void* user_data) { + jvmtiHeapObjectFilter object_filter ATTRIBUTE_UNUSED, + jvmtiHeapObjectCallback heap_object_callback ATTRIBUTE_UNUSED, + const void* user_data ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_tag_objects); return ERR(NOT_IMPLEMENTED); } - static jvmtiError IterateOverInstancesOfClass(jvmtiEnv* env, - jclass klass, - jvmtiHeapObjectFilter object_filter, - jvmtiHeapObjectCallback heap_object_callback, - const void* user_data) { + static jvmtiError IterateOverInstancesOfClass( + jvmtiEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jvmtiHeapObjectFilter object_filter ATTRIBUTE_UNUSED, + jvmtiHeapObjectCallback heap_object_callback ATTRIBUTE_UNUSED, + const void* user_data ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_tag_objects); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetLocalObject(jvmtiEnv* env, - jthread thread, - jint depth, - jint slot, - jobject* value_ptr) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jint slot ATTRIBUTE_UNUSED, + jobject* value_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetLocalInstance(jvmtiEnv* env, - jthread thread, - jint depth, - jobject* value_ptr) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jobject* value_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetLocalInt(jvmtiEnv* env, - jthread thread, - jint depth, - jint slot, - jint* value_ptr) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jint slot ATTRIBUTE_UNUSED, + jint* value_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetLocalLong(jvmtiEnv* env, - jthread thread, - jint depth, - jint slot, - jlong* value_ptr) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jint slot ATTRIBUTE_UNUSED, + jlong* value_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetLocalFloat(jvmtiEnv* env, - jthread thread, - jint depth, - jint slot, - jfloat* value_ptr) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jint slot ATTRIBUTE_UNUSED, + jfloat* value_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetLocalDouble(jvmtiEnv* env, - jthread thread, - jint depth, - jint slot, - jdouble* value_ptr) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jint slot ATTRIBUTE_UNUSED, + jdouble* value_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError SetLocalObject(jvmtiEnv* env, - jthread thread, - jint depth, - jint slot, - jobject value) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jint slot ATTRIBUTE_UNUSED, + jobject value ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError SetLocalInt(jvmtiEnv* env, - jthread thread, - jint depth, - jint slot, - jint value) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jint slot ATTRIBUTE_UNUSED, + jint value ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError SetLocalLong(jvmtiEnv* env, - jthread thread, - jint depth, - jint slot, - jlong value) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jint slot ATTRIBUTE_UNUSED, + jlong value ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError SetLocalFloat(jvmtiEnv* env, - jthread thread, - jint depth, - jint slot, - jfloat value) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jint slot ATTRIBUTE_UNUSED, + jfloat value ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError SetLocalDouble(jvmtiEnv* env, - jthread thread, - jint depth, - jint slot, - jdouble value) { + jthread thread ATTRIBUTE_UNUSED, + jint depth ATTRIBUTE_UNUSED, + jint slot ATTRIBUTE_UNUSED, + jdouble value ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } - static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) { + static jvmtiError SetBreakpoint(jvmtiEnv* env, + jmethodID method ATTRIBUTE_UNUSED, + jlocation location ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_breakpoint_events); return ERR(NOT_IMPLEMENTED); } - static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) { + static jvmtiError ClearBreakpoint(jvmtiEnv* env, + jmethodID method ATTRIBUTE_UNUSED, + jlocation location ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_breakpoint_events); return ERR(NOT_IMPLEMENTED); } - static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) { + static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jfieldID field ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_field_access_events); return ERR(NOT_IMPLEMENTED); } - static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) { + static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jfieldID field ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_field_access_events); return ERR(NOT_IMPLEMENTED); } - static jvmtiError SetFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field) { + static jvmtiError SetFieldModificationWatch(jvmtiEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jfieldID field ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_field_modification_events); return ERR(NOT_IMPLEMENTED); } - static jvmtiError ClearFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field) { + static jvmtiError ClearFieldModificationWatch(jvmtiEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jfieldID field ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_field_modification_events); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetLoadedClasses(jvmtiEnv* env, jint* class_count_ptr, jclass** classes_ptr) { + ENSURE_VALID_ENV(env); HeapUtil heap_util(ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get()); return heap_util.GetLoadedClasses(env, class_count_ptr, classes_ptr); } @@ -586,6 +677,7 @@ class JvmtiFunctions { jobject initiating_loader, jint* class_count_ptr, jclass** classes_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::GetClassLoaderClasses(env, initiating_loader, class_count_ptr, classes_ptr); } @@ -593,19 +685,25 @@ class JvmtiFunctions { jclass klass, char** signature_ptr, char** generic_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::GetClassSignature(env, klass, signature_ptr, generic_ptr); } static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::GetClassStatus(env, klass, status_ptr); } - static jvmtiError GetSourceFileName(jvmtiEnv* env, jclass klass, char** source_name_ptr) { + static jvmtiError GetSourceFileName(jvmtiEnv* env, + jclass klass ATTRIBUTE_UNUSED, + char** source_name_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_source_file_name); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::GetClassModifiers(env, klass, modifiers_ptr); } @@ -613,6 +711,7 @@ class JvmtiFunctions { jclass klass, jint* method_count_ptr, jmethodID** methods_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::GetClassMethods(env, klass, method_count_ptr, methods_ptr); } @@ -620,6 +719,7 @@ class JvmtiFunctions { jclass klass, jint* field_count_ptr, jfieldID** fields_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::GetClassFields(env, klass, field_count_ptr, fields_ptr); } @@ -627,6 +727,7 @@ class JvmtiFunctions { jclass klass, jint* interface_count_ptr, jclass** interfaces_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::GetImplementedInterfaces(env, klass, interface_count_ptr, interfaces_ptr); } @@ -634,46 +735,54 @@ class JvmtiFunctions { jclass klass, jint* minor_version_ptr, jint* major_version_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::GetClassVersionNumbers(env, klass, minor_version_ptr, major_version_ptr); } static jvmtiError GetConstantPool(jvmtiEnv* env, - jclass klass, - jint* constant_pool_count_ptr, - jint* constant_pool_byte_count_ptr, - unsigned char** constant_pool_bytes_ptr) { + jclass klass ATTRIBUTE_UNUSED, + jint* constant_pool_count_ptr ATTRIBUTE_UNUSED, + jint* constant_pool_byte_count_ptr ATTRIBUTE_UNUSED, + unsigned char** constant_pool_bytes_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_constant_pool); return ERR(NOT_IMPLEMENTED); } static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::IsInterface(env, klass, is_interface_ptr); } static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::IsArrayClass(env, klass, is_array_class_ptr); } static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_modifiable_class_ptr) { + ENSURE_VALID_ENV(env); return Redefiner::IsModifiableClass(env, klass, is_modifiable_class_ptr); } static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr) { + ENSURE_VALID_ENV(env); return ClassUtil::GetClassLoader(env, klass, classloader_ptr); } static jvmtiError GetSourceDebugExtension(jvmtiEnv* env, - jclass klass, - char** source_debug_extension_ptr) { + jclass klass ATTRIBUTE_UNUSED, + char** source_debug_extension_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_source_debug_extension); return ERR(NOT_IMPLEMENTED); } static jvmtiError RetransformClasses(jvmtiEnv* env, jint class_count, const jclass* classes) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_retransform_classes); std::string error_msg; jvmtiError res = Transformer::RetransformClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env), @@ -692,6 +801,7 @@ class JvmtiFunctions { static jvmtiError RedefineClasses(jvmtiEnv* env, jint class_count, const jvmtiClassDefinition* class_definitions) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_redefine_classes); std::string error_msg; jvmtiError res = Redefiner::RedefineClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env), @@ -708,16 +818,19 @@ class JvmtiFunctions { } static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr) { + ENSURE_VALID_ENV(env); return ObjectUtil::GetObjectSize(env, object, size_ptr); } static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr) { + ENSURE_VALID_ENV(env); return ObjectUtil::GetObjectHashCode(env, object, hash_code_ptr); } static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env, - jobject object, - jvmtiMonitorUsage* info_ptr) { + jobject object ATTRIBUTE_UNUSED, + jvmtiMonitorUsage* info_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_monitor_info); return ERR(NOT_IMPLEMENTED); } @@ -728,6 +841,7 @@ class JvmtiFunctions { char** name_ptr, char** signature_ptr, char** generic_ptr) { + ENSURE_VALID_ENV(env); return FieldUtil::GetFieldName(env, klass, field, name_ptr, signature_ptr, generic_ptr); } @@ -735,6 +849,7 @@ class JvmtiFunctions { jclass klass, jfieldID field, jclass* declaring_class_ptr) { + ENSURE_VALID_ENV(env); return FieldUtil::GetFieldDeclaringClass(env, klass, field, declaring_class_ptr); } @@ -742,6 +857,7 @@ class JvmtiFunctions { jclass klass, jfieldID field, jint* modifiers_ptr) { + ENSURE_VALID_ENV(env); return FieldUtil::GetFieldModifiers(env, klass, field, modifiers_ptr); } @@ -749,6 +865,7 @@ class JvmtiFunctions { jclass klass, jfieldID field, jboolean* is_synthetic_ptr) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_synthetic_attribute); return FieldUtil::IsFieldSynthetic(env, klass, field, is_synthetic_ptr); } @@ -758,30 +875,35 @@ class JvmtiFunctions { char** name_ptr, char** signature_ptr, char** generic_ptr) { + ENSURE_VALID_ENV(env); return MethodUtil::GetMethodName(env, method, name_ptr, signature_ptr, generic_ptr); } static jvmtiError GetMethodDeclaringClass(jvmtiEnv* env, jmethodID method, jclass* declaring_class_ptr) { + ENSURE_VALID_ENV(env); return MethodUtil::GetMethodDeclaringClass(env, method, declaring_class_ptr); } static jvmtiError GetMethodModifiers(jvmtiEnv* env, jmethodID method, jint* modifiers_ptr) { + ENSURE_VALID_ENV(env); return MethodUtil::GetMethodModifiers(env, method, modifiers_ptr); } static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr) { + ENSURE_VALID_ENV(env); return MethodUtil::GetMaxLocals(env, method, max_ptr); } static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr) { + ENSURE_VALID_ENV(env); return MethodUtil::GetArgumentsSize(env, method, size_ptr); } @@ -789,6 +911,7 @@ class JvmtiFunctions { jmethodID method, jint* entry_count_ptr, jvmtiLineNumberEntry** table_ptr) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_line_numbers); return MethodUtil::GetLineNumberTable(env, method, entry_count_ptr, table_ptr); } @@ -797,81 +920,100 @@ class JvmtiFunctions { jmethodID method, jlocation* start_location_ptr, jlocation* end_location_ptr) { + ENSURE_VALID_ENV(env); return MethodUtil::GetMethodLocation(env, method, start_location_ptr, end_location_ptr); } static jvmtiError GetLocalVariableTable(jvmtiEnv* env, - jmethodID method, - jint* entry_count_ptr, - jvmtiLocalVariableEntry** table_ptr) { + jmethodID method ATTRIBUTE_UNUSED, + jint* entry_count_ptr ATTRIBUTE_UNUSED, + jvmtiLocalVariableEntry** table_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetBytecodes(jvmtiEnv* env, - jmethodID method, - jint* bytecode_count_ptr, - unsigned char** bytecodes_ptr) { + jmethodID method ATTRIBUTE_UNUSED, + jint* bytecode_count_ptr ATTRIBUTE_UNUSED, + unsigned char** bytecodes_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_bytecodes); return ERR(NOT_IMPLEMENTED); } static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) { + ENSURE_VALID_ENV(env); return MethodUtil::IsMethodNative(env, method, is_native_ptr); } static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_synthetic_attribute); return MethodUtil::IsMethodSynthetic(env, method, is_synthetic_ptr); } static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr) { + ENSURE_VALID_ENV(env); return MethodUtil::IsMethodObsolete(env, method, is_obsolete_ptr); } - static jvmtiError SetNativeMethodPrefix(jvmtiEnv* env, const char* prefix) { + static jvmtiError SetNativeMethodPrefix(jvmtiEnv* env, const char* prefix ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_set_native_method_prefix); return ERR(NOT_IMPLEMENTED); } - static jvmtiError SetNativeMethodPrefixes(jvmtiEnv* env, jint prefix_count, char** prefixes) { + static jvmtiError SetNativeMethodPrefixes(jvmtiEnv* env, + jint prefix_count ATTRIBUTE_UNUSED, + char** prefixes ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_set_native_method_prefix); return ERR(NOT_IMPLEMENTED); } static jvmtiError CreateRawMonitor(jvmtiEnv* env, const char* name, jrawMonitorID* monitor_ptr) { + ENSURE_VALID_ENV(env); return MonitorUtil::CreateRawMonitor(env, name, monitor_ptr); } static jvmtiError DestroyRawMonitor(jvmtiEnv* env, jrawMonitorID monitor) { + ENSURE_VALID_ENV(env); return MonitorUtil::DestroyRawMonitor(env, monitor); } static jvmtiError RawMonitorEnter(jvmtiEnv* env, jrawMonitorID monitor) { + ENSURE_VALID_ENV(env); return MonitorUtil::RawMonitorEnter(env, monitor); } static jvmtiError RawMonitorExit(jvmtiEnv* env, jrawMonitorID monitor) { + ENSURE_VALID_ENV(env); return MonitorUtil::RawMonitorExit(env, monitor); } static jvmtiError RawMonitorWait(jvmtiEnv* env, jrawMonitorID monitor, jlong millis) { + ENSURE_VALID_ENV(env); return MonitorUtil::RawMonitorWait(env, monitor, millis); } static jvmtiError RawMonitorNotify(jvmtiEnv* env, jrawMonitorID monitor) { + ENSURE_VALID_ENV(env); return MonitorUtil::RawMonitorNotify(env, monitor); } static jvmtiError RawMonitorNotifyAll(jvmtiEnv* env, jrawMonitorID monitor) { + ENSURE_VALID_ENV(env); return MonitorUtil::RawMonitorNotifyAll(env, monitor); } static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table) { + ENSURE_VALID_ENV(env); return JNIUtil::SetJNIFunctionTable(env, function_table); } static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) { + ENSURE_VALID_ENV(env); return JNIUtil::GetJNIFunctionTable(env, function_table); } @@ -926,13 +1068,16 @@ class JvmtiFunctions { return gEventHandler.SetEvent(art_env, art_thread, GetArtJvmtiEvent(art_env, event_type), mode); } - static jvmtiError GenerateEvents(jvmtiEnv* env, jvmtiEvent event_type) { - return ERR(NOT_IMPLEMENTED); + static jvmtiError GenerateEvents(jvmtiEnv* env, + jvmtiEvent event_type ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); + return OK; } static jvmtiError GetExtensionFunctions(jvmtiEnv* env, jint* extension_count_ptr, jvmtiExtensionFunctionInfo** extensions) { + ENSURE_VALID_ENV(env); // We do not have any extension functions. *extension_count_ptr = 0; *extensions = nullptr; @@ -943,6 +1088,7 @@ class JvmtiFunctions { static jvmtiError GetExtensionEvents(jvmtiEnv* env, jint* extension_count_ptr, jvmtiExtensionEventInfo** extensions) { + ENSURE_VALID_ENV(env); // We do not have any extension events. *extension_count_ptr = 0; *extensions = nullptr; @@ -951,8 +1097,9 @@ class JvmtiFunctions { } static jvmtiError SetExtensionEventCallback(jvmtiEnv* env, - jint extension_event_index, - jvmtiExtensionEvent callback) { + jint extension_event_index ATTRIBUTE_UNUSED, + jvmtiExtensionEvent callback ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); // We do not have any extension events, so any call is illegal. return ERR(ILLEGAL_ARGUMENT); } @@ -969,11 +1116,16 @@ class JvmtiFunctions { ENSURE_NON_NULL(capabilities_ptr); ArtJvmTiEnv* art_env = static_cast<ArtJvmTiEnv*>(env); jvmtiError ret = OK; - jvmtiCapabilities changed; + jvmtiCapabilities changed = {}; + jvmtiCapabilities potential_capabilities = {}; + ret = env->GetPotentialCapabilities(&potential_capabilities); + if (ret != OK) { + return ret; + } #define ADD_CAPABILITY(e) \ do { \ if (capabilities_ptr->e == 1) { \ - if (kPotentialCapabilities.e == 1) { \ + if (potential_capabilities.e == 1) { \ if (art_env->capabilities.e != 1) { \ art_env->capabilities.e = 1; \ changed.e = 1; \ @@ -1037,7 +1189,7 @@ class JvmtiFunctions { ENSURE_VALID_ENV(env); ENSURE_NON_NULL(capabilities_ptr); ArtJvmTiEnv* art_env = reinterpret_cast<ArtJvmTiEnv*>(env); - jvmtiCapabilities changed; + jvmtiCapabilities changed = {}; #define DEL_CAPABILITY(e) \ do { \ if (capabilities_ptr->e == 1) { \ @@ -1104,59 +1256,76 @@ class JvmtiFunctions { return OK; } - static jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiEnv* env, jvmtiTimerInfo* info_ptr) { + static jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiEnv* env, + jvmtiTimerInfo* info_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_current_thread_cpu_time); return ERR(NOT_IMPLEMENTED); } - static jvmtiError GetCurrentThreadCpuTime(jvmtiEnv* env, jlong* nanos_ptr) { + static jvmtiError GetCurrentThreadCpuTime(jvmtiEnv* env, jlong* nanos_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_current_thread_cpu_time); return ERR(NOT_IMPLEMENTED); } - static jvmtiError GetThreadCpuTimerInfo(jvmtiEnv* env, jvmtiTimerInfo* info_ptr) { + static jvmtiError GetThreadCpuTimerInfo(jvmtiEnv* env, + jvmtiTimerInfo* info_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_thread_cpu_time); return ERR(NOT_IMPLEMENTED); } - static jvmtiError GetThreadCpuTime(jvmtiEnv* env, jthread thread, jlong* nanos_ptr) { + static jvmtiError GetThreadCpuTime(jvmtiEnv* env, + jthread thread ATTRIBUTE_UNUSED, + jlong* nanos_ptr ATTRIBUTE_UNUSED) { + ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_thread_cpu_time); return ERR(NOT_IMPLEMENTED); } static jvmtiError GetTimerInfo(jvmtiEnv* env, jvmtiTimerInfo* info_ptr) { + ENSURE_VALID_ENV(env); return TimerUtil::GetTimerInfo(env, info_ptr); } static jvmtiError GetTime(jvmtiEnv* env, jlong* nanos_ptr) { + ENSURE_VALID_ENV(env); return TimerUtil::GetTime(env, nanos_ptr); } static jvmtiError GetAvailableProcessors(jvmtiEnv* env, jint* processor_count_ptr) { + ENSURE_VALID_ENV(env); return TimerUtil::GetAvailableProcessors(env, processor_count_ptr); } static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment) { + ENSURE_VALID_ENV(env); return SearchUtil::AddToBootstrapClassLoaderSearch(env, segment); } static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment) { + ENSURE_VALID_ENV(env); return SearchUtil::AddToSystemClassLoaderSearch(env, segment); } static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) { + ENSURE_VALID_ENV(env); return PropertiesUtil::GetSystemProperties(env, count_ptr, property_ptr); } static jvmtiError GetSystemProperty(jvmtiEnv* env, const char* property, char** value_ptr) { + ENSURE_VALID_ENV(env); return PropertiesUtil::GetSystemProperty(env, property, value_ptr); } static jvmtiError SetSystemProperty(jvmtiEnv* env, const char* property, const char* value) { + ENSURE_VALID_ENV(env); return PropertiesUtil::SetSystemProperty(env, property, value); } static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr) { + ENSURE_VALID_ENV(env); return PhaseUtil::GetPhase(env, phase_ptr); } @@ -1264,7 +1433,10 @@ class JvmtiFunctions { } } - static jvmtiError SetVerboseFlag(jvmtiEnv* env, jvmtiVerboseFlag flag, jboolean value) { + static jvmtiError SetVerboseFlag(jvmtiEnv* env, + jvmtiVerboseFlag flag, + jboolean value) { + ENSURE_VALID_ENV(env); if (flag == jvmtiVerboseFlag::JVMTI_VERBOSE_OTHER) { // OTHER is special, as it's 0, so can't do a bit check. bool val = (value == JNI_TRUE) ? true : false; @@ -1319,6 +1491,7 @@ class JvmtiFunctions { } static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) { + ENSURE_VALID_ENV(env); // Report BCI as jlocation format. We report dex bytecode indices. if (format_ptr == nullptr) { return ERR(NULL_POINTER); @@ -1340,8 +1513,8 @@ extern const jvmtiInterface_1 gJvmtiInterface; ArtJvmTiEnv::ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler) : art_vm(runtime), local_data(nullptr), - capabilities(), - object_tag_table(new ObjectTagTable(event_handler)) { + capabilities() { + object_tag_table = std::unique_ptr<ObjectTagTable>(new ObjectTagTable(event_handler, this)); functions = &gJvmtiInterface; } @@ -1384,6 +1557,7 @@ extern "C" bool ArtPlugin_Initialize() { ClassUtil::Register(&gEventHandler); DumpUtil::Register(&gEventHandler); SearchUtil::Register(); + HeapUtil::Register(); runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); @@ -1396,6 +1570,7 @@ extern "C" bool ArtPlugin_Deinitialize() { ClassUtil::Unregister(); DumpUtil::Unregister(); SearchUtil::Unregister(); + HeapUtil::Unregister(); return true; } diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h index 4f5eb0c33f..1ddbb869f9 100644 --- a/runtime/openjdkjvmti/events-inl.h +++ b/runtime/openjdkjvmti/events-inl.h @@ -126,6 +126,7 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, 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; @@ -168,15 +169,19 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, // 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 { - using FnType = void(jvmtiEnv*, Args...); +inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const { for (ArtJvmTiEnv* env : envs) { - if (ShouldDispatch<kEvent>(env, thread)) { - FnType* callback = impl::GetCallback<kEvent>(env); - if (callback != nullptr) { - (*callback)(env, args...); - } + DispatchEvent<kEvent, Args...>(env, thread, args...); + } +} + +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...); } } } diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h index 4e20d1776b..ae8bf0f803 100644 --- a/runtime/openjdkjvmti/events.h +++ b/runtime/openjdkjvmti/events.h @@ -156,9 +156,14 @@ class EventHandler { ArtJvmtiEvent event, jvmtiEventMode mode); + // Dispatch event to all registered environments. template <ArtJvmtiEvent kEvent, typename ...Args> ALWAYS_INLINE inline void DispatchEvent(art::Thread* thread, Args... args) const; + // Dispatch event to the given environment, only. + template <ArtJvmtiEvent kEvent, typename ...Args> + ALWAYS_INLINE + inline void DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const; // Tell the event handler capabilities were added/lost so it can adjust the sent events.If // caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false diff --git a/runtime/openjdkjvmti/fixed_up_dex_file.cc b/runtime/openjdkjvmti/fixed_up_dex_file.cc new file mode 100644 index 0000000000..3338358796 --- /dev/null +++ b/runtime/openjdkjvmti/fixed_up_dex_file.cc @@ -0,0 +1,145 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "fixed_up_dex_file.h" +#include "dex_file-inl.h" + +// Compiler includes. +#include "dex/dex_to_dex_decompiler.h" + +// Runtime includes. +#include "oat_file.h" +#include "vdex_file.h" + +namespace openjdkjvmti { + +static void RecomputeDexChecksum(art::DexFile* dex_file) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + reinterpret_cast<art::DexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()))->checksum_ = + dex_file->CalculateChecksum(); +} + +// TODO This is more complicated then it seems like it should be. +// The fact we don't keep around the data of where in the flat binary log of dex-quickening changes +// each dex file starts means we need to search for it. Since JVMTI is the exception though we are +// not going to put in the effort to optimize for it. +static void DoDexUnquicken(const art::DexFile& new_dex_file, + const art::DexFile& original_dex_file) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + const art::OatDexFile* oat_dex = original_dex_file.GetOatDexFile(); + if (oat_dex == nullptr) { + return; + } + const art::OatFile* oat_file = oat_dex->GetOatFile(); + if (oat_file == nullptr) { + return; + } + const art::VdexFile* vdex = oat_file->GetVdexFile(); + if (vdex == nullptr || vdex->GetQuickeningInfo().size() == 0) { + return; + } + const art::ArrayRef<const uint8_t> quickening_info(vdex->GetQuickeningInfo()); + const uint8_t* quickening_info_ptr = quickening_info.data(); + for (const art::OatDexFile* cur_oat_dex : oat_file->GetOatDexFiles()) { + std::string error; + std::unique_ptr<const art::DexFile> cur_dex_file(cur_oat_dex->OpenDexFile(&error)); + DCHECK(cur_dex_file.get() != nullptr); + // Is this the dex file we are looking for? + if (UNLIKELY(cur_dex_file->Begin() == original_dex_file.Begin())) { + // Simple sanity check. + CHECK_EQ(new_dex_file.NumClassDefs(), original_dex_file.NumClassDefs()); + for (uint32_t i = 0; i < new_dex_file.NumClassDefs(); ++i) { + const art::DexFile::ClassDef& class_def = new_dex_file.GetClassDef(i); + const uint8_t* class_data = new_dex_file.GetClassData(class_def); + if (class_data == nullptr) { + continue; + } + for (art::ClassDataItemIterator it(new_dex_file, class_data); it.HasNext(); it.Next()) { + if (it.IsAtMethod() && it.GetMethodCodeItem() != nullptr) { + uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr); + quickening_info_ptr += sizeof(uint32_t); + art::optimizer::ArtDecompileDEX( + *it.GetMethodCodeItem(), + art::ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size), + /*decompile_return_instruction*/true); + quickening_info_ptr += quickening_size; + } + } + } + // We don't need to bother looking through the rest of the dex-files. + break; + } else { + // Not the dex file we want. Skip over all the quickening info for all its classes. + for (uint32_t i = 0; i < cur_dex_file->NumClassDefs(); ++i) { + const art::DexFile::ClassDef& class_def = cur_dex_file->GetClassDef(i); + const uint8_t* class_data = cur_dex_file->GetClassData(class_def); + if (class_data == nullptr) { + continue; + } + for (art::ClassDataItemIterator it(*cur_dex_file, class_data); it.HasNext(); it.Next()) { + if (it.IsAtMethod() && it.GetMethodCodeItem() != nullptr) { + uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr); + quickening_info_ptr += sizeof(uint32_t); + quickening_info_ptr += quickening_size; + } + } + } + } + } +} + +std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original) { + // Copy the data into mutable memory. + std::vector<unsigned char> data; + data.resize(original.Size()); + memcpy(data.data(), original.Begin(), original.Size()); + std::string error; + std::unique_ptr<const art::DexFile> new_dex_file(art::DexFile::Open( + data.data(), + data.size(), + /*location*/"Unquickening_dexfile.dex", + /*location_checksum*/0, + /*oat_dex_file*/nullptr, + /*verify*/false, + /*verify_checksum*/false, + &error)); + if (new_dex_file.get() == nullptr) { + LOG(ERROR) << "Unable to open dex file from memory for unquickening! error: " << error; + return nullptr; + } + + DoDexUnquicken(*new_dex_file, original); + RecomputeDexChecksum(const_cast<art::DexFile*>(new_dex_file.get())); + std::unique_ptr<FixedUpDexFile> ret(new FixedUpDexFile(std::move(new_dex_file), std::move(data))); + return ret; +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/fixed_up_dex_file.h b/runtime/openjdkjvmti/fixed_up_dex_file.h new file mode 100644 index 0000000000..db12f489e9 --- /dev/null +++ b/runtime/openjdkjvmti/fixed_up_dex_file.h @@ -0,0 +1,82 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_ +#define ART_RUNTIME_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_ + +#include <memory> +#include <vector> + +#include "jni.h" +#include "jvmti.h" +#include "base/mutex.h" +#include "dex_file.h" + +namespace openjdkjvmti { + +// A holder for a DexFile that has been 'fixed up' to ensure it is fully compliant with the +// published standard (no internal/quick opcodes, all fields are the defined values, etc). This is +// used to ensure that agents get a consistent dex file regardless of what version of android they +// are running on. +class FixedUpDexFile { + public: + static std::unique_ptr<FixedUpDexFile> Create(const art::DexFile& original) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + const art::DexFile& GetDexFile() { + return *dex_file_; + } + + const unsigned char* Begin() { + return data_.data(); + } + + size_t Size() { + return data_.size(); + } + + private: + explicit FixedUpDexFile(std::unique_ptr<const art::DexFile> fixed_up_dex_file, + std::vector<unsigned char> data) + : dex_file_(std::move(fixed_up_dex_file)), + data_(std::move(data)) {} + + // the fixed up DexFile + std::unique_ptr<const art::DexFile> dex_file_; + // The backing data for dex_file_. + const std::vector<unsigned char> data_; + + DISALLOW_COPY_AND_ASSIGN(FixedUpDexFile); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_ diff --git a/runtime/openjdkjvmti/jvmti.h b/runtime/openjdkjvmti/include/jvmti.h index de07c163fc..de07c163fc 100644 --- a/runtime/openjdkjvmti/jvmti.h +++ b/runtime/openjdkjvmti/include/jvmti.h diff --git a/runtime/openjdkjvmti/jvmti_weak_table-inl.h b/runtime/openjdkjvmti/jvmti_weak_table-inl.h new file mode 100644 index 0000000000..f67fffccbb --- /dev/null +++ b/runtime/openjdkjvmti/jvmti_weak_table-inl.h @@ -0,0 +1,389 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_ +#define ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_ + +#include "jvmti_weak_table.h" + +#include <limits> + +#include "art_jvmti.h" +#include "base/logging.h" +#include "gc/allocation_listener.h" +#include "instrumentation.h" +#include "jni_env_ext-inl.h" +#include "jvmti_allocator.h" +#include "mirror/class.h" +#include "mirror/object.h" +#include "runtime.h" +#include "ScopedLocalRef.h" + +namespace openjdkjvmti { + +template <typename T> +void JvmtiWeakTable<T>::Lock() { + allow_disallow_lock_.ExclusiveLock(art::Thread::Current()); +} +template <typename T> +void JvmtiWeakTable<T>::Unlock() { + allow_disallow_lock_.ExclusiveUnlock(art::Thread::Current()); +} +template <typename T> +void JvmtiWeakTable<T>::AssertLocked() { + allow_disallow_lock_.AssertHeld(art::Thread::Current()); +} + +template <typename T> +void JvmtiWeakTable<T>::UpdateTableWithReadBarrier() { + update_since_last_sweep_ = true; + + auto WithReadBarrierUpdater = [&](const art::GcRoot<art::mirror::Object>& original_root, + art::mirror::Object* original_obj ATTRIBUTE_UNUSED) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return original_root.Read<art::kWithReadBarrier>(); + }; + + UpdateTableWith<decltype(WithReadBarrierUpdater), kIgnoreNull>(WithReadBarrierUpdater); +} + +template <typename T> +bool JvmtiWeakTable<T>::GetTagSlowPath(art::Thread* self, art::mirror::Object* obj, T* result) { + // Under concurrent GC, there is a window between moving objects and sweeping of system + // weaks in which mutators are active. We may receive a to-space object pointer in obj, + // but still have from-space pointers in the table. Explicitly update the table once. + // Note: this will keep *all* objects in the table live, but should be a rare occurrence. + UpdateTableWithReadBarrier(); + return GetTagLocked(self, obj, result); +} + +template <typename T> +bool JvmtiWeakTable<T>::Remove(art::mirror::Object* obj, /* out */ T* tag) { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + Wait(self); + + return RemoveLocked(self, obj, tag); +} +template <typename T> +bool JvmtiWeakTable<T>::RemoveLocked(art::mirror::Object* obj, T* tag) { + art::Thread* self = art::Thread::Current(); + allow_disallow_lock_.AssertHeld(self); + Wait(self); + + return RemoveLocked(self, obj, tag); +} + +template <typename T> +bool JvmtiWeakTable<T>::RemoveLocked(art::Thread* self, art::mirror::Object* obj, T* tag) { + auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj)); + if (it != tagged_objects_.end()) { + if (tag != nullptr) { + *tag = it->second; + } + tagged_objects_.erase(it); + return true; + } + + if (art::kUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) { + // Under concurrent GC, there is a window between moving objects and sweeping of system + // weaks in which mutators are active. We may receive a to-space object pointer in obj, + // but still have from-space pointers in the table. Explicitly update the table once. + // Note: this will keep *all* objects in the table live, but should be a rare occurrence. + + // Update the table. + UpdateTableWithReadBarrier(); + + // And try again. + return RemoveLocked(self, obj, tag); + } + + // Not in here. + return false; +} + +template <typename T> +bool JvmtiWeakTable<T>::Set(art::mirror::Object* obj, T new_tag) { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + Wait(self); + + return SetLocked(self, obj, new_tag); +} +template <typename T> +bool JvmtiWeakTable<T>::SetLocked(art::mirror::Object* obj, T new_tag) { + art::Thread* self = art::Thread::Current(); + allow_disallow_lock_.AssertHeld(self); + Wait(self); + + return SetLocked(self, obj, new_tag); +} + +template <typename T> +bool JvmtiWeakTable<T>::SetLocked(art::Thread* self, art::mirror::Object* obj, T new_tag) { + auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj)); + if (it != tagged_objects_.end()) { + it->second = new_tag; + return true; + } + + if (art::kUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) { + // Under concurrent GC, there is a window between moving objects and sweeping of system + // weaks in which mutators are active. We may receive a to-space object pointer in obj, + // but still have from-space pointers in the table. Explicitly update the table once. + // Note: this will keep *all* objects in the table live, but should be a rare occurrence. + + // Update the table. + UpdateTableWithReadBarrier(); + + // And try again. + return SetLocked(self, obj, new_tag); + } + + // New element. + auto insert_it = tagged_objects_.emplace(art::GcRoot<art::mirror::Object>(obj), new_tag); + DCHECK(insert_it.second); + return false; +} + +template <typename T> +void JvmtiWeakTable<T>::Sweep(art::IsMarkedVisitor* visitor) { + if (DoesHandleNullOnSweep()) { + SweepImpl<true>(visitor); + } else { + SweepImpl<false>(visitor); + } + + // Under concurrent GC, there is a window between moving objects and sweeping of system + // weaks in which mutators are active. We may receive a to-space object pointer in obj, + // but still have from-space pointers in the table. We explicitly update the table then + // to ensure we compare against to-space pointers. But we want to do this only once. Once + // sweeping is done, we know all objects are to-space pointers until the next GC cycle, + // so we re-enable the explicit update for the next marking. + update_since_last_sweep_ = false; +} + +template <typename T> +template <bool kHandleNull> +void JvmtiWeakTable<T>::SweepImpl(art::IsMarkedVisitor* visitor) { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + + auto IsMarkedUpdater = [&](const art::GcRoot<art::mirror::Object>& original_root ATTRIBUTE_UNUSED, + art::mirror::Object* original_obj) { + return visitor->IsMarked(original_obj); + }; + + UpdateTableWith<decltype(IsMarkedUpdater), + kHandleNull ? kCallHandleNull : kRemoveNull>(IsMarkedUpdater); +} + +template <typename T> +template <typename Updater, typename JvmtiWeakTable<T>::TableUpdateNullTarget kTargetNull> +ALWAYS_INLINE inline void JvmtiWeakTable<T>::UpdateTableWith(Updater& updater) { + // We optimistically hope that elements will still be well-distributed when re-inserting them. + // So play with the map mechanics, and postpone rehashing. This avoids the need of a side + // vector and two passes. + float original_max_load_factor = tagged_objects_.max_load_factor(); + tagged_objects_.max_load_factor(std::numeric_limits<float>::max()); + // For checking that a max load-factor actually does what we expect. + size_t original_bucket_count = tagged_objects_.bucket_count(); + + for (auto it = tagged_objects_.begin(); it != tagged_objects_.end();) { + DCHECK(!it->first.IsNull()); + art::mirror::Object* original_obj = it->first.template Read<art::kWithoutReadBarrier>(); + art::mirror::Object* target_obj = updater(it->first, original_obj); + if (original_obj != target_obj) { + if (kTargetNull == kIgnoreNull && target_obj == nullptr) { + // Ignore null target, don't do anything. + } else { + T tag = it->second; + it = tagged_objects_.erase(it); + if (target_obj != nullptr) { + tagged_objects_.emplace(art::GcRoot<art::mirror::Object>(target_obj), tag); + DCHECK_EQ(original_bucket_count, tagged_objects_.bucket_count()); + } else if (kTargetNull == kCallHandleNull) { + HandleNullSweep(tag); + } + continue; // Iterator was implicitly updated by erase. + } + } + it++; + } + + tagged_objects_.max_load_factor(original_max_load_factor); + // TODO: consider rehash here. +} + +template <typename T> +template <typename Storage, class Allocator> +struct JvmtiWeakTable<T>::ReleasableContainer { + using allocator_type = Allocator; + + explicit ReleasableContainer(const allocator_type& alloc, size_t reserve = 10) + : allocator(alloc), + data(reserve > 0 ? allocator.allocate(reserve) : nullptr), + size(0), + capacity(reserve) { + } + + ~ReleasableContainer() { + if (data != nullptr) { + allocator.deallocate(data, capacity); + capacity = 0; + size = 0; + } + } + + Storage* Release() { + Storage* tmp = data; + + data = nullptr; + size = 0; + capacity = 0; + + return tmp; + } + + void Resize(size_t new_capacity) { + CHECK_GT(new_capacity, capacity); + + Storage* tmp = allocator.allocate(new_capacity); + DCHECK(tmp != nullptr); + if (data != nullptr) { + memcpy(tmp, data, sizeof(Storage) * size); + } + Storage* old = data; + data = tmp; + allocator.deallocate(old, capacity); + capacity = new_capacity; + } + + void Pushback(const Storage& elem) { + if (size == capacity) { + size_t new_capacity = 2 * capacity + 1; + Resize(new_capacity); + } + data[size++] = elem; + } + + Allocator allocator; + Storage* data; + size_t size; + size_t capacity; +}; + +template <typename T> +jvmtiError JvmtiWeakTable<T>::GetTaggedObjects(jvmtiEnv* jvmti_env, + jint tag_count, + const T* tags, + jint* count_ptr, + jobject** object_result_ptr, + T** tag_result_ptr) { + if (tag_count < 0) { + return ERR(ILLEGAL_ARGUMENT); + } + if (tag_count > 0) { + for (size_t i = 0; i != static_cast<size_t>(tag_count); ++i) { + if (tags[i] == 0) { + return ERR(ILLEGAL_ARGUMENT); + } + } + } + if (tags == nullptr) { + return ERR(NULL_POINTER); + } + if (count_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + Wait(self); + + art::JNIEnvExt* jni_env = self->GetJniEnv(); + + constexpr size_t kDefaultSize = 10; + size_t initial_object_size; + size_t initial_tag_size; + if (tag_count == 0) { + initial_object_size = (object_result_ptr != nullptr) ? tagged_objects_.size() : 0; + initial_tag_size = (tag_result_ptr != nullptr) ? tagged_objects_.size() : 0; + } else { + initial_object_size = initial_tag_size = kDefaultSize; + } + JvmtiAllocator<void> allocator(jvmti_env); + ReleasableContainer<jobject, JvmtiAllocator<jobject>> selected_objects(allocator, + initial_object_size); + ReleasableContainer<T, JvmtiAllocator<T>> selected_tags(allocator, initial_tag_size); + + size_t count = 0; + for (auto& pair : tagged_objects_) { + bool select; + if (tag_count > 0) { + select = false; + for (size_t i = 0; i != static_cast<size_t>(tag_count); ++i) { + if (tags[i] == pair.second) { + select = true; + break; + } + } + } else { + select = true; + } + + if (select) { + art::mirror::Object* obj = pair.first.template Read<art::kWithReadBarrier>(); + if (obj != nullptr) { + count++; + if (object_result_ptr != nullptr) { + selected_objects.Pushback(jni_env->AddLocalReference<jobject>(obj)); + } + if (tag_result_ptr != nullptr) { + selected_tags.Pushback(pair.second); + } + } + } + } + + if (object_result_ptr != nullptr) { + *object_result_ptr = selected_objects.Release(); + } + if (tag_result_ptr != nullptr) { + *tag_result_ptr = selected_tags.Release(); + } + *count_ptr = static_cast<jint>(count); + return ERR(NONE); +} + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_ diff --git a/runtime/openjdkjvmti/jvmti_weak_table.h b/runtime/openjdkjvmti/jvmti_weak_table.h new file mode 100644 index 0000000000..eeea75aa9d --- /dev/null +++ b/runtime/openjdkjvmti/jvmti_weak_table.h @@ -0,0 +1,215 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_ +#define ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_ + +#include <unordered_map> + +#include "base/macros.h" +#include "base/mutex.h" +#include "gc/system_weak.h" +#include "gc_root-inl.h" +#include "globals.h" +#include "jvmti.h" +#include "mirror/object.h" +#include "thread-inl.h" + +namespace openjdkjvmti { + +class EventHandler; + +// A system-weak container mapping objects to elements of the template type. This corresponds +// to a weak hash map. For historical reasons the stored value is called "tag." +template <typename T> +class JvmtiWeakTable : public art::gc::SystemWeakHolder { + public: + JvmtiWeakTable() + : art::gc::SystemWeakHolder(art::kTaggingLockLevel), + update_since_last_sweep_(false) { + } + + // Remove the mapping for the given object, returning whether such a mapping existed (and the old + // value). + bool Remove(art::mirror::Object* obj, /* out */ T* tag) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_); + bool RemoveLocked(art::mirror::Object* obj, /* out */ T* tag) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(allow_disallow_lock_); + + // Set the mapping for the given object. Returns true if this overwrites an already existing + // mapping. + virtual bool Set(art::mirror::Object* obj, T tag) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_); + virtual bool SetLocked(art::mirror::Object* obj, T tag) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(allow_disallow_lock_); + + // Return the value associated with the given object. Returns true if the mapping exists, false + // otherwise. + bool GetTag(art::mirror::Object* obj, /* out */ T* result) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_) { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + Wait(self); + + return GetTagLocked(self, obj, result); + } + bool GetTagLocked(art::mirror::Object* obj, /* out */ T* result) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(allow_disallow_lock_) { + art::Thread* self = art::Thread::Current(); + allow_disallow_lock_.AssertHeld(self); + Wait(self); + + return GetTagLocked(self, obj, result); + } + + // Sweep the container. DO NOT CALL MANUALLY. + void Sweep(art::IsMarkedVisitor* visitor) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_); + + // Return all objects that have a value mapping in tags. + jvmtiError GetTaggedObjects(jvmtiEnv* jvmti_env, + jint tag_count, + const T* tags, + /* out */ jint* count_ptr, + /* out */ jobject** object_result_ptr, + /* out */ T** tag_result_ptr) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_); + + // Locking functions, to allow coarse-grained locking and amortization. + void Lock() ACQUIRE(allow_disallow_lock_); + void Unlock() RELEASE(allow_disallow_lock_); + void AssertLocked() ASSERT_CAPABILITY(allow_disallow_lock_); + + protected: + // Should HandleNullSweep be called when Sweep detects the release of an object? + virtual bool DoesHandleNullOnSweep() { + return false; + } + // If DoesHandleNullOnSweep returns true, this function will be called. + virtual void HandleNullSweep(T tag ATTRIBUTE_UNUSED) {} + + private: + bool SetLocked(art::Thread* self, art::mirror::Object* obj, T tag) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(allow_disallow_lock_); + + bool RemoveLocked(art::Thread* self, art::mirror::Object* obj, /* out */ T* tag) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(allow_disallow_lock_); + + bool GetTagLocked(art::Thread* self, art::mirror::Object* obj, /* out */ T* result) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(allow_disallow_lock_) { + auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj)); + if (it != tagged_objects_.end()) { + *result = it->second; + return true; + } + + // Performance optimization: To avoid multiple table updates, ensure that during GC we + // only update once. See the comment on the implementation of GetTagSlowPath. + if (art::kUseReadBarrier && + self != nullptr && + self->GetIsGcMarking() && + !update_since_last_sweep_) { + return GetTagSlowPath(self, obj, result); + } + + return false; + } + + // Slow-path for GetTag. We didn't find the object, but we might be storing from-pointers and + // are asked to retrieve with a to-pointer. + bool GetTagSlowPath(art::Thread* self, art::mirror::Object* obj, /* out */ T* result) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(allow_disallow_lock_); + + // Update the table by doing read barriers on each element, ensuring that to-space pointers + // are stored. + void UpdateTableWithReadBarrier() + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(allow_disallow_lock_); + + template <bool kHandleNull> + void SweepImpl(art::IsMarkedVisitor* visitor) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_); + + enum TableUpdateNullTarget { + kIgnoreNull, + kRemoveNull, + kCallHandleNull + }; + + template <typename Updater, TableUpdateNullTarget kTargetNull> + void UpdateTableWith(Updater& updater) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(allow_disallow_lock_); + + template <typename Storage, class Allocator = std::allocator<T>> + struct ReleasableContainer; + + struct HashGcRoot { + size_t operator()(const art::GcRoot<art::mirror::Object>& r) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return reinterpret_cast<uintptr_t>(r.Read<art::kWithoutReadBarrier>()); + } + }; + + struct EqGcRoot { + bool operator()(const art::GcRoot<art::mirror::Object>& r1, + const art::GcRoot<art::mirror::Object>& r2) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return r1.Read<art::kWithoutReadBarrier>() == r2.Read<art::kWithoutReadBarrier>(); + } + }; + + std::unordered_map<art::GcRoot<art::mirror::Object>, + T, + HashGcRoot, + EqGcRoot> tagged_objects_ + GUARDED_BY(allow_disallow_lock_) + GUARDED_BY(art::Locks::mutator_lock_); + // To avoid repeatedly scanning the whole table, remember if we did that since the last sweep. + bool update_since_last_sweep_; +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_ diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc index b27c2a3834..dcdd3ede13 100644 --- a/runtime/openjdkjvmti/object_tagging.cc +++ b/runtime/openjdkjvmti/object_tagging.cc @@ -34,354 +34,34 @@ #include <limits> #include "art_jvmti.h" -#include "base/logging.h" #include "events-inl.h" -#include "gc/allocation_listener.h" -#include "instrumentation.h" -#include "jni_env_ext-inl.h" -#include "jvmti_allocator.h" -#include "mirror/class.h" -#include "mirror/object.h" -#include "runtime.h" -#include "ScopedLocalRef.h" +#include "jvmti_weak_table-inl.h" namespace openjdkjvmti { -void ObjectTagTable::Lock() { - allow_disallow_lock_.ExclusiveLock(art::Thread::Current()); -} -void ObjectTagTable::Unlock() { - allow_disallow_lock_.ExclusiveUnlock(art::Thread::Current()); -} -void ObjectTagTable::AssertLocked() { - allow_disallow_lock_.AssertHeld(art::Thread::Current()); -} - -void ObjectTagTable::UpdateTableWithReadBarrier() { - update_since_last_sweep_ = true; - - auto WithReadBarrierUpdater = [&](const art::GcRoot<art::mirror::Object>& original_root, - art::mirror::Object* original_obj ATTRIBUTE_UNUSED) - REQUIRES_SHARED(art::Locks::mutator_lock_) { - return original_root.Read<art::kWithReadBarrier>(); - }; - - UpdateTableWith<decltype(WithReadBarrierUpdater), kIgnoreNull>(WithReadBarrierUpdater); -} - -bool ObjectTagTable::GetTagSlowPath(art::Thread* self, art::mirror::Object* obj, jlong* result) { - // Under concurrent GC, there is a window between moving objects and sweeping of system - // weaks in which mutators are active. We may receive a to-space object pointer in obj, - // but still have from-space pointers in the table. Explicitly update the table once. - // Note: this will keep *all* objects in the table live, but should be a rare occurrence. - UpdateTableWithReadBarrier(); - return GetTagLocked(self, obj, result); -} - -void ObjectTagTable::Add(art::mirror::Object* obj, jlong tag) { - // Same as Set(), as we don't have duplicates in an unordered_map. - Set(obj, tag); -} - -bool ObjectTagTable::Remove(art::mirror::Object* obj, jlong* tag) { - art::Thread* self = art::Thread::Current(); - art::MutexLock mu(self, allow_disallow_lock_); - Wait(self); - - return RemoveLocked(self, obj, tag); -} -bool ObjectTagTable::RemoveLocked(art::mirror::Object* obj, jlong* tag) { - art::Thread* self = art::Thread::Current(); - allow_disallow_lock_.AssertHeld(self); - Wait(self); - - return RemoveLocked(self, obj, tag); -} - -bool ObjectTagTable::RemoveLocked(art::Thread* self, art::mirror::Object* obj, jlong* tag) { - auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj)); - if (it != tagged_objects_.end()) { - if (tag != nullptr) { - *tag = it->second; - } - tagged_objects_.erase(it); - return true; - } - - if (art::kUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) { - // Under concurrent GC, there is a window between moving objects and sweeping of system - // weaks in which mutators are active. We may receive a to-space object pointer in obj, - // but still have from-space pointers in the table. Explicitly update the table once. - // Note: this will keep *all* objects in the table live, but should be a rare occurrence. - - // Update the table. - UpdateTableWithReadBarrier(); - - // And try again. - return RemoveLocked(self, obj, tag); - } - - // Not in here. - return false; -} +// Instantiate for jlong = JVMTI tags. +template class JvmtiWeakTable<jlong>; bool ObjectTagTable::Set(art::mirror::Object* obj, jlong new_tag) { if (new_tag == 0) { jlong tmp; return Remove(obj, &tmp); } - - art::Thread* self = art::Thread::Current(); - art::MutexLock mu(self, allow_disallow_lock_); - Wait(self); - - return SetLocked(self, obj, new_tag); + return JvmtiWeakTable<jlong>::Set(obj, new_tag); } bool ObjectTagTable::SetLocked(art::mirror::Object* obj, jlong new_tag) { if (new_tag == 0) { jlong tmp; return RemoveLocked(obj, &tmp); } - - art::Thread* self = art::Thread::Current(); - allow_disallow_lock_.AssertHeld(self); - Wait(self); - - return SetLocked(self, obj, new_tag); -} - -bool ObjectTagTable::SetLocked(art::Thread* self, art::mirror::Object* obj, jlong new_tag) { - auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj)); - if (it != tagged_objects_.end()) { - it->second = new_tag; - return true; - } - - if (art::kUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) { - // Under concurrent GC, there is a window between moving objects and sweeping of system - // weaks in which mutators are active. We may receive a to-space object pointer in obj, - // but still have from-space pointers in the table. Explicitly update the table once. - // Note: this will keep *all* objects in the table live, but should be a rare occurrence. - - // Update the table. - UpdateTableWithReadBarrier(); - - // And try again. - return SetLocked(self, obj, new_tag); - } - - // New element. - auto insert_it = tagged_objects_.emplace(art::GcRoot<art::mirror::Object>(obj), new_tag); - DCHECK(insert_it.second); - return false; + return JvmtiWeakTable<jlong>::SetLocked(obj, new_tag); } -void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) { - if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree)) { - SweepImpl<true>(visitor); - } else { - SweepImpl<false>(visitor); - } - - // Under concurrent GC, there is a window between moving objects and sweeping of system - // weaks in which mutators are active. We may receive a to-space object pointer in obj, - // but still have from-space pointers in the table. We explicitly update the table then - // to ensure we compare against to-space pointers. But we want to do this only once. Once - // sweeping is done, we know all objects are to-space pointers until the next GC cycle, - // so we re-enable the explicit update for the next marking. - update_since_last_sweep_ = false; +bool ObjectTagTable::DoesHandleNullOnSweep() { + return event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree); } - -template <bool kHandleNull> -void ObjectTagTable::SweepImpl(art::IsMarkedVisitor* visitor) { - art::Thread* self = art::Thread::Current(); - art::MutexLock mu(self, allow_disallow_lock_); - - auto IsMarkedUpdater = [&](const art::GcRoot<art::mirror::Object>& original_root ATTRIBUTE_UNUSED, - art::mirror::Object* original_obj) { - return visitor->IsMarked(original_obj); - }; - - UpdateTableWith<decltype(IsMarkedUpdater), - kHandleNull ? kCallHandleNull : kRemoveNull>(IsMarkedUpdater); -} - void ObjectTagTable::HandleNullSweep(jlong tag) { - event_handler_->DispatchEvent<ArtJvmtiEvent::kObjectFree>(nullptr, tag); -} - -template <typename T, ObjectTagTable::TableUpdateNullTarget kTargetNull> -ALWAYS_INLINE inline void ObjectTagTable::UpdateTableWith(T& updater) { - // We optimistically hope that elements will still be well-distributed when re-inserting them. - // So play with the map mechanics, and postpone rehashing. This avoids the need of a side - // vector and two passes. - float original_max_load_factor = tagged_objects_.max_load_factor(); - tagged_objects_.max_load_factor(std::numeric_limits<float>::max()); - // For checking that a max load-factor actually does what we expect. - size_t original_bucket_count = tagged_objects_.bucket_count(); - - for (auto it = tagged_objects_.begin(); it != tagged_objects_.end();) { - DCHECK(!it->first.IsNull()); - art::mirror::Object* original_obj = it->first.Read<art::kWithoutReadBarrier>(); - art::mirror::Object* target_obj = updater(it->first, original_obj); - if (original_obj != target_obj) { - if (kTargetNull == kIgnoreNull && target_obj == nullptr) { - // Ignore null target, don't do anything. - } else { - jlong tag = it->second; - it = tagged_objects_.erase(it); - if (target_obj != nullptr) { - tagged_objects_.emplace(art::GcRoot<art::mirror::Object>(target_obj), tag); - DCHECK_EQ(original_bucket_count, tagged_objects_.bucket_count()); - } else if (kTargetNull == kCallHandleNull) { - HandleNullSweep(tag); - } - continue; // Iterator was implicitly updated by erase. - } - } - it++; - } - - tagged_objects_.max_load_factor(original_max_load_factor); - // TODO: consider rehash here. -} - -template <typename T, class Allocator = std::allocator<T>> -struct ReleasableContainer { - using allocator_type = Allocator; - - explicit ReleasableContainer(const allocator_type& alloc, size_t reserve = 10) - : allocator(alloc), - data(reserve > 0 ? allocator.allocate(reserve) : nullptr), - size(0), - capacity(reserve) { - } - - ~ReleasableContainer() { - if (data != nullptr) { - allocator.deallocate(data, capacity); - capacity = 0; - size = 0; - } - } - - T* Release() { - T* tmp = data; - - data = nullptr; - size = 0; - capacity = 0; - - return tmp; - } - - void Resize(size_t new_capacity) { - CHECK_GT(new_capacity, capacity); - - T* tmp = allocator.allocate(new_capacity); - DCHECK(tmp != nullptr); - if (data != nullptr) { - memcpy(tmp, data, sizeof(T) * size); - } - T* old = data; - data = tmp; - allocator.deallocate(old, capacity); - capacity = new_capacity; - } - - void Pushback(const T& elem) { - if (size == capacity) { - size_t new_capacity = 2 * capacity + 1; - Resize(new_capacity); - } - data[size++] = elem; - } - - Allocator allocator; - T* data; - size_t size; - size_t capacity; -}; - -jvmtiError ObjectTagTable::GetTaggedObjects(jvmtiEnv* jvmti_env, - jint tag_count, - const jlong* tags, - jint* count_ptr, - jobject** object_result_ptr, - jlong** tag_result_ptr) { - if (tag_count < 0) { - return ERR(ILLEGAL_ARGUMENT); - } - if (tag_count > 0) { - for (size_t i = 0; i != static_cast<size_t>(tag_count); ++i) { - if (tags[i] == 0) { - return ERR(ILLEGAL_ARGUMENT); - } - } - } - if (tags == nullptr) { - return ERR(NULL_POINTER); - } - if (count_ptr == nullptr) { - return ERR(NULL_POINTER); - } - - art::Thread* self = art::Thread::Current(); - art::MutexLock mu(self, allow_disallow_lock_); - Wait(self); - - art::JNIEnvExt* jni_env = self->GetJniEnv(); - - constexpr size_t kDefaultSize = 10; - size_t initial_object_size; - size_t initial_tag_size; - if (tag_count == 0) { - initial_object_size = (object_result_ptr != nullptr) ? tagged_objects_.size() : 0; - initial_tag_size = (tag_result_ptr != nullptr) ? tagged_objects_.size() : 0; - } else { - initial_object_size = initial_tag_size = kDefaultSize; - } - JvmtiAllocator<void> allocator(jvmti_env); - ReleasableContainer<jobject, JvmtiAllocator<jobject>> selected_objects(allocator, initial_object_size); - ReleasableContainer<jlong, JvmtiAllocator<jlong>> selected_tags(allocator, initial_tag_size); - - size_t count = 0; - for (auto& pair : tagged_objects_) { - bool select; - if (tag_count > 0) { - select = false; - for (size_t i = 0; i != static_cast<size_t>(tag_count); ++i) { - if (tags[i] == pair.second) { - select = true; - break; - } - } - } else { - select = true; - } - - if (select) { - art::mirror::Object* obj = pair.first.Read<art::kWithReadBarrier>(); - if (obj != nullptr) { - count++; - if (object_result_ptr != nullptr) { - selected_objects.Pushback(jni_env->AddLocalReference<jobject>(obj)); - } - if (tag_result_ptr != nullptr) { - selected_tags.Pushback(pair.second); - } - } - } - } - - if (object_result_ptr != nullptr) { - *object_result_ptr = selected_objects.Release(); - } - if (tag_result_ptr != nullptr) { - *tag_result_ptr = selected_tags.Release(); - } - *count_ptr = static_cast<jint>(count); - return ERR(NONE); + event_handler_->DispatchEvent<ArtJvmtiEvent::kObjectFree>(jvmti_env_, nullptr, tag); } } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h index 0296f1ad80..ca84e442dc 100644 --- a/runtime/openjdkjvmti/object_tagging.h +++ b/runtime/openjdkjvmti/object_tagging.h @@ -1,17 +1,32 @@ -/* - * Copyright (C) 2016 The Android Open Source Project +/* Copyright (C) 2016 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * 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 + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * 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. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. */ #ifndef ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_ @@ -20,62 +35,28 @@ #include <unordered_map> #include "base/mutex.h" -#include "gc/system_weak.h" -#include "gc_root-inl.h" #include "globals.h" #include "jvmti.h" +#include "jvmti_weak_table.h" #include "mirror/object.h" -#include "thread-inl.h" namespace openjdkjvmti { +struct ArtJvmTiEnv; class EventHandler; -class ObjectTagTable : public art::gc::SystemWeakHolder { +class ObjectTagTable FINAL : public JvmtiWeakTable<jlong> { public: - explicit ObjectTagTable(EventHandler* event_handler) - : art::gc::SystemWeakHolder(kTaggingLockLevel), - update_since_last_sweep_(false), - event_handler_(event_handler) { - } - - void Add(art::mirror::Object* obj, jlong tag) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(!allow_disallow_lock_); + ObjectTagTable(EventHandler* event_handler, ArtJvmTiEnv* env) + : event_handler_(event_handler), jvmti_env_(env) {} - bool Remove(art::mirror::Object* obj, jlong* tag) + bool Set(art::mirror::Object* obj, jlong tag) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_); - bool RemoveLocked(art::mirror::Object* obj, jlong* tag) + bool SetLocked(art::mirror::Object* obj, jlong tag) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(allow_disallow_lock_); - bool Set(art::mirror::Object* obj, jlong tag) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(!allow_disallow_lock_); - bool SetLocked(art::mirror::Object* obj, jlong tag) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(allow_disallow_lock_); - - bool GetTag(art::mirror::Object* obj, jlong* result) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(!allow_disallow_lock_) { - art::Thread* self = art::Thread::Current(); - art::MutexLock mu(self, allow_disallow_lock_); - Wait(self); - - return GetTagLocked(self, obj, result); - } - bool GetTagLocked(art::mirror::Object* obj, jlong* result) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(allow_disallow_lock_) { - art::Thread* self = art::Thread::Current(); - allow_disallow_lock_.AssertHeld(self); - Wait(self); - - return GetTagLocked(self, obj, result); - } - jlong GetTagOrZero(art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_) { @@ -91,109 +72,13 @@ class ObjectTagTable : public art::gc::SystemWeakHolder { return tmp; } - void Sweep(art::IsMarkedVisitor* visitor) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(!allow_disallow_lock_); - - jvmtiError GetTaggedObjects(jvmtiEnv* jvmti_env, - jint tag_count, - const jlong* tags, - jint* count_ptr, - jobject** object_result_ptr, - jlong** tag_result_ptr) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(!allow_disallow_lock_); - - void Lock() ACQUIRE(allow_disallow_lock_); - void Unlock() RELEASE(allow_disallow_lock_); - void AssertLocked() ASSERT_CAPABILITY(allow_disallow_lock_); + protected: + bool DoesHandleNullOnSweep() OVERRIDE; + void HandleNullSweep(jlong tag) OVERRIDE; private: - bool SetLocked(art::Thread* self, art::mirror::Object* obj, jlong tag) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(allow_disallow_lock_); - - bool RemoveLocked(art::Thread* self, art::mirror::Object* obj, jlong* tag) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(allow_disallow_lock_); - - bool GetTagLocked(art::Thread* self, art::mirror::Object* obj, jlong* result) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(allow_disallow_lock_) { - auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj)); - if (it != tagged_objects_.end()) { - *result = it->second; - return true; - } - - if (art::kUseReadBarrier && - self != nullptr && - self->GetIsGcMarking() && - !update_since_last_sweep_) { - return GetTagSlowPath(self, obj, result); - } - - return false; - } - - // Slow-path for GetTag. We didn't find the object, but we might be storing from-pointers and - // are asked to retrieve with a to-pointer. - bool GetTagSlowPath(art::Thread* self, art::mirror::Object* obj, jlong* result) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(allow_disallow_lock_); - - // Update the table by doing read barriers on each element, ensuring that to-space pointers - // are stored. - void UpdateTableWithReadBarrier() - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(allow_disallow_lock_); - - template <bool kHandleNull> - void SweepImpl(art::IsMarkedVisitor* visitor) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(!allow_disallow_lock_); - void HandleNullSweep(jlong tag); - - enum TableUpdateNullTarget { - kIgnoreNull, - kRemoveNull, - kCallHandleNull - }; - - template <typename T, TableUpdateNullTarget kTargetNull> - void UpdateTableWith(T& updater) - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(allow_disallow_lock_); - - struct HashGcRoot { - size_t operator()(const art::GcRoot<art::mirror::Object>& r) const - REQUIRES_SHARED(art::Locks::mutator_lock_) { - return reinterpret_cast<uintptr_t>(r.Read<art::kWithoutReadBarrier>()); - } - }; - - struct EqGcRoot { - bool operator()(const art::GcRoot<art::mirror::Object>& r1, - const art::GcRoot<art::mirror::Object>& r2) const - REQUIRES_SHARED(art::Locks::mutator_lock_) { - return r1.Read<art::kWithoutReadBarrier>() == r2.Read<art::kWithoutReadBarrier>(); - } - }; - - // The tag table is used when visiting roots. So it needs to have a low lock level. - static constexpr art::LockLevel kTaggingLockLevel = - static_cast<art::LockLevel>(art::LockLevel::kAbortLock + 1); - - std::unordered_map<art::GcRoot<art::mirror::Object>, - jlong, - HashGcRoot, - EqGcRoot> tagged_objects_ - GUARDED_BY(allow_disallow_lock_) - GUARDED_BY(art::Locks::mutator_lock_); - // To avoid repeatedly scanning the whole table, remember if we did that since the last sweep. - bool update_since_last_sweep_; - EventHandler* event_handler_; + ArtJvmTiEnv* jvmti_env_; }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 4282e38b17..e94c4e6112 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -43,6 +43,7 @@ #include "common_throws.h" #include "dex_file_annotations.h" #include "events-inl.h" +#include "fixed_up_dex_file.h" #include "gc/heap.h" #include "gc_root.h" #include "handle.h" @@ -55,6 +56,8 @@ #include "mirror/object_reference.h" #include "mirror/object-inl.h" #include "mirror/reference.h" +#include "primitive.h" +#include "reflection.h" #include "runtime.h" #include "runtime_callbacks.h" #include "ScopedLocalRef.h" @@ -62,6 +65,7 @@ #include "thread-inl.h" #include "thread_list.h" #include "ti_class_loader.h" +#include "ti_phase.h" #include "ti_redefine.h" #include "utils.h" @@ -77,9 +81,9 @@ static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self, REQUIRES_SHARED(art::Locks::mutator_lock_) { // Make the mmap std::string error_msg; + art::ArraySlice<const unsigned char> final_data(final_dex_data, final_len); std::unique_ptr<art::MemMap> map(Redefiner::MoveDataToMemMap(orig_location, - final_len, - final_dex_data, + final_data, &error_msg)); if (map.get() == nullptr) { LOG(WARNING) << "Unable to allocate mmap for redefined dex file! Error was: " << error_msg; @@ -142,12 +146,26 @@ struct ClassCallback : public art::ClassLoadCallback { // It is a primitive or array. Just return return; } + jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked(); + if (UNLIKELY(phase != JVMTI_PHASE_START && phase != JVMTI_PHASE_LIVE)) { + // We want to wait until we are at least in the START phase so that all WellKnownClasses and + // mirror classes have been initialized and loaded. The runtime relies on these classes having + // specific fields and methods present. Since PreDefine hooks don't need to abide by this + // restriction we will simply not send the event for these classes. + LOG(WARNING) << "Ignoring load of class <" << descriptor << "> as it is being loaded during " + << "runtime initialization."; + return; + } + + // Strip the 'L' and ';' from the descriptor std::string name(std::string(descriptor).substr(1, strlen(descriptor) - 2)); art::Thread* self = art::Thread::Current(); art::JNIEnvExt* env = self->GetJniEnv(); ScopedLocalRef<jobject> loader( env, class_loader.IsNull() ? nullptr : env->AddLocalReference<jobject>(class_loader.Get())); + std::unique_ptr<FixedUpDexFile> dex_file_copy(FixedUpDexFile::Create(initial_dex_file)); + // Go back to native. art::ScopedThreadSuspension sts(self, art::ThreadState::kNative); // Call all Non-retransformable agents. @@ -161,14 +179,14 @@ struct ClassCallback : public art::ClassLoadCallback { loader.get(), name.c_str(), static_cast<jobject>(nullptr), // Android doesn't seem to have protection domains - static_cast<jint>(initial_dex_file.Size()), - static_cast<const unsigned char*>(initial_dex_file.Begin()), + static_cast<jint>(dex_file_copy->Size()), + static_cast<const unsigned char*>(dex_file_copy->Begin()), static_cast<jint*>(&post_no_redefine_len), static_cast<unsigned char**>(&post_no_redefine_dex_data)); if (post_no_redefine_dex_data == nullptr) { DCHECK_EQ(post_no_redefine_len, 0); - post_no_redefine_dex_data = const_cast<unsigned char*>(initial_dex_file.Begin()); - post_no_redefine_len = initial_dex_file.Size(); + post_no_redefine_dex_data = const_cast<unsigned char*>(dex_file_copy->Begin()); + post_no_redefine_len = dex_file_copy->Size(); } else { post_no_redefine_unique_ptr = std::unique_ptr<const unsigned char>(post_no_redefine_dex_data); DCHECK_GT(post_no_redefine_len, 0); @@ -197,7 +215,7 @@ struct ClassCallback : public art::ClassLoadCallback { DCHECK_GT(final_len, 0); } - if (final_dex_data != initial_dex_file.Begin()) { + if (final_dex_data != dex_file_copy->Begin()) { LOG(WARNING) << "Changing class " << descriptor; art::ScopedObjectAccess soa(self); art::StackHandleScope<2> hs(self); @@ -215,14 +233,22 @@ struct ClassCallback : public art::ClassLoadCallback { } // Allocate the byte array to store the dex file bytes in. - art::Handle<art::mirror::ByteArray> arr(hs.NewHandle( - art::mirror::ByteArray::AllocateAndFill( - self, - reinterpret_cast<const signed char*>(post_no_redefine_dex_data), - post_no_redefine_len))); + art::MutableHandle<art::mirror::Object> arr(hs.NewHandle<art::mirror::Object>(nullptr)); + if (post_no_redefine_dex_data == dex_file_copy->Begin() && name != "java/lang/Long") { + // we didn't have any non-retransformable agents. We can just cache a pointer to the + // initial_dex_file. It will be kept live by the class_loader. + jlong dex_ptr = reinterpret_cast<uintptr_t>(&initial_dex_file); + art::JValue val; + val.SetJ(dex_ptr); + arr.Assign(art::BoxPrimitive(art::Primitive::kPrimLong, val)); + } else { + arr.Assign(art::mirror::ByteArray::AllocateAndFill( + self, + reinterpret_cast<const signed char*>(post_no_redefine_dex_data), + post_no_redefine_len)); + } if (arr.IsNull()) { - LOG(WARNING) << "Unable to allocate byte array for initial dex-file bytes. Aborting " - << "transformation"; + LOG(WARNING) << "Unable to allocate memory for initial dex-file. Aborting transformation"; self->AssertPendingOOMException(); return; } @@ -246,7 +272,7 @@ struct ClassCallback : public art::ClassLoadCallback { } // Actually set the ClassExt's original bytes once we have actually succeeded. - ext->SetOriginalDexFileBytes(arr.Get()); + ext->SetOriginalDexFile(arr.Get()); // Set the return values *final_class_def = &dex_file->GetClassDef(0); *final_dex_file = dex_file.release(); diff --git a/runtime/openjdkjvmti/ti_class_definition.cc b/runtime/openjdkjvmti/ti_class_definition.cc index 2c2a79bc58..153692b2fe 100644 --- a/runtime/openjdkjvmti/ti_class_definition.cc +++ b/runtime/openjdkjvmti/ti_class_definition.cc @@ -31,25 +31,145 @@ #include "ti_class_definition.h" +#include "base/array_slice.h" #include "dex_file.h" +#include "fixed_up_dex_file.h" #include "handle_scope-inl.h" #include "handle.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" +#include "reflection.h" #include "thread.h" namespace openjdkjvmti { -bool ArtClassDefinition::IsModified(art::Thread* self) const { - if (modified) { +bool ArtClassDefinition::IsModified() const { + // RedefineClasses calls always are 'modified' since they need to change the original_dex_file of + // the class. + if (redefined_) { return true; } // Check if the dex file we want to set is the same as the current one. + // Unfortunately we need to do this check even if no modifications have been done since it could + // be that agents were removed in the mean-time so we still have a different dex file. The dex + // checksum means this is likely to be fairly fast. + return static_cast<jint>(original_dex_file_.size()) != dex_len_ || + memcmp(&original_dex_file_.At(0), dex_data_.get(), dex_len_) != 0; +} + +jvmtiError ArtClassDefinition::InitCommon(ArtJvmTiEnv* env, jclass klass) { + JNIEnv* jni_env = GetJniEnv(env); + if (jni_env == nullptr) { + return ERR(INTERNAL); + } + art::ScopedObjectAccess soa(jni_env); + art::ObjPtr<art::mirror::Class> m_klass(soa.Decode<art::mirror::Class>(klass)); + if (m_klass.IsNull()) { + return ERR(INVALID_CLASS); + } + klass_ = klass; + loader_ = soa.AddLocalReference<jobject>(m_klass->GetClassLoader()); + std::string descriptor_store; + std::string descriptor(m_klass->GetDescriptor(&descriptor_store)); + name_ = descriptor.substr(1, descriptor.size() - 2); + // Android doesn't really have protection domains. + protection_domain_ = nullptr; + return OK; +} + +// Gets the data surrounding the given class. +static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env, + art::Handle<art::mirror::Class> klass, + /*out*/jint* dex_data_len, + /*out*/unsigned char** dex_data) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::StackHandleScope<3> hs(art::Thread::Current()); + art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData())); + const art::DexFile* dex_file = nullptr; + if (!ext.IsNull()) { + art::Handle<art::mirror::Object> orig_dex(hs.NewHandle(ext->GetOriginalDexFile())); + if (!orig_dex.IsNull()) { + if (orig_dex->IsArrayInstance()) { + DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte()); + art::Handle<art::mirror::ByteArray> orig_dex_bytes( + hs.NewHandle(art::down_cast<art::mirror::ByteArray*>(orig_dex->AsArray()))); + *dex_data_len = static_cast<jint>(orig_dex_bytes->GetLength()); + return CopyDataIntoJvmtiBuffer( + env, + reinterpret_cast<const unsigned char*>(orig_dex_bytes->GetData()), + *dex_data_len, + /*out*/dex_data); + } else if (orig_dex->IsDexCache()) { + dex_file = orig_dex->AsDexCache()->GetDexFile(); + } else { + DCHECK_EQ(orig_dex->GetClass()->GetPrimitiveType(), art::Primitive::kPrimLong); + art::ObjPtr<art::mirror::Class> prim_long_class( + art::Runtime::Current()->GetClassLinker()->GetClassRoot( + art::ClassLinker::kPrimitiveLong)); + art::JValue val; + if (!art::UnboxPrimitiveForResult(orig_dex.Get(), prim_long_class, &val)) { + // This should never happen. + return ERR(INTERNAL); + } + dex_file = reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ())); + } + } + } + if (dex_file == nullptr) { + dex_file = &klass->GetDexFile(); + } + std::unique_ptr<FixedUpDexFile> fixed_dex_file(FixedUpDexFile::Create(*dex_file)); + *dex_data_len = static_cast<jint>(fixed_dex_file->Size()); + return CopyDataIntoJvmtiBuffer(env, + fixed_dex_file->Begin(), + fixed_dex_file->Size(), + /*out*/dex_data); +} + +jvmtiError ArtClassDefinition::Init(ArtJvmTiEnv* env, jclass klass) { + jvmtiError res = InitCommon(env, klass); + if (res != OK) { + return res; + } + unsigned char* new_data = nullptr; + art::Thread* self = art::Thread::Current(); + art::ScopedObjectAccess soa(self); art::StackHandleScope<1> hs(self); - art::Handle<art::mirror::Class> h_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass())); - const art::DexFile& cur_dex_file = h_klass->GetDexFile(); - return static_cast<jint>(cur_dex_file.Size()) != dex_len || - memcmp(cur_dex_file.Begin(), dex_data.get(), dex_len) != 0; + art::Handle<art::mirror::Class> m_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass())); + res = GetDexDataForRetransformation(env, m_klass, &dex_len_, &new_data); + if (res != OK) { + return res; + } + dex_data_ = MakeJvmtiUniquePtr(env, new_data); + if (m_klass->GetExtData() == nullptr || m_klass->GetExtData()->GetOriginalDexFile() == nullptr) { + // We have never redefined class this yet. Keep track of what the (de-quickened) dex file looks + // like so we can tell if anything has changed. Really we would like to just always do the + // 'else' block but the fact that we de-quickened stuff screws us over. + unsigned char* original_data_memory = nullptr; + res = CopyDataIntoJvmtiBuffer(env, dex_data_.get(), dex_len_, &original_data_memory); + original_dex_file_memory_ = MakeJvmtiUniquePtr(env, original_data_memory); + original_dex_file_ = art::ArraySlice<const unsigned char>(original_data_memory, dex_len_); + } else { + // We know that we have been redefined at least once (there is an original_dex_file set in + // the class) so we can just use the current dex file directly. + const art::DexFile& dex_file = m_klass->GetDexFile(); + original_dex_file_ = art::ArraySlice<const unsigned char>(dex_file.Begin(), dex_file.Size()); + } + return res; +} + +jvmtiError ArtClassDefinition::Init(ArtJvmTiEnv* env, const jvmtiClassDefinition& def) { + jvmtiError res = InitCommon(env, def.klass); + if (res != OK) { + return res; + } + unsigned char* new_data = nullptr; + original_dex_file_ = art::ArraySlice<const unsigned char>(def.class_bytes, def.class_byte_count); + redefined_ = true; + dex_len_ = def.class_byte_count; + res = CopyDataIntoJvmtiBuffer(env, def.class_bytes, def.class_byte_count, /*out*/ &new_data); + dex_data_ = MakeJvmtiUniquePtr(env, new_data); + return res; } } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class_definition.h b/runtime/openjdkjvmti/ti_class_definition.h index 3c251d4b44..43d0c3fc62 100644 --- a/runtime/openjdkjvmti/ti_class_definition.h +++ b/runtime/openjdkjvmti/ti_class_definition.h @@ -39,37 +39,89 @@ namespace openjdkjvmti { // A struct that stores data needed for redefining/transforming classes. This structure should only // even be accessed from a single thread and must not survive past the completion of the // redefinition/retransformation function that created it. -struct ArtClassDefinition { +class ArtClassDefinition { public: - jclass klass; - jobject loader; - std::string name; - jobject protection_domain; - jint dex_len; - JvmtiUniquePtr<unsigned char> dex_data; - art::ArraySlice<const unsigned char> original_dex_file; - - ArtClassDefinition() = default; + ArtClassDefinition() + : klass_(nullptr), + loader_(nullptr), + name_(), + protection_domain_(nullptr), + dex_len_(0), + dex_data_(nullptr), + original_dex_file_memory_(nullptr), + original_dex_file_(), + redefined_(false) {} + + jvmtiError Init(ArtJvmTiEnv* env, jclass klass); + jvmtiError Init(ArtJvmTiEnv* env, const jvmtiClassDefinition& def); + ArtClassDefinition(ArtClassDefinition&& o) = default; + ArtClassDefinition& operator=(ArtClassDefinition&& o) = default; void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) { + DCHECK(IsInitialized()); if (new_dex_data == nullptr) { return; - } else if (new_dex_data != dex_data.get() || new_dex_len != dex_len) { - SetModified(); - dex_len = new_dex_len; - dex_data = MakeJvmtiUniquePtr(env, new_dex_data); + } else if (new_dex_data != dex_data_.get() || new_dex_len != dex_len_) { + dex_len_ = new_dex_len; + dex_data_ = MakeJvmtiUniquePtr(env, new_dex_data); } } - void SetModified() { - modified = true; + art::ArraySlice<const unsigned char> GetNewOriginalDexFile() const { + DCHECK(IsInitialized()); + if (redefined_) { + return original_dex_file_; + } else { + return art::ArraySlice<const unsigned char>(); + } } - bool IsModified(art::Thread* self) const REQUIRES_SHARED(art::Locks::mutator_lock_); + bool IsModified() const; + + bool IsInitialized() const { + return klass_ != nullptr; + } + + jclass GetClass() const { + DCHECK(IsInitialized()); + return klass_; + } + + jobject GetLoader() const { + DCHECK(IsInitialized()); + return loader_; + } + + const std::string& GetName() const { + DCHECK(IsInitialized()); + return name_; + } + + jobject GetProtectionDomain() const { + DCHECK(IsInitialized()); + return protection_domain_; + } + + art::ArraySlice<const unsigned char> GetDexData() const { + DCHECK(IsInitialized()); + return art::ArraySlice<const unsigned char>(dex_data_.get(), dex_len_); + } private: - bool modified; + jvmtiError InitCommon(ArtJvmTiEnv* env, jclass klass); + + jclass klass_; + jobject loader_; + std::string name_; + jobject protection_domain_; + jint dex_len_; + JvmtiUniquePtr<unsigned char> dex_data_; + JvmtiUniquePtr<unsigned char> original_dex_file_memory_; + art::ArraySlice<const unsigned char> original_dex_file_; + bool redefined_; + + DISALLOW_COPY_AND_ASSIGN(ArtClassDefinition); }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc index 976ce66f11..49d9aca46e 100644 --- a/runtime/openjdkjvmti/ti_heap.cc +++ b/runtime/openjdkjvmti/ti_heap.cc @@ -25,6 +25,7 @@ #include "gc_root-inl.h" #include "jni_env_ext.h" #include "jni_internal.h" +#include "jvmti_weak_table-inl.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" @@ -41,6 +42,21 @@ namespace openjdkjvmti { namespace { +struct IndexCache { + // The number of interface fields implemented by the class. This is a prefix to all assigned + // field indices. + size_t interface_fields; + + // It would be nice to also cache the following, but it is complicated to wire up into the + // generic visit: + // The number of fields in interfaces and superclasses. This is the first index assigned to + // fields of the class. + // size_t superclass_fields; +}; +using IndexCachingTable = JvmtiWeakTable<IndexCache>; + +static IndexCachingTable gIndexCachingTable; + // Report the contents of a string, if a callback is set. jint ReportString(art::ObjPtr<art::mirror::Object> obj, jvmtiEnv* env, @@ -50,25 +66,28 @@ jint ReportString(art::ObjPtr<art::mirror::Object> obj, if (UNLIKELY(cb->string_primitive_value_callback != nullptr) && obj->IsString()) { art::ObjPtr<art::mirror::String> str = obj->AsString(); int32_t string_length = str->GetLength(); - jvmtiError alloc_error; - JvmtiUniquePtr<uint16_t[]> data = AllocJvmtiUniquePtr<uint16_t[]>(env, - string_length, - &alloc_error); - if (data == nullptr) { - // TODO: Not really sure what to do here. Should we abort the iteration and go all the way - // back? For now just warn. - LOG(WARNING) << "Unable to allocate buffer for string reporting! Silently dropping value."; - return 0; - } + JvmtiUniquePtr<uint16_t[]> data; - if (str->IsCompressed()) { - uint8_t* compressed_data = str->GetValueCompressed(); - for (int32_t i = 0; i != string_length; ++i) { - data[i] = compressed_data[i]; + if (string_length > 0) { + jvmtiError alloc_error; + data = AllocJvmtiUniquePtr<uint16_t[]>(env, string_length, &alloc_error); + if (data == nullptr) { + // TODO: Not really sure what to do here. Should we abort the iteration and go all the way + // back? For now just warn. + LOG(WARNING) << "Unable to allocate buffer for string reporting! Silently dropping value." + << " >" << str->ToModifiedUtf8() << "<"; + return 0; + } + + if (str->IsCompressed()) { + uint8_t* compressed_data = str->GetValueCompressed(); + for (int32_t i = 0; i != string_length; ++i) { + data[i] = compressed_data[i]; + } + } else { + // Can copy directly. + memcpy(data.get(), str->GetValue(), string_length * sizeof(uint16_t)); } - } else { - // Can copy directly. - memcpy(data.get(), str->GetValue(), string_length * sizeof(uint16_t)); } const jlong class_tag = tag_table->GetTagOrZero(obj->GetClass()); @@ -159,6 +178,433 @@ jint ReportPrimitiveArray(art::ObjPtr<art::mirror::Object> obj, return 0; } +template <typename UserData> +bool VisitorFalse(art::ObjPtr<art::mirror::Object> obj ATTRIBUTE_UNUSED, + art::ObjPtr<art::mirror::Class> klass ATTRIBUTE_UNUSED, + art::ArtField& field ATTRIBUTE_UNUSED, + size_t field_index ATTRIBUTE_UNUSED, + UserData* user_data ATTRIBUTE_UNUSED) { + return false; +} + +template <typename UserData, bool kCallVisitorOnRecursion> +class FieldVisitor { + public: + // Report the contents of a primitive fields of the given object, if a callback is set. + template <typename StaticPrimitiveVisitor, + typename StaticReferenceVisitor, + typename InstancePrimitiveVisitor, + typename InstanceReferenceVisitor> + static bool ReportFields(art::ObjPtr<art::mirror::Object> obj, + UserData* user_data, + StaticPrimitiveVisitor& static_prim_visitor, + StaticReferenceVisitor& static_ref_visitor, + InstancePrimitiveVisitor& instance_prim_visitor, + InstanceReferenceVisitor& instance_ref_visitor) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + FieldVisitor fv(user_data); + + if (obj->IsClass()) { + // When visiting a class, we only visit the static fields of the given class. No field of + // superclasses is visited. + art::ObjPtr<art::mirror::Class> klass = obj->AsClass(); + // Only report fields on resolved classes. We need valid field data. + if (!klass->IsResolved()) { + return false; + } + return fv.ReportFieldsImpl(nullptr, + obj->AsClass(), + obj->AsClass()->IsInterface(), + static_prim_visitor, + static_ref_visitor, + instance_prim_visitor, + instance_ref_visitor); + } else { + // See comment above. Just double-checking here, but an instance *should* mean the class was + // resolved. + DCHECK(obj->GetClass()->IsResolved() || obj->GetClass()->IsErroneousResolved()); + return fv.ReportFieldsImpl(obj, + obj->GetClass(), + false, + static_prim_visitor, + static_ref_visitor, + instance_prim_visitor, + instance_ref_visitor); + } + } + + private: + explicit FieldVisitor(UserData* user_data) : user_data_(user_data) {} + + // Report the contents of fields of the given object. If obj is null, report the static fields, + // otherwise the instance fields. + template <typename StaticPrimitiveVisitor, + typename StaticReferenceVisitor, + typename InstancePrimitiveVisitor, + typename InstanceReferenceVisitor> + bool ReportFieldsImpl(art::ObjPtr<art::mirror::Object> obj, + art::ObjPtr<art::mirror::Class> klass, + bool skip_java_lang_object, + StaticPrimitiveVisitor& static_prim_visitor, + StaticReferenceVisitor& static_ref_visitor, + InstancePrimitiveVisitor& instance_prim_visitor, + InstanceReferenceVisitor& instance_ref_visitor) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + // Compute the offset of field indices. + size_t interface_field_count = CountInterfaceFields(klass); + + size_t tmp; + bool aborted = ReportFieldsRecursive(obj, + klass, + interface_field_count, + skip_java_lang_object, + static_prim_visitor, + static_ref_visitor, + instance_prim_visitor, + instance_ref_visitor, + &tmp); + return aborted; + } + + // Visit primitive fields in an object (instance). Return true if the visit was aborted. + template <typename StaticPrimitiveVisitor, + typename StaticReferenceVisitor, + typename InstancePrimitiveVisitor, + typename InstanceReferenceVisitor> + bool ReportFieldsRecursive(art::ObjPtr<art::mirror::Object> obj, + art::ObjPtr<art::mirror::Class> klass, + size_t interface_fields, + bool skip_java_lang_object, + StaticPrimitiveVisitor& static_prim_visitor, + StaticReferenceVisitor& static_ref_visitor, + InstancePrimitiveVisitor& instance_prim_visitor, + InstanceReferenceVisitor& instance_ref_visitor, + size_t* field_index_out) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK(klass != nullptr); + size_t field_index; + if (klass->GetSuperClass() == nullptr) { + // j.l.Object. Start with the fields from interfaces. + field_index = interface_fields; + if (skip_java_lang_object) { + *field_index_out = field_index; + return false; + } + } else { + // Report superclass fields. + if (kCallVisitorOnRecursion) { + if (ReportFieldsRecursive(obj, + klass->GetSuperClass(), + interface_fields, + skip_java_lang_object, + static_prim_visitor, + static_ref_visitor, + instance_prim_visitor, + instance_ref_visitor, + &field_index)) { + return true; + } + } else { + // Still call, but with empty visitor. This is required for correct counting. + ReportFieldsRecursive(obj, + klass->GetSuperClass(), + interface_fields, + skip_java_lang_object, + VisitorFalse<UserData>, + VisitorFalse<UserData>, + VisitorFalse<UserData>, + VisitorFalse<UserData>, + &field_index); + } + } + + // Now visit fields for the current klass. + + for (auto& static_field : klass->GetSFields()) { + if (static_field.IsPrimitiveType()) { + if (static_prim_visitor(obj, + klass, + static_field, + field_index, + user_data_)) { + return true; + } + } else { + if (static_ref_visitor(obj, + klass, + static_field, + field_index, + user_data_)) { + return true; + } + } + field_index++; + } + + for (auto& instance_field : klass->GetIFields()) { + if (instance_field.IsPrimitiveType()) { + if (instance_prim_visitor(obj, + klass, + instance_field, + field_index, + user_data_)) { + return true; + } + } else { + if (instance_ref_visitor(obj, + klass, + instance_field, + field_index, + user_data_)) { + return true; + } + } + field_index++; + } + + *field_index_out = field_index; + return false; + } + + // Implements a visit of the implemented interfaces of a given class. + template <typename T> + struct RecursiveInterfaceVisit { + static void VisitStatic(art::Thread* self, art::ObjPtr<art::mirror::Class> klass, T& visitor) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + RecursiveInterfaceVisit rv; + rv.Visit(self, klass, visitor); + } + + void Visit(art::Thread* self, art::ObjPtr<art::mirror::Class> klass, T& visitor) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + // First visit the parent, to get the order right. + // (We do this in preparation for actual visiting of interface fields.) + if (klass->GetSuperClass() != nullptr) { + Visit(self, klass->GetSuperClass(), visitor); + } + for (uint32_t i = 0; i != klass->NumDirectInterfaces(); ++i) { + art::ObjPtr<art::mirror::Class> inf_klass = + art::mirror::Class::GetDirectInterface(self, klass, i); + DCHECK(inf_klass != nullptr); + VisitInterface(self, inf_klass, visitor); + } + } + + void VisitInterface(art::Thread* self, art::ObjPtr<art::mirror::Class> inf_klass, T& visitor) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + auto it = visited_interfaces.find(inf_klass.Ptr()); + if (it != visited_interfaces.end()) { + return; + } + visited_interfaces.insert(inf_klass.Ptr()); + + // Let the visitor know about this one. Note that this order is acceptable, as the ordering + // of these fields never matters for known visitors. + visitor(inf_klass); + + // Now visit the superinterfaces. + for (uint32_t i = 0; i != inf_klass->NumDirectInterfaces(); ++i) { + art::ObjPtr<art::mirror::Class> super_inf_klass = + art::mirror::Class::GetDirectInterface(self, inf_klass, i); + DCHECK(super_inf_klass != nullptr); + VisitInterface(self, super_inf_klass, visitor); + } + } + + std::unordered_set<art::mirror::Class*> visited_interfaces; + }; + + // Counting interface fields. Note that we cannot use the interface table, as that only contains + // "non-marker" interfaces (= interfaces with methods). + static size_t CountInterfaceFields(art::ObjPtr<art::mirror::Class> klass) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + // Do we have a cached value? + IndexCache tmp; + if (gIndexCachingTable.GetTag(klass.Ptr(), &tmp)) { + return tmp.interface_fields; + } + + size_t count = 0; + auto visitor = [&count](art::ObjPtr<art::mirror::Class> inf_klass) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK(inf_klass->IsInterface()); + DCHECK_EQ(0u, inf_klass->NumInstanceFields()); + count += inf_klass->NumStaticFields(); + }; + RecursiveInterfaceVisit<decltype(visitor)>::VisitStatic(art::Thread::Current(), klass, visitor); + + // Store this into the cache. + tmp.interface_fields = count; + gIndexCachingTable.Set(klass.Ptr(), tmp); + + return count; + } + + UserData* user_data_; +}; + +// Debug helper. Prints the structure of an object. +template <bool kStatic, bool kRef> +struct DumpVisitor { + static bool Callback(art::ObjPtr<art::mirror::Object> obj ATTRIBUTE_UNUSED, + art::ObjPtr<art::mirror::Class> klass ATTRIBUTE_UNUSED, + art::ArtField& field, + size_t field_index, + void* user_data ATTRIBUTE_UNUSED) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + LOG(ERROR) << (kStatic ? "static " : "instance ") + << (kRef ? "ref " : "primitive ") + << field.PrettyField() + << " @ " + << field_index; + return false; + } +}; +ATTRIBUTE_UNUSED +void DumpObjectFields(art::ObjPtr<art::mirror::Object> obj) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (obj->IsClass()) { + FieldVisitor<void, false>:: ReportFields(obj, + nullptr, + DumpVisitor<true, false>::Callback, + DumpVisitor<true, true>::Callback, + DumpVisitor<false, false>::Callback, + DumpVisitor<false, true>::Callback); + } else { + FieldVisitor<void, true>::ReportFields(obj, + nullptr, + DumpVisitor<true, false>::Callback, + DumpVisitor<true, true>::Callback, + DumpVisitor<false, false>::Callback, + DumpVisitor<false, true>::Callback); + } +} + +class ReportPrimitiveField { + public: + static bool Report(art::ObjPtr<art::mirror::Object> obj, + ObjectTagTable* tag_table, + const jvmtiHeapCallbacks* cb, + const void* user_data) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (UNLIKELY(cb->primitive_field_callback != nullptr)) { + jlong class_tag = tag_table->GetTagOrZero(obj->GetClass()); + ReportPrimitiveField rpf(tag_table, class_tag, cb, user_data); + if (obj->IsClass()) { + return FieldVisitor<ReportPrimitiveField, false>::ReportFields( + obj, + &rpf, + ReportPrimitiveFieldCallback<true>, + VisitorFalse<ReportPrimitiveField>, + VisitorFalse<ReportPrimitiveField>, + VisitorFalse<ReportPrimitiveField>); + } else { + return FieldVisitor<ReportPrimitiveField, true>::ReportFields( + obj, + &rpf, + VisitorFalse<ReportPrimitiveField>, + VisitorFalse<ReportPrimitiveField>, + ReportPrimitiveFieldCallback<false>, + VisitorFalse<ReportPrimitiveField>); + } + } + return false; + } + + + private: + ReportPrimitiveField(ObjectTagTable* tag_table, + jlong class_tag, + const jvmtiHeapCallbacks* cb, + const void* user_data) + : tag_table_(tag_table), class_tag_(class_tag), cb_(cb), user_data_(user_data) {} + + template <bool kReportStatic> + static bool ReportPrimitiveFieldCallback(art::ObjPtr<art::mirror::Object> obj, + art::ObjPtr<art::mirror::Class> klass, + art::ArtField& field, + size_t field_index, + ReportPrimitiveField* user_data) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::Primitive::Type art_prim_type = field.GetTypeAsPrimitiveType(); + jvmtiPrimitiveType prim_type = + static_cast<jvmtiPrimitiveType>(art::Primitive::Descriptor(art_prim_type)[0]); + DCHECK(prim_type == JVMTI_PRIMITIVE_TYPE_BOOLEAN || + prim_type == JVMTI_PRIMITIVE_TYPE_BYTE || + prim_type == JVMTI_PRIMITIVE_TYPE_CHAR || + prim_type == JVMTI_PRIMITIVE_TYPE_SHORT || + prim_type == JVMTI_PRIMITIVE_TYPE_INT || + prim_type == JVMTI_PRIMITIVE_TYPE_LONG || + prim_type == JVMTI_PRIMITIVE_TYPE_FLOAT || + prim_type == JVMTI_PRIMITIVE_TYPE_DOUBLE); + jvmtiHeapReferenceInfo info; + info.field.index = field_index; + + jvalue value; + memset(&value, 0, sizeof(jvalue)); + art::ObjPtr<art::mirror::Object> src = kReportStatic ? klass : obj; + switch (art_prim_type) { + case art::Primitive::Type::kPrimBoolean: + value.z = field.GetBoolean(src) == 0 ? JNI_FALSE : JNI_TRUE; + break; + case art::Primitive::Type::kPrimByte: + value.b = field.GetByte(src); + break; + case art::Primitive::Type::kPrimChar: + value.c = field.GetChar(src); + break; + case art::Primitive::Type::kPrimShort: + value.s = field.GetShort(src); + break; + case art::Primitive::Type::kPrimInt: + value.i = field.GetInt(src); + break; + case art::Primitive::Type::kPrimLong: + value.j = field.GetLong(src); + break; + case art::Primitive::Type::kPrimFloat: + value.f = field.GetFloat(src); + break; + case art::Primitive::Type::kPrimDouble: + value.d = field.GetDouble(src); + break; + case art::Primitive::Type::kPrimVoid: + case art::Primitive::Type::kPrimNot: { + LOG(FATAL) << "Should not reach here"; + UNREACHABLE(); + } + } + + jlong obj_tag = user_data->tag_table_->GetTagOrZero(src.Ptr()); + const jlong saved_obj_tag = obj_tag; + + jint ret = user_data->cb_->primitive_field_callback(kReportStatic + ? JVMTI_HEAP_REFERENCE_STATIC_FIELD + : JVMTI_HEAP_REFERENCE_FIELD, + &info, + user_data->class_tag_, + &obj_tag, + value, + prim_type, + const_cast<void*>(user_data->user_data_)); + + if (saved_obj_tag != obj_tag) { + user_data->tag_table_->Set(src.Ptr(), obj_tag); + } + + if ((ret & JVMTI_VISIT_ABORT) != 0) { + return true; + } + + return false; + } + + ObjectTagTable* tag_table_; + jlong class_tag_; + const jvmtiHeapCallbacks* cb_; + const void* user_data_; +}; + struct HeapFilter { explicit HeapFilter(jint heap_filter) : filter_out_tagged((heap_filter & JVMTI_HEAP_FILTER_TAGGED) != 0), @@ -197,6 +643,14 @@ struct HeapFilter { } // namespace +void HeapUtil::Register() { + art::Runtime::Current()->AddSystemWeakHolder(&gIndexCachingTable); +} + +void HeapUtil::Unregister() { + art::Runtime::Current()->RemoveSystemWeakHolder(&gIndexCachingTable); +} + struct IterateThroughHeapData { IterateThroughHeapData(HeapUtil* _heap_util, jvmtiEnv* _env, @@ -289,7 +743,12 @@ static void IterateThroughHeapObjectCallback(art::mirror::Object* obj, void* arg ithd->stop_reports = (array_ret & JVMTI_VISIT_ABORT) != 0; } - // TODO Implement primitive field callback. + if (!ithd->stop_reports) { + ithd->stop_reports = ReportPrimitiveField::Report(obj, + ithd->heap_util->GetTags(), + ithd->callbacks, + ithd->user_data); + } } jvmtiError HeapUtil::IterateThroughHeap(jvmtiEnv* env, @@ -423,12 +882,17 @@ class FollowReferencesHelper FINAL { void AddRoot(art::mirror::Object* root_obj, const art::RootInfo& info) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!*tag_table_->GetAllowDisallowLock()) { + if (stop_reports_) { + return; + } + bool add_to_worklist = ReportRoot(root_obj, info); // We use visited_ to mark roots already so we do not need another set. if (visited_->find(root_obj) == visited_->end()) { visited_->insert(root_obj); - worklist_->push_back(root_obj); + if (add_to_worklist) { + worklist_->push_back(root_obj); + } } - ReportRoot(root_obj, info); } // Remove NO_THREAD_SAFETY_ANALYSIS once ASSERT_CAPABILITY works correctly. @@ -534,7 +998,7 @@ class FollowReferencesHelper FINAL { UNREACHABLE(); } - void ReportRoot(art::mirror::Object* root_obj, const art::RootInfo& info) + bool ReportRoot(art::mirror::Object* root_obj, const art::RootInfo& info) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!*tag_table_->GetAllowDisallowLock()) { jvmtiHeapReferenceInfo ref_info; @@ -543,6 +1007,7 @@ class FollowReferencesHelper FINAL { if ((result & JVMTI_VISIT_ABORT) != 0) { stop_reports_ = true; } + return (result & JVMTI_VISIT_OBJECTS) != 0; } private: @@ -565,64 +1030,49 @@ class FollowReferencesHelper FINAL { return; } - // TODO: We'll probably have to rewrite this completely with our own visiting logic, if we - // want to have a chance of getting the field indices computed halfway efficiently. For - // now, ignore them altogether. - - struct InstanceReferenceVisitor { - explicit InstanceReferenceVisitor(FollowReferencesHelper* helper_) - : helper(helper_), stop_reports(false) {} - - void operator()(art::mirror::Object* src, - art::MemberOffset field_offset, - bool is_static ATTRIBUTE_UNUSED) const - REQUIRES_SHARED(art::Locks::mutator_lock_) - REQUIRES(!*helper->tag_table_->GetAllowDisallowLock()) { - if (stop_reports) { - return; - } - - art::mirror::Object* trg = src->GetFieldObjectReferenceAddr(field_offset)->AsMirrorPtr(); + // All instance fields. + auto report_instance_field = [&](art::ObjPtr<art::mirror::Object> src, + art::ObjPtr<art::mirror::Class> obj_klass ATTRIBUTE_UNUSED, + art::ArtField& field, + size_t field_index, + void* user_data ATTRIBUTE_UNUSED) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!*tag_table_->GetAllowDisallowLock()) { + art::ObjPtr<art::mirror::Object> field_value = field.GetObject(src); + if (field_value != nullptr) { jvmtiHeapReferenceInfo reference_info; memset(&reference_info, 0, sizeof(reference_info)); - // TODO: Implement spec-compliant numbering. - reference_info.field.index = field_offset.Int32Value(); + reference_info.field.index = field_index; jvmtiHeapReferenceKind kind = - field_offset.Int32Value() == art::mirror::Object::ClassOffset().Int32Value() + field.GetOffset().Int32Value() == art::mirror::Object::ClassOffset().Int32Value() ? JVMTI_HEAP_REFERENCE_CLASS : JVMTI_HEAP_REFERENCE_FIELD; const jvmtiHeapReferenceInfo* reference_info_ptr = kind == JVMTI_HEAP_REFERENCE_CLASS ? nullptr : &reference_info; - stop_reports = !helper->ReportReferenceMaybeEnqueue(kind, reference_info_ptr, src, trg); - } - - void VisitRoot(art::mirror::CompressedReference<art::mirror::Object>* root ATTRIBUTE_UNUSED) - const { - LOG(FATAL) << "Unreachable"; + return !ReportReferenceMaybeEnqueue(kind, reference_info_ptr, src.Ptr(), field_value.Ptr()); } - void VisitRootIfNonNull( - art::mirror::CompressedReference<art::mirror::Object>* root ATTRIBUTE_UNUSED) const { - LOG(FATAL) << "Unreachable"; - } - - // "mutable" required by the visitor API. - mutable FollowReferencesHelper* helper; - mutable bool stop_reports; + return false; }; + stop_reports_ = FieldVisitor<void, true>::ReportFields(obj, + nullptr, + VisitorFalse<void>, + VisitorFalse<void>, + VisitorFalse<void>, + report_instance_field); + if (stop_reports_) { + return; + } - InstanceReferenceVisitor visitor(this); - // Visit references, not native roots. - obj->VisitReferences<false>(visitor, art::VoidFunctor()); - - stop_reports_ = visitor.stop_reports; - - if (!stop_reports_) { - jint string_ret = ReportString(obj, env, tag_table_, callbacks_, user_data_); - stop_reports_ = (string_ret & JVMTI_VISIT_ABORT) != 0; + jint string_ret = ReportString(obj, env, tag_table_, callbacks_, user_data_); + stop_reports_ = (string_ret & JVMTI_VISIT_ABORT) != 0; + if (stop_reports_) { + return; } + + stop_reports_ = ReportPrimitiveField::Report(obj, tag_table_, callbacks_, user_data_); } void VisitArray(art::mirror::Object* array) @@ -716,26 +1166,38 @@ class FollowReferencesHelper FINAL { DCHECK_EQ(h_klass.Get(), klass); // Declared static fields. - for (auto& field : klass->GetSFields()) { - if (!field.IsPrimitiveType()) { - art::ObjPtr<art::mirror::Object> field_value = field.GetObject(klass); - if (field_value != nullptr) { - jvmtiHeapReferenceInfo reference_info; - memset(&reference_info, 0, sizeof(reference_info)); + auto report_static_field = [&](art::ObjPtr<art::mirror::Object> obj ATTRIBUTE_UNUSED, + art::ObjPtr<art::mirror::Class> obj_klass, + art::ArtField& field, + size_t field_index, + void* user_data ATTRIBUTE_UNUSED) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!*tag_table_->GetAllowDisallowLock()) { + art::ObjPtr<art::mirror::Object> field_value = field.GetObject(obj_klass); + if (field_value != nullptr) { + jvmtiHeapReferenceInfo reference_info; + memset(&reference_info, 0, sizeof(reference_info)); - // TODO: Implement spec-compliant numbering. - reference_info.field.index = field.GetOffset().Int32Value(); + reference_info.field.index = static_cast<jint>(field_index); - stop_reports_ = !ReportReferenceMaybeEnqueue(JVMTI_HEAP_REFERENCE_STATIC_FIELD, - &reference_info, - klass, - field_value.Ptr()); - if (stop_reports_) { - return; - } - } + return !ReportReferenceMaybeEnqueue(JVMTI_HEAP_REFERENCE_STATIC_FIELD, + &reference_info, + obj_klass.Ptr(), + field_value.Ptr()); } + return false; + }; + stop_reports_ = FieldVisitor<void, false>::ReportFields(klass, + nullptr, + VisitorFalse<void>, + report_static_field, + VisitorFalse<void>, + VisitorFalse<void>); + if (stop_reports_) { + return; } + + stop_reports_ = ReportPrimitiveField::Report(klass, tag_table_, callbacks_, user_data_); } void MaybeEnqueue(art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) { diff --git a/runtime/openjdkjvmti/ti_heap.h b/runtime/openjdkjvmti/ti_heap.h index 72ee097566..dccecb4aa3 100644 --- a/runtime/openjdkjvmti/ti_heap.h +++ b/runtime/openjdkjvmti/ti_heap.h @@ -49,6 +49,9 @@ class HeapUtil { return tags_; } + static void Register(); + static void Unregister(); + private: ObjectTagTable* tags_; }; diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc index e494cb6530..941cf7b73b 100644 --- a/runtime/openjdkjvmti/ti_phase.cc +++ b/runtime/openjdkjvmti/ti_phase.cc @@ -40,6 +40,7 @@ #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "thread_list.h" +#include "ti_thread.h" namespace openjdkjvmti { @@ -69,6 +70,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { break; case RuntimePhase::kInit: { + ThreadUtil::CacheData(); ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread()); art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); event_handler->DispatchEvent<ArtJvmtiEvent::kVmInit>(nullptr, GetJniEnv(), thread.get()); @@ -105,6 +107,16 @@ jvmtiError PhaseUtil::GetPhase(jvmtiEnv* env ATTRIBUTE_UNUSED, jvmtiPhase* phase return ERR(NONE); } +bool PhaseUtil::IsLivePhase() { + jvmtiPhase now = PhaseUtil::current_phase_; + DCHECK(now == JVMTI_PHASE_ONLOAD || + now == JVMTI_PHASE_PRIMORDIAL || + now == JVMTI_PHASE_START || + now == JVMTI_PHASE_LIVE || + now == JVMTI_PHASE_DEAD); + return now == JVMTI_PHASE_LIVE; +} + void PhaseUtil::SetToOnLoad() { DCHECK_EQ(0u, static_cast<size_t>(PhaseUtil::current_phase_)); PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD; @@ -117,6 +129,7 @@ void PhaseUtil::SetToPrimordial() { void PhaseUtil::SetToLive() { DCHECK_EQ(static_cast<size_t>(0), static_cast<size_t>(PhaseUtil::current_phase_)); + ThreadUtil::CacheData(); PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; } diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h index 851fc27de5..a2c0d114ef 100644 --- a/runtime/openjdkjvmti/ti_phase.h +++ b/runtime/openjdkjvmti/ti_phase.h @@ -42,6 +42,7 @@ class EventHandler; class PhaseUtil { public: static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr); + static bool IsLivePhase(); static void Register(EventHandler* event_handler); static void Unregister(); diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index c4d20c007e..7d95de823e 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -178,7 +178,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { art::ClassLinker* cl = runtime->GetClassLinker(); auto ptr_size = cl->GetImagePointerSize(); const size_t method_size = art::ArtMethod::Size(ptr_size); - auto* method_storage = allocator_->Alloc(GetThread(), method_size); + auto* method_storage = allocator_->Alloc(art::Thread::Current(), method_size); CHECK(method_storage != nullptr) << "Unable to allocate storage for obsolete version of '" << old_method->PrettyMethod() << "'"; new_obsolete_method = new (method_storage) art::ArtMethod(); @@ -186,6 +186,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass()); new_obsolete_method->SetIsObsolete(); new_obsolete_method->SetDontCompile(); + cl->SetEntryPointsForObsoleteMethod(new_obsolete_method); obsolete_maps_->RecordObsolete(old_method, new_obsolete_method); // Update JIT Data structures to point to the new method. art::jit::Jit* jit = art::Runtime::Current()->GetJit(); @@ -261,13 +262,12 @@ jvmtiError Redefiner::GetClassRedefinitionError(art::Handle<art::mirror::Class> // Moves dex data to an anonymous, read-only mmap'd region. std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location, - jint data_len, - const unsigned char* dex_data, + art::ArraySlice<const unsigned char> data, std::string* error_msg) { std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous( StringPrintf("%s-transformed", original_location.c_str()).c_str(), nullptr, - data_len, + data.size(), PROT_READ|PROT_WRITE, /*low_4gb*/false, /*reuse*/false, @@ -275,7 +275,7 @@ std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& orig if (map == nullptr) { return map; } - memcpy(map->Begin(), dex_data, data_len); + memcpy(map->Begin(), &data.At(0), data.size()); // Make the dex files mmap read only. This matches how other DexFiles are mmaped and prevents // programs from corrupting it. map->Protect(PROT_READ); @@ -325,25 +325,26 @@ jvmtiError Redefiner::RedefineClasses(ArtJvmTiEnv* env, std::vector<ArtClassDefinition> def_vector; def_vector.reserve(class_count); for (jint i = 0; i < class_count; i++) { + jboolean is_modifiable = JNI_FALSE; + jvmtiError res = env->IsModifiableClass(definitions[i].klass, &is_modifiable); + if (res != OK) { + return res; + } else if (!is_modifiable) { + return ERR(UNMODIFIABLE_CLASS); + } // We make a copy of the class_bytes to pass into the retransformation. // This makes cleanup easier (since we unambiguously own the bytes) and also is useful since we // will need to keep the original bytes around unaltered for subsequent RetransformClasses calls // to get the passed in bytes. unsigned char* class_bytes_copy = nullptr; - jvmtiError res = env->Allocate(definitions[i].class_byte_count, &class_bytes_copy); + res = env->Allocate(definitions[i].class_byte_count, &class_bytes_copy); if (res != OK) { return res; } memcpy(class_bytes_copy, definitions[i].class_bytes, definitions[i].class_byte_count); ArtClassDefinition def; - def.dex_len = definitions[i].class_byte_count; - def.dex_data = MakeJvmtiUniquePtr(env, class_bytes_copy); - // We are definitely modified. - def.SetModified(); - def.original_dex_file = art::ArraySlice<const unsigned char>(definitions[i].class_bytes, - definitions[i].class_byte_count); - res = Transformer::FillInTransformationData(env, definitions[i].klass, &def); + res = def.Init(env, definitions[i]); if (res != OK) { return res; } @@ -379,7 +380,7 @@ jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env, Redefiner r(runtime, self, error_msg); for (const ArtClassDefinition& def : definitions) { // Only try to transform classes that have been modified. - if (def.IsModified(self)) { + if (def.IsModified()) { jvmtiError res = r.AddRedefinition(env, def); if (res != OK) { return res; @@ -392,25 +393,24 @@ jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env, jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition& def) { std::string original_dex_location; jvmtiError ret = OK; - if ((ret = GetClassLocation(env, def.klass, &original_dex_location))) { + if ((ret = GetClassLocation(env, def.GetClass(), &original_dex_location))) { *error_msg_ = "Unable to get original dex file location!"; return ret; } char* generic_ptr_unused = nullptr; char* signature_ptr = nullptr; - if ((ret = env->GetClassSignature(def.klass, &signature_ptr, &generic_ptr_unused)) != OK) { + if ((ret = env->GetClassSignature(def.GetClass(), &signature_ptr, &generic_ptr_unused)) != OK) { *error_msg_ = "Unable to get class signature!"; return ret; } JvmtiUniquePtr<char> generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused)); JvmtiUniquePtr<char> signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr)); std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location, - def.dex_len, - def.dex_data.get(), + def.GetDexData(), error_msg_)); std::ostringstream os; if (map.get() == nullptr) { - os << "Failed to create anonymous mmap for modified dex file of class " << def.name + os << "Failed to create anonymous mmap for modified dex file of class " << def.GetName() << "in dex file " << original_dex_location << " because: " << *error_msg_; *error_msg_ = os.str(); return ERR(OUT_OF_MEMORY); @@ -427,16 +427,16 @@ jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition /*verify_checksum*/true, error_msg_)); if (dex_file.get() == nullptr) { - os << "Unable to load modified dex file for " << def.name << ": " << *error_msg_; + os << "Unable to load modified dex file for " << def.GetName() << ": " << *error_msg_; *error_msg_ = os.str(); return ERR(INVALID_CLASS_FORMAT); } redefinitions_.push_back( Redefiner::ClassRedefinition(this, - def.klass, + def.GetClass(), dex_file.release(), signature_ptr, - def.original_dex_file)); + def.GetNewOriginalDexFile())); return OK; } @@ -462,7 +462,7 @@ void Redefiner::RecordFailure(jvmtiError result, result_ = result; } -art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFileBytes() { +art::mirror::Object* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFile() { // If we have been specifically given a new set of bytes use that if (original_dex_file_.size() != 0) { return art::mirror::ByteArray::AllocateAndFill( @@ -474,24 +474,21 @@ art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFi // See if we already have one set. art::ObjPtr<art::mirror::ClassExt> ext(GetMirrorClass()->GetExtData()); if (!ext.IsNull()) { - art::ObjPtr<art::mirror::ByteArray> old_original_bytes(ext->GetOriginalDexFileBytes()); - if (!old_original_bytes.IsNull()) { + art::ObjPtr<art::mirror::Object> old_original_dex_file(ext->GetOriginalDexFile()); + if (!old_original_dex_file.IsNull()) { // We do. Use it. - return old_original_bytes.Ptr(); + return old_original_dex_file.Ptr(); } } - // Copy the current dex_file - const art::DexFile& current_dex_file = GetMirrorClass()->GetDexFile(); + // return the current dex_cache which has the dex file in it. + art::ObjPtr<art::mirror::DexCache> current_dex_cache(GetMirrorClass()->GetDexCache()); // TODO Handle this or make it so it cannot happen. - if (current_dex_file.NumClassDefs() != 1) { + if (current_dex_cache->GetDexFile()->NumClassDefs() != 1) { LOG(WARNING) << "Current dex file has more than one class in it. Calling RetransformClasses " << "on this class might fail if no transformations are applied to it!"; } - return art::mirror::ByteArray::AllocateAndFill( - driver_->self_, - reinterpret_cast<const signed char*>(current_dex_file.Begin()), - current_dex_file.Size()); + return current_dex_cache.Ptr(); } struct CallbackCtx { @@ -779,6 +776,8 @@ bool Redefiner::ClassRedefinition::CheckRedefinitionIsValid() { CheckSameMethods(); } +class RedefinitionDataIter; + // A wrapper that lets us hold onto the arbitrary sized data needed for redefinitions in a // reasonably sane way. This adds no fields to the normal ObjectArray. By doing this we can avoid // having to deal with the fact that we need to hold an arbitrary number of references live. @@ -802,13 +801,15 @@ class RedefinitionDataHolder { RedefinitionDataHolder(art::StackHandleScope<1>* hs, art::Runtime* runtime, art::Thread* self, - int32_t num_redefinitions) REQUIRES_SHARED(art::Locks::mutator_lock_) : + std::vector<Redefiner::ClassRedefinition>* redefinitions) + REQUIRES_SHARED(art::Locks::mutator_lock_) : arr_( hs->NewHandle( art::mirror::ObjectArray<art::mirror::Object>::Alloc( self, runtime->GetClassLinker()->GetClassRoot(art::ClassLinker::kObjectArrayClass), - num_redefinitions * kNumSlots))) {} + redefinitions->size() * kNumSlots))), + redefinitions_(redefinitions) {} bool IsNull() const REQUIRES_SHARED(art::Locks::mutator_lock_) { return arr_.IsNull(); @@ -836,9 +837,9 @@ class RedefinitionDataHolder { return art::down_cast<art::mirror::Class*>(GetSlot(klass_index, kSlotMirrorClass)); } - art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index) const + art::mirror::Object* GetOriginalDexFile(jint klass_index) const REQUIRES_SHARED(art::Locks::mutator_lock_) { - return art::down_cast<art::mirror::ByteArray*>(GetSlot(klass_index, kSlotOrigDexFile)); + return art::down_cast<art::mirror::Object*>(GetSlot(klass_index, kSlotOrigDexFile)); } void SetSourceClassLoader(jint klass_index, art::mirror::ClassLoader* loader) @@ -861,7 +862,7 @@ class RedefinitionDataHolder { REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotMirrorClass, klass); } - void SetOriginalDexFileBytes(jint klass_index, art::mirror::ByteArray* bytes) + void SetOriginalDexFile(jint klass_index, art::mirror::Object* bytes) REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotOrigDexFile, bytes); } @@ -870,8 +871,27 @@ class RedefinitionDataHolder { return arr_->GetLength() / kNumSlots; } + std::vector<Redefiner::ClassRedefinition>* GetRedefinitions() + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return redefinitions_; + } + + bool operator==(const RedefinitionDataHolder& other) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return arr_.Get() == other.arr_.Get(); + } + + bool operator!=(const RedefinitionDataHolder& other) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return !(*this == other); + } + + RedefinitionDataIter begin() REQUIRES_SHARED(art::Locks::mutator_lock_); + RedefinitionDataIter end() REQUIRES_SHARED(art::Locks::mutator_lock_); + private: mutable art::Handle<art::mirror::ObjectArray<art::mirror::Object>> arr_; + std::vector<Redefiner::ClassRedefinition>* redefinitions_; art::mirror::Object* GetSlot(jint klass_index, DataSlot slot) const REQUIRES_SHARED(art::Locks::mutator_lock_) { @@ -890,8 +910,115 @@ class RedefinitionDataHolder { DISALLOW_COPY_AND_ASSIGN(RedefinitionDataHolder); }; -bool Redefiner::ClassRedefinition::CheckVerification(int32_t klass_index, - const RedefinitionDataHolder& holder) { +class RedefinitionDataIter { + public: + RedefinitionDataIter(int32_t idx, RedefinitionDataHolder& holder) : idx_(idx), holder_(holder) {} + + RedefinitionDataIter(const RedefinitionDataIter&) = default; + RedefinitionDataIter(RedefinitionDataIter&&) = default; + RedefinitionDataIter& operator=(const RedefinitionDataIter&) = default; + RedefinitionDataIter& operator=(RedefinitionDataIter&&) = default; + + bool operator==(const RedefinitionDataIter& other) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return idx_ == other.idx_ && holder_ == other.holder_; + } + + bool operator!=(const RedefinitionDataIter& other) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return !(*this == other); + } + + RedefinitionDataIter operator++() { // Value after modification. + idx_++; + return *this; + } + + RedefinitionDataIter operator++(int) { + RedefinitionDataIter temp = *this; + idx_++; + return temp; + } + + RedefinitionDataIter operator+(ssize_t delta) const { + RedefinitionDataIter temp = *this; + temp += delta; + return temp; + } + + RedefinitionDataIter& operator+=(ssize_t delta) { + idx_ += delta; + return *this; + } + + Redefiner::ClassRedefinition& GetRedefinition() REQUIRES_SHARED(art::Locks::mutator_lock_) { + return (*holder_.GetRedefinitions())[idx_]; + } + + RedefinitionDataHolder& GetHolder() { + return holder_; + } + + art::mirror::ClassLoader* GetSourceClassLoader() const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return holder_.GetSourceClassLoader(idx_); + } + art::mirror::Object* GetJavaDexFile() const REQUIRES_SHARED(art::Locks::mutator_lock_) { + return holder_.GetJavaDexFile(idx_); + } + art::mirror::LongArray* GetNewDexFileCookie() const REQUIRES_SHARED(art::Locks::mutator_lock_) { + return holder_.GetNewDexFileCookie(idx_); + } + art::mirror::DexCache* GetNewDexCache() const REQUIRES_SHARED(art::Locks::mutator_lock_) { + return holder_.GetNewDexCache(idx_); + } + art::mirror::Class* GetMirrorClass() const REQUIRES_SHARED(art::Locks::mutator_lock_) { + return holder_.GetMirrorClass(idx_); + } + art::mirror::Object* GetOriginalDexFile() const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return holder_.GetOriginalDexFile(idx_); + } + int32_t GetIndex() const { + return idx_; + } + + void SetSourceClassLoader(art::mirror::ClassLoader* loader) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + holder_.SetSourceClassLoader(idx_, loader); + } + void SetJavaDexFile(art::mirror::Object* dexfile) REQUIRES_SHARED(art::Locks::mutator_lock_) { + holder_.SetJavaDexFile(idx_, dexfile); + } + void SetNewDexFileCookie(art::mirror::LongArray* cookie) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + holder_.SetNewDexFileCookie(idx_, cookie); + } + void SetNewDexCache(art::mirror::DexCache* cache) REQUIRES_SHARED(art::Locks::mutator_lock_) { + holder_.SetNewDexCache(idx_, cache); + } + void SetMirrorClass(art::mirror::Class* klass) REQUIRES_SHARED(art::Locks::mutator_lock_) { + holder_.SetMirrorClass(idx_, klass); + } + void SetOriginalDexFile(art::mirror::Object* bytes) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + holder_.SetOriginalDexFile(idx_, bytes); + } + + private: + int32_t idx_; + RedefinitionDataHolder& holder_; +}; + +RedefinitionDataIter RedefinitionDataHolder::begin() { + return RedefinitionDataIter(0, *this); +} + +RedefinitionDataIter RedefinitionDataHolder::end() { + return RedefinitionDataIter(Length(), *this); +} + +bool Redefiner::ClassRedefinition::CheckVerification(const RedefinitionDataIter& iter) { DCHECK_EQ(dex_file_->NumClassDefs(), 1u); art::StackHandleScope<2> hs(driver_->self_); std::string error; @@ -899,7 +1026,7 @@ bool Redefiner::ClassRedefinition::CheckVerification(int32_t klass_index, art::verifier::MethodVerifier::FailureKind failure = art::verifier::MethodVerifier::VerifyClass(driver_->self_, dex_file_.get(), - hs.NewHandle(holder.GetNewDexCache(klass_index)), + hs.NewHandle(iter.GetNewDexCache()), hs.NewHandle(GetClassLoader()), dex_file_->GetClassDef(0), /*class_def*/ nullptr, /*compiler_callbacks*/ @@ -918,21 +1045,20 @@ bool Redefiner::ClassRedefinition::CheckVerification(int32_t klass_index, // dexfile. This is so that even if multiple classes with the same classloader are redefined at // once they are all added to the classloader. bool Redefiner::ClassRedefinition::AllocateAndRememberNewDexFileCookie( - int32_t klass_index, art::Handle<art::mirror::ClassLoader> source_class_loader, art::Handle<art::mirror::Object> dex_file_obj, - /*out*/RedefinitionDataHolder* holder) { + /*out*/RedefinitionDataIter* cur_data) { art::StackHandleScope<2> hs(driver_->self_); art::MutableHandle<art::mirror::LongArray> old_cookie( hs.NewHandle<art::mirror::LongArray>(nullptr)); bool has_older_cookie = false; // See if we already have a cookie that a previous redefinition got from the same classloader. - for (int32_t i = 0; i < klass_index; i++) { - if (holder->GetSourceClassLoader(i) == source_class_loader.Get()) { + for (auto old_data = cur_data->GetHolder().begin(); old_data != *cur_data; ++old_data) { + if (old_data.GetSourceClassLoader() == source_class_loader.Get()) { // Since every instance of this classloader should have the same cookie associated with it we // can stop looking here. has_older_cookie = true; - old_cookie.Assign(holder->GetNewDexFileCookie(i)); + old_cookie.Assign(old_data.GetNewDexFileCookie()); break; } } @@ -953,14 +1079,14 @@ bool Redefiner::ClassRedefinition::AllocateAndRememberNewDexFileCookie( } // Save the cookie. - holder->SetNewDexFileCookie(klass_index, new_cookie.Get()); + cur_data->SetNewDexFileCookie(new_cookie.Get()); // If there are other copies of this same classloader we need to make sure that we all have the // same cookie. if (has_older_cookie) { - for (int32_t i = 0; i < klass_index; i++) { + for (auto old_data = cur_data->GetHolder().begin(); old_data != *cur_data; ++old_data) { // We will let the GC take care of the cookie we allocated for this one. - if (holder->GetSourceClassLoader(i) == source_class_loader.Get()) { - holder->SetNewDexFileCookie(i, new_cookie.Get()); + if (old_data.GetSourceClassLoader() == source_class_loader.Get()) { + old_data.SetNewDexFileCookie(new_cookie.Get()); } } } @@ -969,32 +1095,32 @@ bool Redefiner::ClassRedefinition::AllocateAndRememberNewDexFileCookie( } bool Redefiner::ClassRedefinition::FinishRemainingAllocations( - int32_t klass_index, /*out*/RedefinitionDataHolder* holder) { + /*out*/RedefinitionDataIter* cur_data) { art::ScopedObjectAccessUnchecked soa(driver_->self_); art::StackHandleScope<2> hs(driver_->self_); - holder->SetMirrorClass(klass_index, GetMirrorClass()); + cur_data->SetMirrorClass(GetMirrorClass()); // This shouldn't allocate art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader())); // The bootclasspath is handled specially so it doesn't have a j.l.DexFile. if (!art::ClassLinker::IsBootClassLoader(soa, loader.Get())) { - holder->SetSourceClassLoader(klass_index, loader.Get()); + cur_data->SetSourceClassLoader(loader.Get()); art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle( ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader))); - holder->SetJavaDexFile(klass_index, dex_file_obj.Get()); + cur_data->SetJavaDexFile(dex_file_obj.Get()); if (dex_file_obj == nullptr) { RecordFailure(ERR(INTERNAL), "Unable to find dex file!"); return false; } // Allocate the new dex file cookie. - if (!AllocateAndRememberNewDexFileCookie(klass_index, loader, dex_file_obj, holder)) { + if (!AllocateAndRememberNewDexFileCookie(loader, dex_file_obj, cur_data)) { driver_->self_->AssertPendingOOMException(); driver_->self_->ClearException(); RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate dex file array for class loader"); return false; } } - holder->SetNewDexCache(klass_index, CreateNewDexCache(loader)); - if (holder->GetNewDexCache(klass_index) == nullptr) { + cur_data->SetNewDexCache(CreateNewDexCache(loader)); + if (cur_data->GetNewDexCache() == nullptr) { driver_->self_->AssertPendingException(); driver_->self_->ClearException(); RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate DexCache"); @@ -1002,8 +1128,8 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations( } // We won't always need to set this field. - holder->SetOriginalDexFileBytes(klass_index, AllocateOrGetOriginalDexFileBytes()); - if (holder->GetOriginalDexFileBytes(klass_index) == nullptr) { + cur_data->SetOriginalDexFile(AllocateOrGetOriginalDexFile()); + if (cur_data->GetOriginalDexFile() == nullptr) { driver_->self_->AssertPendingOOMException(); driver_->self_->ClearException(); RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate array for original dex file"); @@ -1048,13 +1174,11 @@ bool Redefiner::EnsureAllClassAllocationsFinished() { } bool Redefiner::FinishAllRemainingAllocations(RedefinitionDataHolder& holder) { - int32_t cnt = 0; - for (Redefiner::ClassRedefinition& redef : redefinitions_) { + for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) { // Allocate the data this redefinition requires. - if (!redef.FinishRemainingAllocations(cnt, &holder)) { + if (!data.GetRedefinition().FinishRemainingAllocations(&data)) { return false; } - cnt++; } return true; } @@ -1069,22 +1193,39 @@ void Redefiner::ReleaseAllDexFiles() { } } -bool Redefiner::CheckAllClassesAreVerified(const RedefinitionDataHolder& holder) { - int32_t cnt = 0; - for (Redefiner::ClassRedefinition& redef : redefinitions_) { - if (!redef.CheckVerification(cnt, holder)) { +bool Redefiner::CheckAllClassesAreVerified(RedefinitionDataHolder& holder) { + for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) { + if (!data.GetRedefinition().CheckVerification(data)) { return false; } - cnt++; } return true; } +class ScopedDisableConcurrentAndMovingGc { + public: + ScopedDisableConcurrentAndMovingGc(art::gc::Heap* heap, art::Thread* self) + : heap_(heap), self_(self) { + if (heap_->IsGcConcurrentAndMoving()) { + heap_->IncrementDisableMovingGC(self_); + } + } + + ~ScopedDisableConcurrentAndMovingGc() { + if (heap_->IsGcConcurrentAndMoving()) { + heap_->DecrementDisableMovingGC(self_); + } + } + private: + art::gc::Heap* heap_; + art::Thread* self_; +}; + jvmtiError Redefiner::Run() { art::StackHandleScope<1> hs(self_); // Allocate an array to hold onto all java temporary objects associated with this redefinition. // We will let this be collected after the end of this function. - RedefinitionDataHolder holder(&hs, runtime_, self_, redefinitions_.size()); + RedefinitionDataHolder holder(&hs, runtime_, self_, &redefinitions_); if (holder.IsNull()) { self_->AssertPendingOOMException(); self_->ClearException(); @@ -1107,57 +1248,43 @@ jvmtiError Redefiner::Run() { // cleaned up by the GC eventually. return result_; } + // At this point we can no longer fail without corrupting the runtime state. - int32_t counter = 0; - for (Redefiner::ClassRedefinition& redef : redefinitions_) { - if (holder.GetSourceClassLoader(counter) == nullptr) { - runtime_->GetClassLinker()->AppendToBootClassPath(self_, redef.GetDexFile()); + for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) { + if (data.GetSourceClassLoader() == nullptr) { + runtime_->GetClassLinker()->AppendToBootClassPath(self_, data.GetRedefinition().GetDexFile()); } - counter++; } UnregisterAllBreakpoints(); + // Disable GC and wait for it to be done if we are a moving GC. This is fine since we are done // allocating so no deadlocks. - art::gc::Heap* heap = runtime_->GetHeap(); - if (heap->IsGcConcurrentAndMoving()) { - // GC moving objects can cause deadlocks as we are deoptimizing the stack. - heap->IncrementDisableMovingGC(self_); - } + ScopedDisableConcurrentAndMovingGc sdcamgc(runtime_->GetHeap(), self_); + // Do transition to final suspension // TODO We might want to give this its own suspended state! // TODO This isn't right. We need to change state without any chance of suspend ideally! - self_->TransitionFromRunnableToSuspended(art::ThreadState::kNative); - runtime_->GetThreadList()->SuspendAll( - "Final installation of redefined Classes!", /*long_suspend*/true); - counter = 0; - for (Redefiner::ClassRedefinition& redef : redefinitions_) { + art::ScopedThreadSuspension sts(self_, art::ThreadState::kNative); + art::ScopedSuspendAll ssa("Final installation of redefined Classes!", /*long_suspend*/true); + for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) { art::ScopedAssertNoThreadSuspension nts("Updating runtime objects for redefinition"); - if (holder.GetSourceClassLoader(counter) != nullptr) { - ClassLoaderHelper::UpdateJavaDexFile(holder.GetJavaDexFile(counter), - holder.GetNewDexFileCookie(counter)); + ClassRedefinition& redef = data.GetRedefinition(); + if (data.GetSourceClassLoader() != nullptr) { + ClassLoaderHelper::UpdateJavaDexFile(data.GetJavaDexFile(), data.GetNewDexFileCookie()); } - art::mirror::Class* klass = holder.GetMirrorClass(counter); + art::mirror::Class* klass = data.GetMirrorClass(); // TODO Rewrite so we don't do a stack walk for each and every class. redef.FindAndAllocateObsoleteMethods(klass); - redef.UpdateClass(klass, holder.GetNewDexCache(counter), - holder.GetOriginalDexFileBytes(counter)); - counter++; + redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFile()); } // TODO We should check for if any of the redefined methods are intrinsic methods here and, if any // are, force a full-world deoptimization before finishing redefinition. If we don't do this then // methods that have been jitted prior to the current redefinition being applied might continue // to use the old versions of the intrinsics! // TODO Shrink the obsolete method maps if possible? - // TODO Put this into a scoped thing. - runtime_->GetThreadList()->ResumeAll(); - // Get back shared mutator lock as expected for return. - self_->TransitionFromSuspendedToRunnable(); // TODO Do the dex_file release at a more reasonable place. This works but it muddles who really // owns the DexFile and when ownership is transferred. ReleaseAllDexFiles(); - if (heap->IsGcConcurrentAndMoving()) { - heap->DecrementDisableMovingGC(self_); - } return OK; } @@ -1228,7 +1355,7 @@ void Redefiner::ClassRedefinition::UpdateFields(art::ObjPtr<art::mirror::Class> void Redefiner::ClassRedefinition::UpdateClass( art::ObjPtr<art::mirror::Class> mclass, art::ObjPtr<art::mirror::DexCache> new_dex_cache, - art::ObjPtr<art::mirror::ByteArray> original_dex_file) { + art::ObjPtr<art::mirror::Object> original_dex_file) { DCHECK_EQ(dex_file_->NumClassDefs(), 1u); const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0); UpdateMethods(mclass, new_dex_cache, class_def); @@ -1242,7 +1369,7 @@ void Redefiner::ClassRedefinition::UpdateClass( mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_.c_str()))); art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData()); CHECK(!ext.IsNull()); - ext->SetOriginalDexFileBytes(original_dex_file); + ext->SetOriginalDexFile(original_dex_file); } // This function does all (java) allocations we need to do for the Class being redefined. @@ -1259,8 +1386,6 @@ bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished() { art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(driver_->self_))); if (ext == nullptr) { // No memory. Clear exception (it's not useful) and return error. - // TODO This doesn't need to be fatal. We could just not support obsolete methods after hitting - // this case. driver_->self_->AssertPendingOOMException(); driver_->self_->ClearException(); RecordFailure(ERR(OUT_OF_MEMORY), "Could not allocate ClassExt"); diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index 4e6d05f056..809a681902 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -66,6 +66,7 @@ namespace openjdkjvmti { class RedefinitionDataHolder; +class RedefinitionDataIter; // Class that can redefine a single class's methods. // TODO We should really make this be driven by an outside class so we can do multiple classes at @@ -98,8 +99,7 @@ class Redefiner { static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable); static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location, - jint data_len, - const unsigned char* dex_data, + art::ArraySlice<const unsigned char> data, std::string* error_msg); private: @@ -136,21 +136,20 @@ class Redefiner { REQUIRES_SHARED(art::Locks::mutator_lock_); // This may return nullptr with a OOME pending if allocation fails. - art::mirror::ByteArray* AllocateOrGetOriginalDexFileBytes() + art::mirror::Object* AllocateOrGetOriginalDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_); void RecordFailure(jvmtiError e, const std::string& err) { driver_->RecordFailure(e, class_sig_, err); } - bool FinishRemainingAllocations(int32_t klass_index, /*out*/RedefinitionDataHolder* holder) + bool FinishRemainingAllocations(/*out*/RedefinitionDataIter* cur_data) REQUIRES_SHARED(art::Locks::mutator_lock_); bool AllocateAndRememberNewDexFileCookie( - int32_t klass_index, art::Handle<art::mirror::ClassLoader> source_class_loader, art::Handle<art::mirror::Object> dex_file_obj, - /*out*/RedefinitionDataHolder* holder) + /*out*/RedefinitionDataIter* cur_data) REQUIRES_SHARED(art::Locks::mutator_lock_); void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) @@ -161,8 +160,7 @@ class Redefiner { bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_); // Checks that the contained class can be successfully verified. - bool CheckVerification(int32_t klass_index, - const RedefinitionDataHolder& holder) + bool CheckVerification(const RedefinitionDataIter& holder) REQUIRES_SHARED(art::Locks::mutator_lock_); // Preallocates all needed allocations in klass so that we can pause execution safely. @@ -197,7 +195,7 @@ class Redefiner { void UpdateClass(art::ObjPtr<art::mirror::Class> mclass, art::ObjPtr<art::mirror::DexCache> new_dex_cache, - art::ObjPtr<art::mirror::ByteArray> original_dex_file) + art::ObjPtr<art::mirror::Object> original_dex_file) REQUIRES(art::Locks::mutator_lock_); void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_); @@ -241,7 +239,7 @@ class Redefiner { jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_); bool CheckAllRedefinitionAreValid() REQUIRES_SHARED(art::Locks::mutator_lock_); - bool CheckAllClassesAreVerified(const RedefinitionDataHolder& holder) + bool CheckAllClassesAreVerified(RedefinitionDataHolder& holder) REQUIRES_SHARED(art::Locks::mutator_lock_); bool EnsureAllClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_); bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder) @@ -255,6 +253,8 @@ class Redefiner { } friend struct CallbackCtx; + friend class RedefinitionDataHolder; + friend class RedefinitionDataIter; }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc index 788ac30873..e5ff090ddf 100644 --- a/runtime/openjdkjvmti/ti_thread.cc +++ b/runtime/openjdkjvmti/ti_thread.cc @@ -44,6 +44,7 @@ #include "mirror/object-inl.h" #include "mirror/string.h" #include "obj_ptr.h" +#include "ti_phase.h" #include "runtime.h" #include "runtime_callbacks.h" #include "ScopedLocalRef.h" @@ -54,6 +55,8 @@ namespace openjdkjvmti { +art::ArtField* ThreadUtil::context_class_loader_ = nullptr; + struct ThreadCallback : public art::ThreadLifecycleCallback, public art::RuntimePhaseCallback { jthread GetThreadObject(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) { if (self->GetPeer() == nullptr) { @@ -121,6 +124,16 @@ void ThreadUtil::Register(EventHandler* handler) { runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gThreadCallback); } +void ThreadUtil::CacheData() { + art::ScopedObjectAccess soa(art::Thread::Current()); + art::ObjPtr<art::mirror::Class> thread_class = + soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread); + CHECK(thread_class != nullptr); + context_class_loader_ = thread_class->FindDeclaredInstanceField("contextClassLoader", + "Ljava/lang/ClassLoader;"); + CHECK(context_class_loader_ != nullptr); +} + void ThreadUtil::Unregister() { art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kWaitingForDebuggerToAttach); @@ -146,22 +159,6 @@ jvmtiError ThreadUtil::GetCurrentThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread* return ERR(NONE); } -// Read the context classloader from a Java thread object. This is a lazy implementation -// that assumes GetThreadInfo isn't called too often. If we instead cache the ArtField, -// we will have to add synchronization as this can't be cached on startup (which is -// potentially runtime startup). -static art::ObjPtr<art::mirror::Object> GetContextClassLoader(art::ObjPtr<art::mirror::Object> peer) - REQUIRES_SHARED(art::Locks::mutator_lock_) { - if (peer == nullptr) { - return nullptr; - } - art::ObjPtr<art::mirror::Class> klass = peer->GetClass(); - art::ArtField* cc_field = klass->FindDeclaredInstanceField("contextClassLoader", - "Ljava/lang/ClassLoader;"); - CHECK(cc_field != nullptr); - return cc_field->GetObject(peer); -} - // Get the native thread. The spec says a null object denotes the current thread. static art::Thread* GetNativeThread(jthread thread, const art::ScopedObjectAccessAlreadyRunnable& soa) @@ -178,6 +175,9 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI if (info_ptr == nullptr) { return ERR(NULL_POINTER); } + if (!PhaseUtil::IsLivePhase()) { + return JVMTI_ERROR_WRONG_PHASE; + } art::ScopedObjectAccess soa(art::Thread::Current()); @@ -217,7 +217,10 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI } // Context classloader. - art::ObjPtr<art::mirror::Object> ccl = GetContextClassLoader(peer); + DCHECK(context_class_loader_ != nullptr); + art::ObjPtr<art::mirror::Object> ccl = peer != nullptr + ? context_class_loader_->GetObject(peer) + : nullptr; info_ptr->context_class_loader = ccl == nullptr ? nullptr : soa.AddLocalReference<jobject>(ccl); @@ -272,7 +275,10 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI } // Context classloader. - art::ObjPtr<art::mirror::Object> ccl = GetContextClassLoader(peer); + DCHECK(context_class_loader_ != nullptr); + art::ObjPtr<art::mirror::Object> ccl = peer != nullptr + ? context_class_loader_->GetObject(peer) + : nullptr; info_ptr->context_class_loader = ccl == nullptr ? nullptr : soa.AddLocalReference<jobject>(ccl); diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h index f6f93ee91a..c7f75d8aec 100644 --- a/runtime/openjdkjvmti/ti_thread.h +++ b/runtime/openjdkjvmti/ti_thread.h @@ -35,6 +35,10 @@ #include "jni.h" #include "jvmti.h" +namespace art { +class ArtField; +} + namespace openjdkjvmti { class EventHandler; @@ -44,6 +48,9 @@ class ThreadUtil { static void Register(EventHandler* event_handler); static void Unregister(); + // To be called when it is safe to cache data. + static void CacheData(); + static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr); static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr); @@ -60,6 +67,9 @@ class ThreadUtil { jvmtiStartFunction proc, const void* arg, jint priority); + + private: + static art::ArtField* context_class_loader_; }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc index 36421b9137..15d8dd0fc2 100644 --- a/runtime/openjdkjvmti/transform.cc +++ b/runtime/openjdkjvmti/transform.cc @@ -42,6 +42,7 @@ #include "gc_root-inl.h" #include "globals.h" #include "jni_env_ext-inl.h" +#include "jvalue.h" #include "jvmti.h" #include "linear_alloc.h" #include "mem_map.h" @@ -69,17 +70,18 @@ jvmtiError Transformer::RetransformClassesDirect( for (ArtClassDefinition& def : *definitions) { jint new_len = -1; unsigned char* new_data = nullptr; + art::ArraySlice<const unsigned char> dex_data = def.GetDexData(); event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( self, GetJniEnv(env), - def.klass, - def.loader, - def.name.c_str(), - def.protection_domain, - def.dex_len, - static_cast<const unsigned char*>(def.dex_data.get()), - &new_len, - &new_data); + def.GetClass(), + def.GetLoader(), + def.GetName().c_str(), + def.GetProtectionDomain(), + static_cast<jint>(dex_data.size()), + &dex_data.At(0), + /*out*/&new_len, + /*out*/&new_data); def.SetNewDexData(env, new_len, new_data); } return OK; @@ -109,8 +111,15 @@ jvmtiError Transformer::RetransformClasses(ArtJvmTiEnv* env, std::vector<ArtClassDefinition> definitions; jvmtiError res = OK; for (jint i = 0; i < class_count; i++) { + jboolean is_modifiable = JNI_FALSE; + res = env->IsModifiableClass(classes[i], &is_modifiable); + if (res != OK) { + return res; + } else if (!is_modifiable) { + return ERR(UNMODIFIABLE_CLASS); + } ArtClassDefinition def; - res = FillInTransformationData(env, classes[i], &def); + res = def.Init(env, classes[i]); if (res != OK) { return res; } @@ -139,63 +148,4 @@ jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* return OK; } -jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env, - art::Handle<art::mirror::Class> klass, - /*out*/jint* dex_data_len, - /*out*/unsigned char** dex_data) { - art::StackHandleScope<2> hs(art::Thread::Current()); - art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData())); - if (!ext.IsNull()) { - art::Handle<art::mirror::ByteArray> orig_dex(hs.NewHandle(ext->GetOriginalDexFileBytes())); - if (!orig_dex.IsNull()) { - *dex_data_len = static_cast<jint>(orig_dex->GetLength()); - return CopyDataIntoJvmtiBuffer(env, - reinterpret_cast<const unsigned char*>(orig_dex->GetData()), - *dex_data_len, - /*out*/dex_data); - } - } - // TODO De-quicken the dex file before passing it to the agents. - LOG(WARNING) << "Dex file is not de-quickened yet! Quickened dex instructions might be present"; - const art::DexFile& dex = klass->GetDexFile(); - *dex_data_len = static_cast<jint>(dex.Size()); - return CopyDataIntoJvmtiBuffer(env, dex.Begin(), *dex_data_len, /*out*/dex_data); -} - -// TODO Move this function somewhere more appropriate. -// Gets the data surrounding the given class. -// TODO Make this less magical. -jvmtiError Transformer::FillInTransformationData(ArtJvmTiEnv* env, - jclass klass, - ArtClassDefinition* def) { - JNIEnv* jni_env = GetJniEnv(env); - if (jni_env == nullptr) { - // TODO Different error might be better? - return ERR(INTERNAL); - } - art::ScopedObjectAccess soa(jni_env); - art::StackHandleScope<3> hs(art::Thread::Current()); - art::Handle<art::mirror::Class> hs_klass(hs.NewHandle(soa.Decode<art::mirror::Class>(klass))); - if (hs_klass.IsNull()) { - return ERR(INVALID_CLASS); - } - def->klass = klass; - def->loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader()); - std::string descriptor_store; - std::string descriptor(hs_klass->GetDescriptor(&descriptor_store)); - def->name = descriptor.substr(1, descriptor.size() - 2); - // TODO is this always null? - def->protection_domain = nullptr; - if (def->dex_data.get() == nullptr) { - unsigned char* new_data; - jvmtiError res = GetDexDataForRetransformation(env, hs_klass, &def->dex_len, &new_data); - if (res == OK) { - def->dex_data = MakeJvmtiUniquePtr(env, new_data); - } else { - return res; - } - } - return OK; -} - } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/transform.h b/runtime/openjdkjvmti/transform.h index c6a36e8e20..ba40e04b44 100644 --- a/runtime/openjdkjvmti/transform.h +++ b/runtime/openjdkjvmti/transform.h @@ -61,18 +61,6 @@ class Transformer { jint class_count, const jclass* classes, /*out*/std::string* error_msg); - - // Gets the data surrounding the given class. - static jvmtiError FillInTransformationData(ArtJvmTiEnv* env, - jclass klass, - ArtClassDefinition* def); - - private: - static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env, - art::Handle<art::mirror::Class> klass, - /*out*/jint* dex_data_length, - /*out*/unsigned char** dex_data) - REQUIRES_SHARED(art::Locks::mutator_lock_); }; } // namespace openjdkjvmti |