|  | /* 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 <memory> | 
|  | #include <string> | 
|  | #include <type_traits> | 
|  | #include <vector> | 
|  |  | 
|  | #include <android-base/logging.h> | 
|  |  | 
|  | #include <jni.h> | 
|  |  | 
|  | #include "jvmti.h" | 
|  |  | 
|  | #include "alloc_manager.h" | 
|  | #include "art_jvmti.h" | 
|  | #include "base/logging.h"  // For gLogVerbosity. | 
|  | #include "base/mutex.h" | 
|  | #include "events-inl.h" | 
|  | #include "jni/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_extension.h" | 
|  | #include "ti_field.h" | 
|  | #include "ti_heap.h" | 
|  | #include "ti_jni.h" | 
|  | #include "ti_logging.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 { | 
|  |  | 
|  | // NB These are heap allocated to avoid the static destructors being run if an agent calls exit(3). | 
|  | // These should never be null. | 
|  | EventHandler* gEventHandler; | 
|  | DeoptManager* gDeoptManager; | 
|  | AllocationManager* gAllocManager; | 
|  |  | 
|  | #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) { | 
|  | jvmtiError err = getEnvironmentError(env); | 
|  | // Allow UNATTACHED_THREAD since we don't really care about that for this function. | 
|  | if (err != OK && err != ERR(UNATTACHED_THREAD)) { | 
|  | return err; | 
|  | } | 
|  | ENSURE_NON_NULL(mem_ptr); | 
|  | return AllocUtil::Allocate(env, size, mem_ptr); | 
|  | } | 
|  |  | 
|  | static jvmtiError Deallocate(jvmtiEnv* env, unsigned char* mem) { | 
|  | jvmtiError err = getEnvironmentError(env); | 
|  | // Allow UNATTACHED_THREAD since we don't really care about that for this function. | 
|  | if (err != OK && err != ERR(UNATTACHED_THREAD)) { | 
|  | return err; | 
|  | } | 
|  | 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, jobject exception) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_signal_thread); | 
|  | return ThreadUtil::StopThread(env, thread, exception); | 
|  | } | 
|  |  | 
|  | static jvmtiError InterruptThread(jvmtiEnv* env, jthread thread) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_signal_thread); | 
|  | return ThreadUtil::InterruptThread(env, thread); | 
|  | } | 
|  |  | 
|  | 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, | 
|  | jobject* monitor_ptr) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_get_current_contended_monitor); | 
|  | return MonitorUtil::GetCurrentContendedMonitor(env, thread, monitor_ptr); | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_pop_frame); | 
|  | return StackUtil::PopFrame(env, thread); | 
|  | } | 
|  |  | 
|  | 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, jint depth) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_generate_frame_pop_events); | 
|  | return StackUtil::NotifyFramePop(env, thread, depth); | 
|  | } | 
|  |  | 
|  | static jvmtiError ForceEarlyReturnObject(jvmtiEnv* env, jthread thread, jobject value) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_force_early_return); | 
|  | return StackUtil::ForceEarlyReturn(env, gEventHandler, thread, value); | 
|  | } | 
|  |  | 
|  | static jvmtiError ForceEarlyReturnInt(jvmtiEnv* env, jthread thread, jint value) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_force_early_return); | 
|  | return StackUtil::ForceEarlyReturn(env, gEventHandler, thread, value); | 
|  | } | 
|  |  | 
|  | static jvmtiError ForceEarlyReturnLong(jvmtiEnv* env, jthread thread, jlong value) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_force_early_return); | 
|  | return StackUtil::ForceEarlyReturn(env, gEventHandler, thread, value); | 
|  | } | 
|  |  | 
|  | static jvmtiError ForceEarlyReturnFloat(jvmtiEnv* env, jthread thread, jfloat value) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_force_early_return); | 
|  | return StackUtil::ForceEarlyReturn(env, gEventHandler, thread, value); | 
|  | } | 
|  |  | 
|  | static jvmtiError ForceEarlyReturnDouble(jvmtiEnv* env, jthread thread, jdouble value) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_force_early_return); | 
|  | return StackUtil::ForceEarlyReturn(env, gEventHandler, thread, value); | 
|  | } | 
|  |  | 
|  | static jvmtiError ForceEarlyReturnVoid(jvmtiEnv* env, jthread thread) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_force_early_return); | 
|  | return StackUtil::ForceEarlyReturn<nullptr_t>(env, gEventHandler, thread, nullptr); | 
|  | } | 
|  |  | 
|  | 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, | 
|  | jvmtiHeapObjectFilter object_filter, | 
|  | jvmtiHeapObjectCallback heap_object_callback, | 
|  | 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.IterateOverInstancesOfClass( | 
|  | env, klass, object_filter, heap_object_callback, user_data); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | return Transformer::RetransformClasses(env, class_count, classes); | 
|  | } | 
|  |  | 
|  | static jvmtiError RedefineClasses(jvmtiEnv* env, | 
|  | jint class_count, | 
|  | const jvmtiClassDefinition* class_definitions) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_redefine_classes); | 
|  | return Redefiner::RedefineClasses(env, class_count, class_definitions); | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_HAS_CAP(env, can_get_monitor_info); | 
|  | return ObjectUtil::GetObjectMonitorUsage(env, object, info_ptr); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | // Lock the event_info_mutex_ while we replace the callbacks. | 
|  | ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); | 
|  | art::WriterMutexLock lk(art::Thread::Current(), art_env->event_info_mutex_); | 
|  | std::unique_ptr<ArtJvmtiEventCallbacks> tmp(new ArtJvmtiEventCallbacks()); | 
|  | // Copy over the extension events. | 
|  | tmp->CopyExtensionsFrom(art_env->event_callbacks.get()); | 
|  | // Never overwrite the extension events. | 
|  | size_t copy_size = std::min(sizeof(jvmtiEventCallbacks), | 
|  | static_cast<size_t>(size_of_callbacks)); | 
|  | copy_size = art::RoundDown(copy_size, sizeof(void*)); | 
|  | // Copy non-extension events. | 
|  | memcpy(tmp.get(), callbacks, copy_size); | 
|  |  | 
|  | // replace the event table. | 
|  | art_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); | 
|  | ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); | 
|  | return gEventHandler->SetEvent(art_env, | 
|  | event_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); | 
|  | return ExtensionUtil::GetExtensionFunctions(env, extension_count_ptr, extensions); | 
|  | } | 
|  |  | 
|  | static jvmtiError GetExtensionEvents(jvmtiEnv* env, | 
|  | jint* extension_count_ptr, | 
|  | jvmtiExtensionEventInfo** extensions) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_NON_NULL(extension_count_ptr); | 
|  | ENSURE_NON_NULL(extensions); | 
|  | return ExtensionUtil::GetExtensionEvents(env, extension_count_ptr, extensions); | 
|  | } | 
|  |  | 
|  | static jvmtiError SetExtensionEventCallback(jvmtiEnv* env, | 
|  | jint extension_event_index, | 
|  | jvmtiExtensionEvent callback) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | return ExtensionUtil::SetExtensionEventCallback(env, | 
|  | extension_event_index, | 
|  | callback, | 
|  | gEventHandler); | 
|  | } | 
|  |  | 
|  | #define FOR_ALL_CAPABILITIES(FUN)                        \ | 
|  | FUN(can_tag_objects)                                 \ | 
|  | FUN(can_generate_field_modification_events)          \ | 
|  | FUN(can_generate_field_access_events)                \ | 
|  | FUN(can_get_bytecodes)                               \ | 
|  | FUN(can_get_synthetic_attribute)                     \ | 
|  | FUN(can_get_owned_monitor_info)                      \ | 
|  | FUN(can_get_current_contended_monitor)               \ | 
|  | FUN(can_get_monitor_info)                            \ | 
|  | FUN(can_pop_frame)                                   \ | 
|  | FUN(can_redefine_classes)                            \ | 
|  | FUN(can_signal_thread)                               \ | 
|  | FUN(can_get_source_file_name)                        \ | 
|  | FUN(can_get_line_numbers)                            \ | 
|  | FUN(can_get_source_debug_extension)                  \ | 
|  | FUN(can_access_local_variables)                      \ | 
|  | FUN(can_maintain_original_method_order)              \ | 
|  | FUN(can_generate_single_step_events)                 \ | 
|  | FUN(can_generate_exception_events)                   \ | 
|  | FUN(can_generate_frame_pop_events)                   \ | 
|  | FUN(can_generate_breakpoint_events)                  \ | 
|  | FUN(can_suspend)                                     \ | 
|  | FUN(can_redefine_any_class)                          \ | 
|  | FUN(can_get_current_thread_cpu_time)                 \ | 
|  | FUN(can_get_thread_cpu_time)                         \ | 
|  | FUN(can_generate_method_entry_events)                \ | 
|  | FUN(can_generate_method_exit_events)                 \ | 
|  | FUN(can_generate_all_class_hook_events)              \ | 
|  | FUN(can_generate_compiled_method_load_events)        \ | 
|  | FUN(can_generate_monitor_events)                     \ | 
|  | FUN(can_generate_vm_object_alloc_events)             \ | 
|  | FUN(can_generate_native_method_bind_events)          \ | 
|  | FUN(can_generate_garbage_collection_events)          \ | 
|  | FUN(can_generate_object_free_events)                 \ | 
|  | FUN(can_force_early_return)                          \ | 
|  | FUN(can_get_owned_monitor_stack_depth_info)          \ | 
|  | FUN(can_get_constant_pool)                           \ | 
|  | FUN(can_set_native_method_prefix)                    \ | 
|  | FUN(can_retransform_classes)                         \ | 
|  | FUN(can_retransform_any_class)                       \ | 
|  | FUN(can_generate_resource_exhaustion_heap_events)    \ | 
|  | FUN(can_generate_resource_exhaustion_threads_events) | 
|  |  | 
|  | static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) { | 
|  | ENSURE_VALID_ENV(env); | 
|  | ENSURE_NON_NULL(capabilities_ptr); | 
|  | *capabilities_ptr = kPotentialCapabilities; | 
|  | if (UNLIKELY(!IsFullJvmtiAvailable())) { | 
|  | #define REMOVE_NONDEBUGGABLE_UNSUPPORTED(e)                 \ | 
|  | do {                                                  \ | 
|  | if (kNonDebuggableUnsupportedCapabilities.e == 1) { \ | 
|  | capabilities_ptr->e = 0;                          \ | 
|  | }                                                   \ | 
|  | } while (false); | 
|  |  | 
|  | FOR_ALL_CAPABILITIES(REMOVE_NONDEBUGGABLE_UNSUPPORTED); | 
|  | #undef REMOVE_NONDEBUGGABLE_UNSUPPORTED | 
|  | } | 
|  | 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); | 
|  |  | 
|  | FOR_ALL_CAPABILITIES(ADD_CAPABILITY); | 
|  | #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); | 
|  |  | 
|  | FOR_ALL_CAPABILITIES(DEL_CAPABILITY); | 
|  | #undef DEL_CAPABILITY | 
|  | gEventHandler->HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env), | 
|  | changed, | 
|  | /*added=*/false); | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | #undef FOR_ALL_CAPABILITIES | 
|  |  | 
|  | 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 = ArtJvmTiEnv::AsArtJvmTiEnv(env)->ti_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); | 
|  | return LogUtil::SetVerboseFlag(env, flag, value); | 
|  | } | 
|  |  | 
|  | 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, jint version) | 
|  | : art_vm(runtime), | 
|  | local_data(nullptr), | 
|  | ti_version(version), | 
|  | capabilities(), | 
|  | event_info_mutex_("jvmtiEnv_EventInfoMutex"), | 
|  | last_error_mutex_("jvmtiEnv_LastErrorMutex", art::LockLevel::kGenericBottomLock) { | 
|  | object_tag_table = std::make_unique<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, jint version, /*out*/void** new_jvmtiEnv) { | 
|  | struct ArtJvmTiEnv* env = new ArtJvmTiEnv(vm, gEventHandler, version); | 
|  | *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) { | 
|  | // JavaDebuggable will either be set by the runtime as it is starting up or the plugin if it's | 
|  | // loaded early enough. If this is false we cannot guarantee conformance to all JVMTI behaviors | 
|  | // due to optimizations. We will only allow agents to get ArtTiEnvs using the kArtTiVersion. | 
|  | if (IsFullJvmtiAvailable() && IsJvmtiVersion(version)) { | 
|  | CreateArtJvmTiEnv(vm, JVMTI_VERSION, env); | 
|  | return JNI_OK; | 
|  | } else if (version == kArtTiVersion) { | 
|  | CreateArtJvmTiEnv(vm, kArtTiVersion, env); | 
|  | return JNI_OK; | 
|  | } else { | 
|  | printf("version 0x%x is not valid!", version); | 
|  | if (IsJvmtiVersion(version)) { | 
|  | LOG(ERROR) << "JVMTI Version 0x" << std::hex << version << " requested but the runtime is not" | 
|  | << " debuggable! Only limited, best effort kArtTiVersion" | 
|  | << " (0x" << std::hex << kArtTiVersion << ") environments are available. If" | 
|  | << " possible, rebuild your apk in debuggable mode or start the runtime with" | 
|  | << " the `-Xcompiler-option --debuggable` flags."; | 
|  | } | 
|  | return JNI_EVERSION; | 
|  | } | 
|  | } | 
|  |  | 
|  | // The plugin initialization function. This adds the jvmti environment. | 
|  | extern "C" bool ArtPlugin_Initialize() { | 
|  | art::Runtime* runtime = art::Runtime::Current(); | 
|  |  | 
|  | gAllocManager = new AllocationManager; | 
|  | gDeoptManager = new DeoptManager; | 
|  | gEventHandler = new EventHandler; | 
|  |  | 
|  | gDeoptManager->Setup(); | 
|  | if (runtime->IsStarted()) { | 
|  | PhaseUtil::SetToLive(); | 
|  | } else { | 
|  | PhaseUtil::SetToOnLoad(); | 
|  | } | 
|  | PhaseUtil::Register(gEventHandler); | 
|  | ThreadUtil::Register(gEventHandler); | 
|  | ClassUtil::Register(gEventHandler); | 
|  | DumpUtil::Register(gEventHandler); | 
|  | MethodUtil::Register(gEventHandler); | 
|  | HeapExtensions::Register(gEventHandler); | 
|  | SearchUtil::Register(); | 
|  | HeapUtil::Register(); | 
|  | FieldUtil::Register(gEventHandler); | 
|  | BreakpointUtil::Register(gEventHandler); | 
|  | Transformer::Register(gEventHandler); | 
|  | gDeoptManager->FinishSetup(); | 
|  | runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | extern "C" bool ArtPlugin_Deinitialize() { | 
|  | // When runtime is shutting down, it is not necessary to unregister callbacks or update | 
|  | // instrumentation levels. Removing callbacks require a GC critical section in some cases and | 
|  | // when runtime is shutting down we already stop GC and hence it is not safe to request to | 
|  | // enter a GC critical section. | 
|  | if (art::Runtime::Current()->IsShuttingDown(art::Thread::Current())) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | gEventHandler->Shutdown(); | 
|  | gDeoptManager->Shutdown(); | 
|  | PhaseUtil::Unregister(); | 
|  | ThreadUtil::Unregister(); | 
|  | ClassUtil::Unregister(); | 
|  | DumpUtil::Unregister(); | 
|  | MethodUtil::Unregister(); | 
|  | SearchUtil::Unregister(); | 
|  | HeapUtil::Unregister(); | 
|  | FieldUtil::Unregister(); | 
|  | BreakpointUtil::Unregister(); | 
|  |  | 
|  | // TODO It would be good to delete the gEventHandler and gDeoptManager here but we cannot since | 
|  | // daemon threads might be suspended and we want to make sure that even if they wake up briefly | 
|  | // they won't hit deallocated memory. By this point none of the functions will do anything since | 
|  | // they have already shutdown. | 
|  |  | 
|  | 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 |