| /* Copyright (C) 2016 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 <string> |
| #include <type_traits> |
| #include <vector> |
| |
| #include <jni.h> |
| |
| #include "jvmti.h" |
| |
| #include "art_jvmti.h" |
| #include "base/logging.h" |
| #include "base/mutex.h" |
| #include "events-inl.h" |
| #include "jni_env_ext-inl.h" |
| #include "obj_ptr-inl.h" |
| #include "object_tagging.h" |
| #include "runtime.h" |
| #include "scoped_thread_state_change-inl.h" |
| #include "thread-current-inl.h" |
| #include "thread_list.h" |
| #include "ti_allocator.h" |
| #include "ti_breakpoint.h" |
| #include "ti_class.h" |
| #include "ti_dump.h" |
| #include "ti_field.h" |
| #include "ti_heap.h" |
| #include "ti_jni.h" |
| #include "ti_method.h" |
| #include "ti_monitor.h" |
| #include "ti_object.h" |
| #include "ti_phase.h" |
| #include "ti_properties.h" |
| #include "ti_redefine.h" |
| #include "ti_search.h" |
| #include "ti_stack.h" |
| #include "ti_thread.h" |
| #include "ti_threadgroup.h" |
| #include "ti_timers.h" |
| #include "transform.h" |
| |
| namespace openjdkjvmti { |
| |
| EventHandler gEventHandler; |
| |
| #define ENSURE_NON_NULL(n) \ |
| do { \ |
| if ((n) == nullptr) { \ |
| return ERR(NULL_POINTER); \ |
| } \ |
| } while (false) |
| |
| class JvmtiFunctions { |
| private: |
| 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 { \ |
| 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 { \ |
| if (ArtJvmTiEnv::AsArtJvmTiEnv(env)->capabilities.cap != 1) { \ |
| return ERR(MUST_POSSESS_CAPABILITY); \ |
| } \ |
| } while (false) |
| |
| public: |
| static jvmtiError Allocate(jvmtiEnv* env, jlong size, unsigned char** mem_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_NON_NULL(mem_ptr); |
| return AllocUtil::Allocate(env, size, mem_ptr); |
| } |
| |
| static jvmtiError Deallocate(jvmtiEnv* env, unsigned char* mem) { |
| ENSURE_VALID_ENV(env); |
| return AllocUtil::Deallocate(env, mem); |
| } |
| |
| 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) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_suspend); |
| return ThreadUtil::SuspendThread(env, thread); |
| } |
| |
| static jvmtiError SuspendThreadList(jvmtiEnv* env, |
| jint request_count, |
| const jthread* request_list, |
| jvmtiError* results) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_suspend); |
| return ThreadUtil::SuspendThreadList(env, request_count, request_list, results); |
| } |
| |
| static jvmtiError ResumeThread(jvmtiEnv* env, jthread thread) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_suspend); |
| return ThreadUtil::ResumeThread(env, thread); |
| } |
| |
| static jvmtiError ResumeThreadList(jvmtiEnv* env, |
| jint request_count, |
| const jthread* request_list, |
| jvmtiError* results) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_suspend); |
| return ThreadUtil::ResumeThreadList(env, request_count, request_list, results); |
| } |
| |
| 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 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) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_get_owned_monitor_info); |
| return StackUtil::GetOwnedMonitorInfo(env, |
| thread, |
| owned_monitor_count_ptr, |
| owned_monitors_ptr); |
| } |
| |
| static jvmtiError GetOwnedMonitorStackDepthInfo(jvmtiEnv* env, |
| jthread thread, |
| jint* monitor_info_count_ptr, |
| jvmtiMonitorStackDepthInfo** monitor_info_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_get_owned_monitor_stack_depth_info); |
| return StackUtil::GetOwnedMonitorStackDepthInfo(env, |
| thread, |
| monitor_info_count_ptr, |
| monitor_info_ptr); |
| } |
| |
| static jvmtiError GetCurrentContendedMonitor(jvmtiEnv* env, |
| 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); |
| } |
| |
| static jvmtiError RunAgentThread(jvmtiEnv* env, |
| jthread thread, |
| 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); |
| } |
| |
| static jvmtiError GetThreadGroupChildren(jvmtiEnv* env, |
| jthreadGroup group, |
| jint* thread_count_ptr, |
| jthread** threads_ptr, |
| jint* group_count_ptr, |
| jthreadGroup** groups_ptr) { |
| ENSURE_VALID_ENV(env); |
| return ThreadGroupUtil::GetThreadGroupChildren(env, |
| group, |
| thread_count_ptr, |
| threads_ptr, |
| group_count_ptr, |
| groups_ptr); |
| } |
| |
| static jvmtiError GetStackTrace(jvmtiEnv* env, |
| jthread thread, |
| jint start_depth, |
| jint max_frame_count, |
| jvmtiFrameInfo* frame_buffer, |
| jint* count_ptr) { |
| ENSURE_VALID_ENV(env); |
| return StackUtil::GetStackTrace(env, |
| thread, |
| start_depth, |
| max_frame_count, |
| frame_buffer, |
| count_ptr); |
| } |
| |
| static jvmtiError GetAllStackTraces(jvmtiEnv* env, |
| 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); |
| } |
| |
| static jvmtiError GetThreadListStackTraces(jvmtiEnv* env, |
| jint thread_count, |
| const jthread* thread_list, |
| jint max_frame_count, |
| jvmtiStackInfo** stack_info_ptr) { |
| ENSURE_VALID_ENV(env); |
| return StackUtil::GetThreadListStackTraces(env, |
| thread_count, |
| thread_list, |
| max_frame_count, |
| stack_info_ptr); |
| } |
| |
| 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 ATTRIBUTE_UNUSED) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_pop_frame); |
| return ERR(NOT_IMPLEMENTED); |
| } |
| |
| static jvmtiError GetFrameLocation(jvmtiEnv* env, |
| jthread thread, |
| 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 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 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 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 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 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 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 ATTRIBUTE_UNUSED) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_force_early_return); |
| return ERR(NOT_IMPLEMENTED); |
| } |
| |
| static jvmtiError FollowReferences(jvmtiEnv* env, |
| jint heap_filter, |
| jclass klass, |
| 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, |
| heap_filter, |
| klass, |
| initial_object, |
| callbacks, |
| user_data); |
| } |
| |
| static jvmtiError IterateThroughHeap(jvmtiEnv* env, |
| jint heap_filter, |
| 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); |
| if (jni_env == nullptr) { |
| return ERR(INTERNAL); |
| } |
| |
| art::ScopedObjectAccess soa(jni_env); |
| art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(object); |
| if (!ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table->GetTag(obj.Ptr(), tag_ptr)) { |
| *tag_ptr = 0; |
| } |
| |
| return ERR(NONE); |
| } |
| |
| static jvmtiError SetTag(jvmtiEnv* env, jobject object, jlong tag) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_tag_objects); |
| |
| if (object == nullptr) { |
| return ERR(NULL_POINTER); |
| } |
| |
| JNIEnv* jni_env = GetJniEnv(env); |
| if (jni_env == nullptr) { |
| return ERR(INTERNAL); |
| } |
| |
| art::ScopedObjectAccess soa(jni_env); |
| art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(object); |
| ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table->Set(obj.Ptr(), tag); |
| |
| return ERR(NONE); |
| } |
| |
| static jvmtiError GetObjectsWithTags(jvmtiEnv* env, |
| jint tag_count, |
| const jlong* tags, |
| 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); |
| if (jni_env == nullptr) { |
| return ERR(INTERNAL); |
| } |
| |
| art::ScopedObjectAccess soa(jni_env); |
| return ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table->GetTaggedObjects(env, |
| tag_count, |
| tags, |
| count_ptr, |
| object_result_ptr, |
| tag_result_ptr); |
| } |
| |
| static jvmtiError ForceGarbageCollection(jvmtiEnv* env) { |
| ENSURE_VALID_ENV(env); |
| return HeapUtil::ForceGarbageCollection(env); |
| } |
| |
| static jvmtiError IterateOverObjectsReachableFromObject( |
| jvmtiEnv* env, |
| 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 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 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 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) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr); |
| } |
| |
| static jvmtiError GetLocalInstance(jvmtiEnv* env, |
| jthread thread, |
| jint depth, |
| jobject* value_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::GetLocalInstance(env, thread, depth, value_ptr); |
| } |
| |
| static jvmtiError GetLocalInt(jvmtiEnv* env, |
| jthread thread, |
| jint depth, |
| jint slot, |
| jint* value_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr); |
| } |
| |
| static jvmtiError GetLocalLong(jvmtiEnv* env, |
| jthread thread, |
| jint depth, |
| jint slot, |
| jlong* value_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr); |
| } |
| |
| static jvmtiError GetLocalFloat(jvmtiEnv* env, |
| jthread thread, |
| jint depth, |
| jint slot, |
| jfloat* value_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr); |
| } |
| |
| static jvmtiError GetLocalDouble(jvmtiEnv* env, |
| jthread thread, |
| jint depth, |
| jint slot, |
| jdouble* value_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr); |
| } |
| |
| static jvmtiError SetLocalObject(jvmtiEnv* env, |
| jthread thread, |
| jint depth, |
| jint slot, |
| jobject value) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::SetLocalVariable(env, thread, depth, slot, value); |
| } |
| |
| static jvmtiError SetLocalInt(jvmtiEnv* env, |
| jthread thread, |
| jint depth, |
| jint slot, |
| jint value) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::SetLocalVariable(env, thread, depth, slot, value); |
| } |
| |
| static jvmtiError SetLocalLong(jvmtiEnv* env, |
| jthread thread, |
| jint depth, |
| jint slot, |
| jlong value) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::SetLocalVariable(env, thread, depth, slot, value); |
| } |
| |
| static jvmtiError SetLocalFloat(jvmtiEnv* env, |
| jthread thread, |
| jint depth, |
| jint slot, |
| jfloat value) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::SetLocalVariable(env, thread, depth, slot, value); |
| } |
| |
| static jvmtiError SetLocalDouble(jvmtiEnv* env, |
| jthread thread, |
| jint depth, |
| jint slot, |
| jdouble value) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::SetLocalVariable(env, thread, depth, slot, value); |
| } |
| |
| |
| static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_generate_breakpoint_events); |
| return BreakpointUtil::SetBreakpoint(env, method, location); |
| } |
| |
| static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_generate_breakpoint_events); |
| return BreakpointUtil::ClearBreakpoint(env, method, location); |
| } |
| |
| static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_generate_field_access_events); |
| return FieldUtil::SetFieldAccessWatch(env, klass, field); |
| } |
| |
| static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_generate_field_access_events); |
| return FieldUtil::ClearFieldAccessWatch(env, klass, field); |
| } |
| |
| static jvmtiError SetFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_generate_field_modification_events); |
| return FieldUtil::SetFieldModificationWatch(env, klass, field); |
| } |
| |
| static jvmtiError ClearFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_generate_field_modification_events); |
| return FieldUtil::ClearFieldModificationWatch(env, klass, field); |
| } |
| |
| 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); |
| } |
| |
| static jvmtiError GetClassLoaderClasses(jvmtiEnv* env, |
| 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); |
| } |
| |
| static jvmtiError GetClassSignature(jvmtiEnv* env, |
| 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) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_get_source_file_name); |
| return ClassUtil::GetSourceFileName(env, klass, source_name_ptr); |
| } |
| |
| static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr) { |
| ENSURE_VALID_ENV(env); |
| return ClassUtil::GetClassModifiers(env, klass, modifiers_ptr); |
| } |
| |
| static jvmtiError GetClassMethods(jvmtiEnv* env, |
| jclass klass, |
| jint* method_count_ptr, |
| jmethodID** methods_ptr) { |
| ENSURE_VALID_ENV(env); |
| return ClassUtil::GetClassMethods(env, klass, method_count_ptr, methods_ptr); |
| } |
| |
| static jvmtiError GetClassFields(jvmtiEnv* env, |
| jclass klass, |
| jint* field_count_ptr, |
| jfieldID** fields_ptr) { |
| ENSURE_VALID_ENV(env); |
| return ClassUtil::GetClassFields(env, klass, field_count_ptr, fields_ptr); |
| } |
| |
| static jvmtiError GetImplementedInterfaces(jvmtiEnv* env, |
| jclass klass, |
| jint* interface_count_ptr, |
| jclass** interfaces_ptr) { |
| ENSURE_VALID_ENV(env); |
| return ClassUtil::GetImplementedInterfaces(env, klass, interface_count_ptr, interfaces_ptr); |
| } |
| |
| static jvmtiError GetClassVersionNumbers(jvmtiEnv* env, |
| 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 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) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_get_source_debug_extension); |
| return ClassUtil::GetSourceDebugExtension(env, klass, source_debug_extension_ptr); |
| } |
| |
| 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), |
| &gEventHandler, |
| art::Runtime::Current(), |
| art::Thread::Current(), |
| class_count, |
| classes, |
| &error_msg); |
| if (res != OK) { |
| LOG(WARNING) << "FAILURE TO RETRANFORM " << error_msg; |
| } |
| return res; |
| } |
| |
| 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), |
| &gEventHandler, |
| art::Runtime::Current(), |
| art::Thread::Current(), |
| class_count, |
| class_definitions, |
| &error_msg); |
| if (res != OK) { |
| LOG(WARNING) << "FAILURE TO REDEFINE " << error_msg; |
| } |
| return res; |
| } |
| |
| 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 ATTRIBUTE_UNUSED, |
| jvmtiMonitorUsage* info_ptr ATTRIBUTE_UNUSED) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_get_monitor_info); |
| return ERR(NOT_IMPLEMENTED); |
| } |
| |
| static jvmtiError GetFieldName(jvmtiEnv* env, |
| jclass klass, |
| jfieldID field, |
| 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); |
| } |
| |
| static jvmtiError GetFieldDeclaringClass(jvmtiEnv* env, |
| jclass klass, |
| jfieldID field, |
| jclass* declaring_class_ptr) { |
| ENSURE_VALID_ENV(env); |
| return FieldUtil::GetFieldDeclaringClass(env, klass, field, declaring_class_ptr); |
| } |
| |
| static jvmtiError GetFieldModifiers(jvmtiEnv* env, |
| jclass klass, |
| jfieldID field, |
| jint* modifiers_ptr) { |
| ENSURE_VALID_ENV(env); |
| return FieldUtil::GetFieldModifiers(env, klass, field, modifiers_ptr); |
| } |
| |
| static jvmtiError IsFieldSynthetic(jvmtiEnv* env, |
| 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); |
| } |
| |
| static jvmtiError GetMethodName(jvmtiEnv* env, |
| jmethodID method, |
| 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); |
| } |
| |
| static jvmtiError GetLineNumberTable(jvmtiEnv* env, |
| 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); |
| } |
| |
| static jvmtiError GetMethodLocation(jvmtiEnv* env, |
| 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) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_access_local_variables); |
| return MethodUtil::GetLocalVariableTable(env, method, entry_count_ptr, table_ptr); |
| } |
| |
| static jvmtiError GetBytecodes(jvmtiEnv* env, |
| jmethodID method, |
| jint* bytecode_count_ptr, |
| unsigned char** bytecodes_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_HAS_CAP(env, can_get_bytecodes); |
| return MethodUtil::GetBytecodes(env, method, bytecode_count_ptr, bytecodes_ptr); |
| } |
| |
| 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 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 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); |
| } |
| |
| // TODO: This will require locking, so that an agent can't remove callbacks when we're dispatching |
| // an event. |
| static jvmtiError SetEventCallbacks(jvmtiEnv* env, |
| const jvmtiEventCallbacks* callbacks, |
| jint size_of_callbacks) { |
| ENSURE_VALID_ENV(env); |
| if (size_of_callbacks < 0) { |
| return ERR(ILLEGAL_ARGUMENT); |
| } |
| |
| if (callbacks == nullptr) { |
| ArtJvmTiEnv::AsArtJvmTiEnv(env)->event_callbacks.reset(); |
| return ERR(NONE); |
| } |
| |
| std::unique_ptr<jvmtiEventCallbacks> tmp(new jvmtiEventCallbacks()); |
| memset(tmp.get(), 0, sizeof(jvmtiEventCallbacks)); |
| size_t copy_size = std::min(sizeof(jvmtiEventCallbacks), |
| static_cast<size_t>(size_of_callbacks)); |
| copy_size = art::RoundDown(copy_size, sizeof(void*)); |
| memcpy(tmp.get(), callbacks, copy_size); |
| |
| ArtJvmTiEnv::AsArtJvmTiEnv(env)->event_callbacks = std::move(tmp); |
| |
| return ERR(NONE); |
| } |
| |
| static jvmtiError SetEventNotificationMode(jvmtiEnv* env, |
| jvmtiEventMode mode, |
| jvmtiEvent event_type, |
| jthread event_thread, |
| ...) { |
| ENSURE_VALID_ENV(env); |
| art::Thread* art_thread = nullptr; |
| if (event_thread != nullptr) { |
| // TODO: Need non-aborting call here, to return JVMTI_ERROR_INVALID_THREAD. |
| art::ScopedObjectAccess soa(art::Thread::Current()); |
| art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); |
| art_thread = art::Thread::FromManagedThread(soa, event_thread); |
| |
| if (art_thread == nullptr || // The thread hasn't been started or is already dead. |
| art_thread->IsStillStarting()) { |
| // TODO: We may want to let the EventHandler know, so it could clean up masks, potentially. |
| return ERR(THREAD_NOT_ALIVE); |
| } |
| } |
| |
| ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); |
| return gEventHandler.SetEvent(art_env, art_thread, GetArtJvmtiEvent(art_env, event_type), mode); |
| } |
| |
| 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); |
| ENSURE_NON_NULL(extension_count_ptr); |
| ENSURE_NON_NULL(extensions); |
| |
| std::vector<jvmtiExtensionFunctionInfo> ext_vector; |
| |
| // Holders for allocated values. |
| std::vector<JvmtiUniquePtr<char[]>> char_buffers; |
| std::vector<JvmtiUniquePtr<jvmtiParamInfo[]>> param_buffers; |
| std::vector<JvmtiUniquePtr<jvmtiError[]>> error_buffers; |
| |
| // Add a helper struct that takes an arbitrary const char*. add_extension will use Allocate |
| // appropriately. |
| struct CParamInfo { |
| const char* name; |
| jvmtiParamKind kind; |
| jvmtiParamTypes base_type; |
| jboolean null_ok; |
| }; |
| |
| auto add_extension = [&](jvmtiExtensionFunction func, |
| const char* id, |
| const char* short_description, |
| jint param_count, |
| const std::vector<CParamInfo>& params, |
| jint error_count, |
| const std::vector<jvmtiError>& errors) { |
| jvmtiExtensionFunctionInfo func_info; |
| jvmtiError error; |
| |
| func_info.func = func; |
| |
| JvmtiUniquePtr<char[]> id_ptr = CopyString(env, id, &error); |
| if (id_ptr == nullptr) { |
| return error; |
| } |
| func_info.id = id_ptr.get(); |
| char_buffers.push_back(std::move(id_ptr)); |
| |
| JvmtiUniquePtr<char[]> descr = CopyString(env, short_description, &error); |
| if (descr == nullptr) { |
| return error; |
| } |
| func_info.short_description = descr.get(); |
| char_buffers.push_back(std::move(descr)); |
| |
| func_info.param_count = param_count; |
| if (param_count > 0) { |
| JvmtiUniquePtr<jvmtiParamInfo[]> params_ptr = |
| AllocJvmtiUniquePtr<jvmtiParamInfo[]>(env, param_count, &error); |
| if (params_ptr == nullptr) { |
| return error; |
| } |
| func_info.params = params_ptr.get(); |
| param_buffers.push_back(std::move(params_ptr)); |
| |
| for (jint i = 0; i != param_count; ++i) { |
| JvmtiUniquePtr<char[]> param_name = CopyString(env, params[i].name, &error); |
| if (param_name == nullptr) { |
| return error; |
| } |
| func_info.params[i].name = param_name.get(); |
| char_buffers.push_back(std::move(param_name)); |
| |
| func_info.params[i].kind = params[i].kind; |
| func_info.params[i].base_type = params[i].base_type; |
| func_info.params[i].null_ok = params[i].null_ok; |
| } |
| } else { |
| func_info.params = nullptr; |
| } |
| |
| func_info.error_count = error_count; |
| if (error_count > 0) { |
| JvmtiUniquePtr<jvmtiError[]> errors_ptr = |
| AllocJvmtiUniquePtr<jvmtiError[]>(env, error_count, &error); |
| if (errors_ptr == nullptr) { |
| return error; |
| } |
| func_info.errors = errors_ptr.get(); |
| error_buffers.push_back(std::move(errors_ptr)); |
| |
| for (jint i = 0; i != error_count; ++i) { |
| func_info.errors[i] = errors[i]; |
| } |
| } else { |
| func_info.errors = nullptr; |
| } |
| |
| ext_vector.push_back(func_info); |
| |
| return ERR(NONE); |
| }; |
| |
| jvmtiError error; |
| |
| // Heap extensions. |
| error = add_extension( |
| reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetObjectHeapId), |
| "com.android.art.heap.get_object_heap_id", |
| "Retrieve the heap id of the the object tagged with the given argument. An " |
| "arbitrary object is chosen if multiple objects exist with the same tag.", |
| 2, |
| { // NOLINT [whitespace/braces] [4] |
| { "tag", JVMTI_KIND_IN, JVMTI_TYPE_JLONG, false}, |
| { "heap_id", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false} |
| }, |
| 1, |
| { JVMTI_ERROR_NOT_FOUND }); |
| if (error != ERR(NONE)) { |
| return error; |
| } |
| |
| error = add_extension( |
| reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetHeapName), |
| "com.android.art.heap.get_heap_name", |
| "Retrieve the name of the heap with the given id.", |
| 2, |
| { // NOLINT [whitespace/braces] [4] |
| { "heap_id", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, |
| { "heap_name", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false} |
| }, |
| 1, |
| { JVMTI_ERROR_ILLEGAL_ARGUMENT }); |
| if (error != ERR(NONE)) { |
| return error; |
| } |
| |
| error = add_extension( |
| reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::IterateThroughHeapExt), |
| "com.android.art.heap.iterate_through_heap_ext", |
| "Iterate through a heap. This is equivalent to the standard IterateThroughHeap function," |
| " except for additionally passing the heap id of the current object. The jvmtiHeapCallbacks" |
| " structure is reused, with the callbacks field overloaded to a signature of " |
| "jint (*)(jlong, jlong, jlong*, jint length, void*, jint).", |
| 4, |
| { // NOLINT [whitespace/braces] [4] |
| { "heap_filter", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, |
| { "klass", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true}, |
| { "callbacks", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false}, |
| { "user_data", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, true} |
| }, |
| 3, |
| { // NOLINT [whitespace/braces] [4] |
| JVMTI_ERROR_MUST_POSSESS_CAPABILITY, |
| JVMTI_ERROR_INVALID_CLASS, |
| JVMTI_ERROR_NULL_POINTER |
| }); |
| if (error != ERR(NONE)) { |
| return error; |
| } |
| |
| error = add_extension( |
| reinterpret_cast<jvmtiExtensionFunction>(AllocUtil::GetGlobalJvmtiAllocationState), |
| "com.android.art.alloc.get_global_jvmti_allocation_state", |
| "Returns the total amount of memory currently allocated by all jvmtiEnvs through the" |
| " 'Allocate' jvmti function. This does not include any memory that has been deallocated" |
| " through the 'Deallocate' function. This number is approximate and might not correspond" |
| " exactly to the sum of the sizes of all not freed allocations.", |
| 1, |
| { // NOLINT [whitespace/braces] [4] |
| { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false}, |
| }, |
| 1, |
| { ERR(NULL_POINTER) }); |
| if (error != ERR(NONE)) { |
| return error; |
| } |
| |
| // Copy into output buffer. |
| |
| *extension_count_ptr = ext_vector.size(); |
| JvmtiUniquePtr<jvmtiExtensionFunctionInfo[]> out_data = |
| AllocJvmtiUniquePtr<jvmtiExtensionFunctionInfo[]>(env, ext_vector.size(), &error); |
| if (out_data == nullptr) { |
| return error; |
| } |
| memcpy(out_data.get(), |
| ext_vector.data(), |
| ext_vector.size() * sizeof(jvmtiExtensionFunctionInfo)); |
| *extensions = out_data.release(); |
| |
| // Release all the buffer holders, we're OK now. |
| for (auto& holder : char_buffers) { |
| holder.release(); |
| } |
| for (auto& holder : param_buffers) { |
| holder.release(); |
| } |
| for (auto& holder : error_buffers) { |
| holder.release(); |
| } |
| |
| return ERR(NONE); |
| } |
| |
| 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; |
| |
| return ERR(NONE); |
| } |
| |
| static jvmtiError SetExtensionEventCallback(jvmtiEnv* env, |
| 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); |
| } |
| |
| static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_NON_NULL(capabilities_ptr); |
| *capabilities_ptr = kPotentialCapabilities; |
| return OK; |
| } |
| |
| static jvmtiError AddCapabilities(jvmtiEnv* env, const jvmtiCapabilities* capabilities_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_NON_NULL(capabilities_ptr); |
| ArtJvmTiEnv* art_env = static_cast<ArtJvmTiEnv*>(env); |
| jvmtiError ret = OK; |
| 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 (potential_capabilities.e == 1) { \ |
| if (art_env->capabilities.e != 1) { \ |
| art_env->capabilities.e = 1; \ |
| changed.e = 1; \ |
| }\ |
| } else { \ |
| ret = ERR(NOT_AVAILABLE); \ |
| } \ |
| } \ |
| } while (false) |
| |
| ADD_CAPABILITY(can_tag_objects); |
| ADD_CAPABILITY(can_generate_field_modification_events); |
| ADD_CAPABILITY(can_generate_field_access_events); |
| ADD_CAPABILITY(can_get_bytecodes); |
| ADD_CAPABILITY(can_get_synthetic_attribute); |
| ADD_CAPABILITY(can_get_owned_monitor_info); |
| ADD_CAPABILITY(can_get_current_contended_monitor); |
| ADD_CAPABILITY(can_get_monitor_info); |
| ADD_CAPABILITY(can_pop_frame); |
| ADD_CAPABILITY(can_redefine_classes); |
| ADD_CAPABILITY(can_signal_thread); |
| ADD_CAPABILITY(can_get_source_file_name); |
| ADD_CAPABILITY(can_get_line_numbers); |
| ADD_CAPABILITY(can_get_source_debug_extension); |
| ADD_CAPABILITY(can_access_local_variables); |
| ADD_CAPABILITY(can_maintain_original_method_order); |
| ADD_CAPABILITY(can_generate_single_step_events); |
| ADD_CAPABILITY(can_generate_exception_events); |
| ADD_CAPABILITY(can_generate_frame_pop_events); |
| ADD_CAPABILITY(can_generate_breakpoint_events); |
| ADD_CAPABILITY(can_suspend); |
| ADD_CAPABILITY(can_redefine_any_class); |
| ADD_CAPABILITY(can_get_current_thread_cpu_time); |
| ADD_CAPABILITY(can_get_thread_cpu_time); |
| ADD_CAPABILITY(can_generate_method_entry_events); |
| ADD_CAPABILITY(can_generate_method_exit_events); |
| ADD_CAPABILITY(can_generate_all_class_hook_events); |
| ADD_CAPABILITY(can_generate_compiled_method_load_events); |
| ADD_CAPABILITY(can_generate_monitor_events); |
| ADD_CAPABILITY(can_generate_vm_object_alloc_events); |
| ADD_CAPABILITY(can_generate_native_method_bind_events); |
| ADD_CAPABILITY(can_generate_garbage_collection_events); |
| ADD_CAPABILITY(can_generate_object_free_events); |
| ADD_CAPABILITY(can_force_early_return); |
| ADD_CAPABILITY(can_get_owned_monitor_stack_depth_info); |
| ADD_CAPABILITY(can_get_constant_pool); |
| ADD_CAPABILITY(can_set_native_method_prefix); |
| ADD_CAPABILITY(can_retransform_classes); |
| ADD_CAPABILITY(can_retransform_any_class); |
| ADD_CAPABILITY(can_generate_resource_exhaustion_heap_events); |
| ADD_CAPABILITY(can_generate_resource_exhaustion_threads_events); |
| #undef ADD_CAPABILITY |
| gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env), |
| changed, |
| /*added*/true); |
| return ret; |
| } |
| |
| static jvmtiError RelinquishCapabilities(jvmtiEnv* env, |
| const jvmtiCapabilities* capabilities_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_NON_NULL(capabilities_ptr); |
| ArtJvmTiEnv* art_env = reinterpret_cast<ArtJvmTiEnv*>(env); |
| jvmtiCapabilities changed = {}; |
| #define DEL_CAPABILITY(e) \ |
| do { \ |
| if (capabilities_ptr->e == 1) { \ |
| if (art_env->capabilities.e == 1) { \ |
| art_env->capabilities.e = 0;\ |
| changed.e = 1; \ |
| } \ |
| } \ |
| } while (false) |
| |
| DEL_CAPABILITY(can_tag_objects); |
| DEL_CAPABILITY(can_generate_field_modification_events); |
| DEL_CAPABILITY(can_generate_field_access_events); |
| DEL_CAPABILITY(can_get_bytecodes); |
| DEL_CAPABILITY(can_get_synthetic_attribute); |
| DEL_CAPABILITY(can_get_owned_monitor_info); |
| DEL_CAPABILITY(can_get_current_contended_monitor); |
| DEL_CAPABILITY(can_get_monitor_info); |
| DEL_CAPABILITY(can_pop_frame); |
| DEL_CAPABILITY(can_redefine_classes); |
| DEL_CAPABILITY(can_signal_thread); |
| DEL_CAPABILITY(can_get_source_file_name); |
| DEL_CAPABILITY(can_get_line_numbers); |
| DEL_CAPABILITY(can_get_source_debug_extension); |
| DEL_CAPABILITY(can_access_local_variables); |
| DEL_CAPABILITY(can_maintain_original_method_order); |
| DEL_CAPABILITY(can_generate_single_step_events); |
| DEL_CAPABILITY(can_generate_exception_events); |
| DEL_CAPABILITY(can_generate_frame_pop_events); |
| DEL_CAPABILITY(can_generate_breakpoint_events); |
| DEL_CAPABILITY(can_suspend); |
| DEL_CAPABILITY(can_redefine_any_class); |
| DEL_CAPABILITY(can_get_current_thread_cpu_time); |
| DEL_CAPABILITY(can_get_thread_cpu_time); |
| DEL_CAPABILITY(can_generate_method_entry_events); |
| DEL_CAPABILITY(can_generate_method_exit_events); |
| DEL_CAPABILITY(can_generate_all_class_hook_events); |
| DEL_CAPABILITY(can_generate_compiled_method_load_events); |
| DEL_CAPABILITY(can_generate_monitor_events); |
| DEL_CAPABILITY(can_generate_vm_object_alloc_events); |
| DEL_CAPABILITY(can_generate_native_method_bind_events); |
| DEL_CAPABILITY(can_generate_garbage_collection_events); |
| DEL_CAPABILITY(can_generate_object_free_events); |
| DEL_CAPABILITY(can_force_early_return); |
| DEL_CAPABILITY(can_get_owned_monitor_stack_depth_info); |
| DEL_CAPABILITY(can_get_constant_pool); |
| DEL_CAPABILITY(can_set_native_method_prefix); |
| DEL_CAPABILITY(can_retransform_classes); |
| DEL_CAPABILITY(can_retransform_any_class); |
| DEL_CAPABILITY(can_generate_resource_exhaustion_heap_events); |
| DEL_CAPABILITY(can_generate_resource_exhaustion_threads_events); |
| #undef DEL_CAPABILITY |
| gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env), |
| changed, |
| /*added*/false); |
| return OK; |
| } |
| |
| static jvmtiError GetCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) { |
| ENSURE_VALID_ENV(env); |
| ENSURE_NON_NULL(capabilities_ptr); |
| ArtJvmTiEnv* artenv = reinterpret_cast<ArtJvmTiEnv*>(env); |
| *capabilities_ptr = artenv->capabilities; |
| return OK; |
| } |
| |
| 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 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 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 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); |
| } |
| |
| static jvmtiError DisposeEnvironment(jvmtiEnv* env) { |
| ENSURE_VALID_ENV(env); |
| ArtJvmTiEnv* tienv = ArtJvmTiEnv::AsArtJvmTiEnv(env); |
| gEventHandler.RemoveArtJvmTiEnv(tienv); |
| art::Runtime::Current()->RemoveSystemWeakHolder(tienv->object_tag_table.get()); |
| ThreadUtil::RemoveEnvironment(tienv); |
| delete tienv; |
| return OK; |
| } |
| |
| static jvmtiError SetEnvironmentLocalStorage(jvmtiEnv* env, const void* data) { |
| ENSURE_VALID_ENV(env); |
| reinterpret_cast<ArtJvmTiEnv*>(env)->local_data = const_cast<void*>(data); |
| return OK; |
| } |
| |
| static jvmtiError GetEnvironmentLocalStorage(jvmtiEnv* env, void** data_ptr) { |
| ENSURE_VALID_ENV(env); |
| *data_ptr = reinterpret_cast<ArtJvmTiEnv*>(env)->local_data; |
| return OK; |
| } |
| |
| static jvmtiError GetVersionNumber(jvmtiEnv* env, jint* version_ptr) { |
| ENSURE_VALID_ENV(env); |
| *version_ptr = JVMTI_VERSION; |
| return OK; |
| } |
| |
| static jvmtiError GetErrorName(jvmtiEnv* env, jvmtiError error, char** name_ptr) { |
| ENSURE_NON_NULL(name_ptr); |
| auto copy_fn = [&](const char* name_cstr) { |
| jvmtiError res; |
| JvmtiUniquePtr<char[]> copy = CopyString(env, name_cstr, &res); |
| if (copy == nullptr) { |
| *name_ptr = nullptr; |
| return res; |
| } else { |
| *name_ptr = copy.release(); |
| return OK; |
| } |
| }; |
| switch (error) { |
| #define ERROR_CASE(e) case (JVMTI_ERROR_ ## e) : \ |
| return copy_fn("JVMTI_ERROR_"#e); |
| ERROR_CASE(NONE); |
| ERROR_CASE(INVALID_THREAD); |
| ERROR_CASE(INVALID_THREAD_GROUP); |
| ERROR_CASE(INVALID_PRIORITY); |
| ERROR_CASE(THREAD_NOT_SUSPENDED); |
| ERROR_CASE(THREAD_SUSPENDED); |
| ERROR_CASE(THREAD_NOT_ALIVE); |
| ERROR_CASE(INVALID_OBJECT); |
| ERROR_CASE(INVALID_CLASS); |
| ERROR_CASE(CLASS_NOT_PREPARED); |
| ERROR_CASE(INVALID_METHODID); |
| ERROR_CASE(INVALID_LOCATION); |
| ERROR_CASE(INVALID_FIELDID); |
| ERROR_CASE(NO_MORE_FRAMES); |
| ERROR_CASE(OPAQUE_FRAME); |
| ERROR_CASE(TYPE_MISMATCH); |
| ERROR_CASE(INVALID_SLOT); |
| ERROR_CASE(DUPLICATE); |
| ERROR_CASE(NOT_FOUND); |
| ERROR_CASE(INVALID_MONITOR); |
| ERROR_CASE(NOT_MONITOR_OWNER); |
| ERROR_CASE(INTERRUPT); |
| ERROR_CASE(INVALID_CLASS_FORMAT); |
| ERROR_CASE(CIRCULAR_CLASS_DEFINITION); |
| ERROR_CASE(FAILS_VERIFICATION); |
| ERROR_CASE(UNSUPPORTED_REDEFINITION_METHOD_ADDED); |
| ERROR_CASE(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED); |
| ERROR_CASE(INVALID_TYPESTATE); |
| ERROR_CASE(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED); |
| ERROR_CASE(UNSUPPORTED_REDEFINITION_METHOD_DELETED); |
| ERROR_CASE(UNSUPPORTED_VERSION); |
| ERROR_CASE(NAMES_DONT_MATCH); |
| ERROR_CASE(UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED); |
| ERROR_CASE(UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED); |
| ERROR_CASE(UNMODIFIABLE_CLASS); |
| ERROR_CASE(NOT_AVAILABLE); |
| ERROR_CASE(MUST_POSSESS_CAPABILITY); |
| ERROR_CASE(NULL_POINTER); |
| ERROR_CASE(ABSENT_INFORMATION); |
| ERROR_CASE(INVALID_EVENT_TYPE); |
| ERROR_CASE(ILLEGAL_ARGUMENT); |
| ERROR_CASE(NATIVE_METHOD); |
| ERROR_CASE(CLASS_LOADER_UNSUPPORTED); |
| ERROR_CASE(OUT_OF_MEMORY); |
| ERROR_CASE(ACCESS_DENIED); |
| ERROR_CASE(WRONG_PHASE); |
| ERROR_CASE(INTERNAL); |
| ERROR_CASE(UNATTACHED_THREAD); |
| ERROR_CASE(INVALID_ENVIRONMENT); |
| #undef ERROR_CASE |
| } |
| |
| return ERR(ILLEGAL_ARGUMENT); |
| } |
| |
| 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; |
| |
| art::gLogVerbosity.collector = val; |
| art::gLogVerbosity.compiler = val; |
| art::gLogVerbosity.deopt = val; |
| art::gLogVerbosity.heap = val; |
| art::gLogVerbosity.jdwp = val; |
| art::gLogVerbosity.jit = val; |
| art::gLogVerbosity.monitor = val; |
| art::gLogVerbosity.oat = val; |
| art::gLogVerbosity.profiler = val; |
| art::gLogVerbosity.signals = val; |
| art::gLogVerbosity.simulator = val; |
| art::gLogVerbosity.startup = val; |
| art::gLogVerbosity.third_party_jni = val; |
| art::gLogVerbosity.threads = val; |
| art::gLogVerbosity.verifier = val; |
| art::gLogVerbosity.image = val; |
| |
| // Note: can't switch systrace_lock_logging. That requires changing entrypoints. |
| |
| art::gLogVerbosity.agents = val; |
| } else { |
| // Spec isn't clear whether "flag" is a mask or supposed to be single. We implement the mask |
| // semantics. |
| constexpr std::underlying_type<jvmtiVerboseFlag>::type kMask = |
| jvmtiVerboseFlag::JVMTI_VERBOSE_GC | |
| jvmtiVerboseFlag::JVMTI_VERBOSE_CLASS | |
| jvmtiVerboseFlag::JVMTI_VERBOSE_JNI; |
| if ((flag & ~kMask) != 0) { |
| return ERR(ILLEGAL_ARGUMENT); |
| } |
| |
| bool val = (value == JNI_TRUE) ? true : false; |
| |
| if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_GC) != 0) { |
| art::gLogVerbosity.gc = val; |
| } |
| |
| if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_CLASS) != 0) { |
| art::gLogVerbosity.class_linker = val; |
| } |
| |
| if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_JNI) != 0) { |
| art::gLogVerbosity.jni = val; |
| } |
| } |
| |
| return ERR(NONE); |
| } |
| |
| 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); |
| } |
| *format_ptr = jvmtiJlocationFormat::JVMTI_JLOCATION_JVMBCI; |
| return ERR(NONE); |
| } |
| }; |
| |
| static bool IsJvmtiVersion(jint version) { |
| return version == JVMTI_VERSION_1 || |
| version == JVMTI_VERSION_1_0 || |
| version == JVMTI_VERSION_1_1 || |
| version == JVMTI_VERSION_1_2 || |
| version == JVMTI_VERSION; |
| } |
| |
| extern const jvmtiInterface_1 gJvmtiInterface; |
| |
| ArtJvmTiEnv::ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler) |
| : art_vm(runtime), |
| local_data(nullptr), |
| capabilities() { |
| object_tag_table = std::unique_ptr<ObjectTagTable>(new ObjectTagTable(event_handler, this)); |
| functions = &gJvmtiInterface; |
| } |
| |
| // Creates a jvmtiEnv and returns it with the art::ti::Env that is associated with it. new_art_ti |
| // is a pointer to the uninitialized memory for an art::ti::Env. |
| static void CreateArtJvmTiEnv(art::JavaVMExt* vm, /*out*/void** new_jvmtiEnv) { |
| struct ArtJvmTiEnv* env = new ArtJvmTiEnv(vm, &gEventHandler); |
| *new_jvmtiEnv = env; |
| |
| gEventHandler.RegisterArtJvmTiEnv(env); |
| |
| art::Runtime::Current()->AddSystemWeakHolder( |
| ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get()); |
| } |
| |
| // A hook that the runtime uses to allow plugins to handle GetEnv calls. It returns true and |
| // places the return value in 'env' if this library can handle the GetEnv request. Otherwise |
| // returns false and does not modify the 'env' pointer. |
| static jint GetEnvHandler(art::JavaVMExt* vm, /*out*/void** env, jint version) { |
| if (IsJvmtiVersion(version)) { |
| CreateArtJvmTiEnv(vm, env); |
| return JNI_OK; |
| } else { |
| printf("version 0x%x is not valid!", version); |
| return JNI_EVERSION; |
| } |
| } |
| |
| // The plugin initialization function. This adds the jvmti environment. |
| extern "C" bool ArtPlugin_Initialize() { |
| art::Runtime* runtime = art::Runtime::Current(); |
| |
| if (runtime->IsStarted()) { |
| PhaseUtil::SetToLive(); |
| } else { |
| PhaseUtil::SetToOnLoad(); |
| } |
| PhaseUtil::Register(&gEventHandler); |
| ThreadUtil::Register(&gEventHandler); |
| ClassUtil::Register(&gEventHandler); |
| DumpUtil::Register(&gEventHandler); |
| MethodUtil::Register(&gEventHandler); |
| SearchUtil::Register(); |
| HeapUtil::Register(); |
| |
| runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); |
| |
| return true; |
| } |
| |
| extern "C" bool ArtPlugin_Deinitialize() { |
| gEventHandler.Shutdown(); |
| PhaseUtil::Unregister(); |
| ThreadUtil::Unregister(); |
| ClassUtil::Unregister(); |
| DumpUtil::Unregister(); |
| MethodUtil::Unregister(); |
| SearchUtil::Unregister(); |
| HeapUtil::Unregister(); |
| |
| return true; |
| } |
| |
| // The actual struct holding all of the entrypoints into the jvmti interface. |
| const jvmtiInterface_1 gJvmtiInterface = { |
| nullptr, // reserved1 |
| JvmtiFunctions::SetEventNotificationMode, |
| nullptr, // reserved3 |
| JvmtiFunctions::GetAllThreads, |
| JvmtiFunctions::SuspendThread, |
| JvmtiFunctions::ResumeThread, |
| JvmtiFunctions::StopThread, |
| JvmtiFunctions::InterruptThread, |
| JvmtiFunctions::GetThreadInfo, |
| JvmtiFunctions::GetOwnedMonitorInfo, // 10 |
| JvmtiFunctions::GetCurrentContendedMonitor, |
| JvmtiFunctions::RunAgentThread, |
| JvmtiFunctions::GetTopThreadGroups, |
| JvmtiFunctions::GetThreadGroupInfo, |
| JvmtiFunctions::GetThreadGroupChildren, |
| JvmtiFunctions::GetFrameCount, |
| JvmtiFunctions::GetThreadState, |
| JvmtiFunctions::GetCurrentThread, |
| JvmtiFunctions::GetFrameLocation, |
| JvmtiFunctions::NotifyFramePop, // 20 |
| JvmtiFunctions::GetLocalObject, |
| JvmtiFunctions::GetLocalInt, |
| JvmtiFunctions::GetLocalLong, |
| JvmtiFunctions::GetLocalFloat, |
| JvmtiFunctions::GetLocalDouble, |
| JvmtiFunctions::SetLocalObject, |
| JvmtiFunctions::SetLocalInt, |
| JvmtiFunctions::SetLocalLong, |
| JvmtiFunctions::SetLocalFloat, |
| JvmtiFunctions::SetLocalDouble, // 30 |
| JvmtiFunctions::CreateRawMonitor, |
| JvmtiFunctions::DestroyRawMonitor, |
| JvmtiFunctions::RawMonitorEnter, |
| JvmtiFunctions::RawMonitorExit, |
| JvmtiFunctions::RawMonitorWait, |
| JvmtiFunctions::RawMonitorNotify, |
| JvmtiFunctions::RawMonitorNotifyAll, |
| JvmtiFunctions::SetBreakpoint, |
| JvmtiFunctions::ClearBreakpoint, |
| nullptr, // reserved40 |
| JvmtiFunctions::SetFieldAccessWatch, |
| JvmtiFunctions::ClearFieldAccessWatch, |
| JvmtiFunctions::SetFieldModificationWatch, |
| JvmtiFunctions::ClearFieldModificationWatch, |
| JvmtiFunctions::IsModifiableClass, |
| JvmtiFunctions::Allocate, |
| JvmtiFunctions::Deallocate, |
| JvmtiFunctions::GetClassSignature, |
| JvmtiFunctions::GetClassStatus, |
| JvmtiFunctions::GetSourceFileName, // 50 |
| JvmtiFunctions::GetClassModifiers, |
| JvmtiFunctions::GetClassMethods, |
| JvmtiFunctions::GetClassFields, |
| JvmtiFunctions::GetImplementedInterfaces, |
| JvmtiFunctions::IsInterface, |
| JvmtiFunctions::IsArrayClass, |
| JvmtiFunctions::GetClassLoader, |
| JvmtiFunctions::GetObjectHashCode, |
| JvmtiFunctions::GetObjectMonitorUsage, |
| JvmtiFunctions::GetFieldName, // 60 |
| JvmtiFunctions::GetFieldDeclaringClass, |
| JvmtiFunctions::GetFieldModifiers, |
| JvmtiFunctions::IsFieldSynthetic, |
| JvmtiFunctions::GetMethodName, |
| JvmtiFunctions::GetMethodDeclaringClass, |
| JvmtiFunctions::GetMethodModifiers, |
| nullptr, // reserved67 |
| JvmtiFunctions::GetMaxLocals, |
| JvmtiFunctions::GetArgumentsSize, |
| JvmtiFunctions::GetLineNumberTable, // 70 |
| JvmtiFunctions::GetMethodLocation, |
| JvmtiFunctions::GetLocalVariableTable, |
| JvmtiFunctions::SetNativeMethodPrefix, |
| JvmtiFunctions::SetNativeMethodPrefixes, |
| JvmtiFunctions::GetBytecodes, |
| JvmtiFunctions::IsMethodNative, |
| JvmtiFunctions::IsMethodSynthetic, |
| JvmtiFunctions::GetLoadedClasses, |
| JvmtiFunctions::GetClassLoaderClasses, |
| JvmtiFunctions::PopFrame, // 80 |
| JvmtiFunctions::ForceEarlyReturnObject, |
| JvmtiFunctions::ForceEarlyReturnInt, |
| JvmtiFunctions::ForceEarlyReturnLong, |
| JvmtiFunctions::ForceEarlyReturnFloat, |
| JvmtiFunctions::ForceEarlyReturnDouble, |
| JvmtiFunctions::ForceEarlyReturnVoid, |
| JvmtiFunctions::RedefineClasses, |
| JvmtiFunctions::GetVersionNumber, |
| JvmtiFunctions::GetCapabilities, |
| JvmtiFunctions::GetSourceDebugExtension, // 90 |
| JvmtiFunctions::IsMethodObsolete, |
| JvmtiFunctions::SuspendThreadList, |
| JvmtiFunctions::ResumeThreadList, |
| nullptr, // reserved94 |
| nullptr, // reserved95 |
| nullptr, // reserved96 |
| nullptr, // reserved97 |
| nullptr, // reserved98 |
| nullptr, // reserved99 |
| JvmtiFunctions::GetAllStackTraces, // 100 |
| JvmtiFunctions::GetThreadListStackTraces, |
| JvmtiFunctions::GetThreadLocalStorage, |
| JvmtiFunctions::SetThreadLocalStorage, |
| JvmtiFunctions::GetStackTrace, |
| nullptr, // reserved105 |
| JvmtiFunctions::GetTag, |
| JvmtiFunctions::SetTag, |
| JvmtiFunctions::ForceGarbageCollection, |
| JvmtiFunctions::IterateOverObjectsReachableFromObject, |
| JvmtiFunctions::IterateOverReachableObjects, // 110 |
| JvmtiFunctions::IterateOverHeap, |
| JvmtiFunctions::IterateOverInstancesOfClass, |
| nullptr, // reserved113 |
| JvmtiFunctions::GetObjectsWithTags, |
| JvmtiFunctions::FollowReferences, |
| JvmtiFunctions::IterateThroughHeap, |
| nullptr, // reserved117 |
| nullptr, // reserved118 |
| nullptr, // reserved119 |
| JvmtiFunctions::SetJNIFunctionTable, // 120 |
| JvmtiFunctions::GetJNIFunctionTable, |
| JvmtiFunctions::SetEventCallbacks, |
| JvmtiFunctions::GenerateEvents, |
| JvmtiFunctions::GetExtensionFunctions, |
| JvmtiFunctions::GetExtensionEvents, |
| JvmtiFunctions::SetExtensionEventCallback, |
| JvmtiFunctions::DisposeEnvironment, |
| JvmtiFunctions::GetErrorName, |
| JvmtiFunctions::GetJLocationFormat, |
| JvmtiFunctions::GetSystemProperties, // 130 |
| JvmtiFunctions::GetSystemProperty, |
| JvmtiFunctions::SetSystemProperty, |
| JvmtiFunctions::GetPhase, |
| JvmtiFunctions::GetCurrentThreadCpuTimerInfo, |
| JvmtiFunctions::GetCurrentThreadCpuTime, |
| JvmtiFunctions::GetThreadCpuTimerInfo, |
| JvmtiFunctions::GetThreadCpuTime, |
| JvmtiFunctions::GetTimerInfo, |
| JvmtiFunctions::GetTime, |
| JvmtiFunctions::GetPotentialCapabilities, // 140 |
| nullptr, // reserved141 |
| JvmtiFunctions::AddCapabilities, |
| JvmtiFunctions::RelinquishCapabilities, |
| JvmtiFunctions::GetAvailableProcessors, |
| JvmtiFunctions::GetClassVersionNumbers, |
| JvmtiFunctions::GetConstantPool, |
| JvmtiFunctions::GetEnvironmentLocalStorage, |
| JvmtiFunctions::SetEnvironmentLocalStorage, |
| JvmtiFunctions::AddToBootstrapClassLoaderSearch, |
| JvmtiFunctions::SetVerboseFlag, // 150 |
| JvmtiFunctions::AddToSystemClassLoaderSearch, |
| JvmtiFunctions::RetransformClasses, |
| JvmtiFunctions::GetOwnedMonitorStackDepthInfo, |
| JvmtiFunctions::GetObjectSize, |
| JvmtiFunctions::GetLocalInstance, |
| }; |
| |
| }; // namespace openjdkjvmti |