| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "common_helper.h" |
| |
| #include "jni.h" |
| #include "jvmti.h" |
| |
| #include "jvmti_helper.h" |
| #include "scoped_local_ref.h" |
| #include "test_env.h" |
| |
| namespace art { |
| |
| namespace common_trace { |
| |
| static bool IsInCallback(JNIEnv* env, jvmtiEnv *jvmti, jthread thr) { |
| void* data; |
| ScopedLocalRef<jthrowable> exc(env, env->ExceptionOccurred()); |
| env->ExceptionClear(); |
| jvmti->GetThreadLocalStorage(thr, &data); |
| if (exc.get() != nullptr) { |
| env->Throw(exc.get()); |
| } |
| if (data == nullptr) { |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| static void SetInCallback(JNIEnv* env, jvmtiEnv *jvmti, jthread thr, bool val) { |
| ScopedLocalRef<jthrowable> exc(env, env->ExceptionOccurred()); |
| env->ExceptionClear(); |
| jvmti->SetThreadLocalStorage(thr, (val ? reinterpret_cast<void*>(0x1) |
| : reinterpret_cast<void*>(0x0))); |
| if (exc.get() != nullptr) { |
| env->Throw(exc.get()); |
| } |
| } |
| |
| class ScopedCallbackState { |
| public: |
| ScopedCallbackState(JNIEnv* jnienv, jvmtiEnv* env, jthread thr) |
| : jnienv_(jnienv), env_(env), thr_(thr) { |
| CHECK(!IsInCallback(jnienv_, env_, thr_)); |
| SetInCallback(jnienv_, env_, thr_, true); |
| } |
| ~ScopedCallbackState() { |
| CHECK(IsInCallback(jnienv_, env_, thr_)); |
| SetInCallback(jnienv_, env_, thr_, false); |
| } |
| |
| private: |
| JNIEnv* jnienv_; |
| jvmtiEnv* env_; |
| jthread thr_; |
| }; |
| |
| struct TraceData { |
| jclass test_klass; |
| jmethodID enter_method; |
| jmethodID exit_method; |
| jmethodID field_access; |
| jmethodID field_modify; |
| jmethodID single_step; |
| jmethodID thread_start; |
| jmethodID thread_end; |
| bool access_watch_on_load; |
| bool modify_watch_on_load; |
| jrawMonitorID trace_mon; |
| |
| jclass GetTestClass(jvmtiEnv* jvmti, JNIEnv* env) { |
| if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorEnter(trace_mon))) { |
| return nullptr; |
| } |
| jclass out = reinterpret_cast<jclass>(env->NewLocalRef(test_klass)); |
| if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorExit(trace_mon))) { |
| return nullptr; |
| } |
| return out; |
| } |
| }; |
| |
| static void threadStartCB(jvmtiEnv* jvmti, |
| JNIEnv* jnienv, |
| jthread thread) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException(jnienv, jvmti, |
| jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); |
| if (klass.get() == nullptr) { |
| return; |
| } |
| CHECK(data->thread_start != nullptr); |
| jnienv->CallStaticVoidMethod(klass.get(), data->thread_start, thread); |
| } |
| static void threadEndCB(jvmtiEnv* jvmti, |
| JNIEnv* jnienv, |
| jthread thread) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException(jnienv, jvmti, |
| jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); |
| if (klass.get() == nullptr) { |
| return; |
| } |
| CHECK(data->thread_end != nullptr); |
| jnienv->CallStaticVoidMethod(klass.get(), data->thread_end, thread); |
| } |
| |
| static void singleStepCB(jvmtiEnv* jvmti, |
| JNIEnv* jnienv, |
| jthread thread, |
| jmethodID method, |
| jlocation location) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException(jnienv, jvmti, |
| jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| if (IsInCallback(jnienv, jvmti, thread)) { |
| return; |
| } |
| ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); |
| if (klass.get() == nullptr) { |
| return; |
| } |
| CHECK(data->single_step != nullptr); |
| ScopedCallbackState st(jnienv, jvmti, thread); |
| jobject method_arg = GetJavaMethod(jvmti, jnienv, method); |
| jnienv->CallStaticVoidMethod(klass.get(), |
| data->single_step, |
| thread, |
| method_arg, |
| static_cast<jlong>(location)); |
| jnienv->DeleteLocalRef(method_arg); |
| } |
| |
| static void fieldAccessCB(jvmtiEnv* jvmti, |
| JNIEnv* jnienv, |
| jthread thr, |
| jmethodID method, |
| jlocation location, |
| jclass field_klass, |
| jobject object, |
| jfieldID field) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException(jnienv, jvmti, |
| jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| if (IsInCallback(jnienv, jvmti, thr)) { |
| // Don't do callback for either of these to prevent an infinite loop. |
| return; |
| } |
| ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); |
| if (klass.get() == nullptr) { |
| return; |
| } |
| CHECK(data->field_access != nullptr); |
| ScopedCallbackState st(jnienv, jvmti, thr); |
| jobject method_arg = GetJavaMethod(jvmti, jnienv, method); |
| jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field); |
| jnienv->CallStaticVoidMethod(klass.get(), |
| data->field_access, |
| method_arg, |
| static_cast<jlong>(location), |
| field_klass, |
| object, |
| field_arg); |
| jnienv->DeleteLocalRef(method_arg); |
| jnienv->DeleteLocalRef(field_arg); |
| } |
| |
| static void fieldModificationCB(jvmtiEnv* jvmti, |
| JNIEnv* jnienv, |
| jthread thr, |
| jmethodID method, |
| jlocation location, |
| jclass field_klass, |
| jobject object, |
| jfieldID field, |
| char type_char, |
| jvalue new_value) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException(jnienv, jvmti, |
| jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| if (IsInCallback(jnienv, jvmti, thr)) { |
| // Don't do callback recursively to prevent an infinite loop. |
| return; |
| } |
| ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); |
| if (klass.get() == nullptr) { |
| return; |
| } |
| CHECK(data->field_modify != nullptr); |
| ScopedCallbackState st(jnienv, jvmti, thr); |
| jobject method_arg = GetJavaMethod(jvmti, jnienv, method); |
| jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field); |
| jobject value = GetJavaValueByType(jnienv, type_char, new_value); |
| if (jnienv->ExceptionCheck()) { |
| jnienv->DeleteLocalRef(method_arg); |
| jnienv->DeleteLocalRef(field_arg); |
| return; |
| } |
| jnienv->CallStaticVoidMethod(klass.get(), |
| data->field_modify, |
| method_arg, |
| static_cast<jlong>(location), |
| field_klass, |
| object, |
| field_arg, |
| value); |
| jnienv->DeleteLocalRef(method_arg); |
| jnienv->DeleteLocalRef(field_arg); |
| } |
| |
| static void methodExitCB(jvmtiEnv* jvmti, |
| JNIEnv* jnienv, |
| jthread thr, |
| jmethodID method, |
| jboolean was_popped_by_exception, |
| jvalue return_value) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException(jnienv, jvmti, |
| jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| if (method == data->exit_method || |
| method == data->enter_method || |
| IsInCallback(jnienv, jvmti, thr)) { |
| // Don't do callback for either of these to prevent an infinite loop. |
| return; |
| } |
| ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); |
| if (klass.get() == nullptr) { |
| return; |
| } |
| CHECK(data->exit_method != nullptr); |
| ScopedCallbackState st(jnienv, jvmti, thr); |
| jobject method_arg = GetJavaMethod(jvmti, jnienv, method); |
| jobject result = |
| was_popped_by_exception ? nullptr : GetJavaValue(jvmti, jnienv, method, return_value); |
| if (jnienv->ExceptionCheck()) { |
| return; |
| } |
| jnienv->CallStaticVoidMethod(klass.get(), |
| data->exit_method, |
| method_arg, |
| was_popped_by_exception, |
| result); |
| jnienv->DeleteLocalRef(method_arg); |
| } |
| |
| static void methodEntryCB(jvmtiEnv* jvmti, |
| JNIEnv* jnienv, |
| jthread thr, |
| jmethodID method) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException(jnienv, jvmti, |
| jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| CHECK(data->enter_method != nullptr); |
| if (method == data->exit_method || |
| method == data->enter_method || |
| IsInCallback(jnienv, jvmti, thr)) { |
| // Don't do callback for either of these to prevent an infinite loop. |
| return; |
| } |
| ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); |
| if (klass.get() == nullptr) { |
| return; |
| } |
| ScopedCallbackState st(jnienv, jvmti, thr); |
| jobject method_arg = GetJavaMethod(jvmti, jnienv, method); |
| if (jnienv->ExceptionCheck()) { |
| return; |
| } |
| jnienv->CallStaticVoidMethod(klass.get(), data->enter_method, method_arg); |
| jnienv->DeleteLocalRef(method_arg); |
| } |
| |
| static void classPrepareCB(jvmtiEnv* jvmti, |
| JNIEnv* jnienv, |
| [[maybe_unused]] jthread thr, |
| jclass klass) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException(jnienv, jvmti, |
| jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| if (data->access_watch_on_load || data->modify_watch_on_load) { |
| jint nfields; |
| jfieldID* fields; |
| if (JvmtiErrorToException(jnienv, jvmti, jvmti->GetClassFields(klass, &nfields, &fields))) { |
| return; |
| } |
| for (jint i = 0; i < nfields; i++) { |
| jfieldID f = fields[i]; |
| // Ignore errors |
| if (data->access_watch_on_load) { |
| jvmti->SetFieldAccessWatch(klass, f); |
| } |
| |
| if (data->modify_watch_on_load) { |
| jvmti->SetFieldModificationWatch(klass, f); |
| } |
| } |
| jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields)); |
| } |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldAccesses(JNIEnv* env) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException( |
| env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| data->access_watch_on_load = true; |
| // We need the classPrepareCB to watch new fields as the classes are loaded/prepared. |
| if (JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_CLASS_PREPARE, |
| nullptr))) { |
| return; |
| } |
| jint nklasses; |
| jclass* klasses; |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) { |
| return; |
| } |
| for (jint i = 0; i < nklasses; i++) { |
| jclass k = klasses[i]; |
| |
| jint nfields; |
| jfieldID* fields; |
| jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields); |
| if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) { |
| continue; |
| } else if (JvmtiErrorToException(env, jvmti_env, err)) { |
| jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses)); |
| return; |
| } |
| for (jint j = 0; j < nfields; j++) { |
| jvmti_env->SetFieldAccessWatch(k, fields[j]); |
| } |
| jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields)); |
| } |
| jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses)); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldModifications(JNIEnv* env) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException( |
| env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| data->modify_watch_on_load = true; |
| // We need the classPrepareCB to watch new fields as the classes are loaded/prepared. |
| if (JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_CLASS_PREPARE, |
| nullptr))) { |
| return; |
| } |
| jint nklasses; |
| jclass* klasses; |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) { |
| return; |
| } |
| for (jint i = 0; i < nklasses; i++) { |
| jclass k = klasses[i]; |
| |
| jint nfields; |
| jfieldID* fields; |
| jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields); |
| if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) { |
| continue; |
| } else if (JvmtiErrorToException(env, jvmti_env, err)) { |
| jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses)); |
| return; |
| } |
| for (jint j = 0; j < nfields; j++) { |
| jvmti_env->SetFieldModificationWatch(k, fields[j]); |
| } |
| jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields)); |
| } |
| jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses)); |
| } |
| |
| static bool GetFieldAndClass(JNIEnv* env, |
| jobject ref_field, |
| jclass* out_klass, |
| jfieldID* out_field) { |
| *out_field = env->FromReflectedField(ref_field); |
| if (env->ExceptionCheck()) { |
| return false; |
| } |
| jclass field_klass = env->FindClass("java/lang/reflect/Field"); |
| if (env->ExceptionCheck()) { |
| return false; |
| } |
| jmethodID get_declaring_class_method = |
| env->GetMethodID(field_klass, "getDeclaringClass", "()Ljava/lang/Class;"); |
| if (env->ExceptionCheck()) { |
| env->DeleteLocalRef(field_klass); |
| return false; |
| } |
| *out_klass = static_cast<jclass>(env->CallObjectMethod(ref_field, get_declaring_class_method)); |
| if (env->ExceptionCheck()) { |
| *out_klass = nullptr; |
| env->DeleteLocalRef(field_klass); |
| return false; |
| } |
| env->DeleteLocalRef(field_klass); |
| return true; |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldModification( |
| JNIEnv* env, |
| [[maybe_unused]] jclass trace, |
| jobject field_obj) { |
| jfieldID field; |
| jclass klass; |
| if (!GetFieldAndClass(env, field_obj, &klass, &field)) { |
| return; |
| } |
| |
| JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldModificationWatch(klass, field)); |
| env->DeleteLocalRef(klass); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldAccess( |
| JNIEnv* env, |
| [[maybe_unused]] jclass trace, |
| jobject field_obj) { |
| jfieldID field; |
| jclass klass; |
| if (!GetFieldAndClass(env, field_obj, &klass, &field)) { |
| return; |
| } |
| JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldAccessWatch(klass, field)); |
| env->DeleteLocalRef(klass); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2( |
| JNIEnv* env, |
| [[maybe_unused]] jclass trace, |
| jclass klass, |
| jobject enter, |
| jobject exit, |
| jobject field_access, |
| jobject field_modify, |
| jobject single_step, |
| jobject thread_start, |
| jobject thread_end, |
| jthread thr) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->Allocate(sizeof(TraceData), |
| reinterpret_cast<unsigned char**>(&data)))) { |
| return; |
| } |
| memset(data, 0, sizeof(TraceData)); |
| if (JvmtiErrorToException(env, jvmti_env, |
| jvmti_env->CreateRawMonitor("Trace monitor", &data->trace_mon))) { |
| return; |
| } |
| data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass)); |
| data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr; |
| data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr; |
| data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr; |
| data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr; |
| data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr; |
| data->thread_start = thread_start != nullptr ? env->FromReflectedMethod(thread_start) : nullptr; |
| data->thread_end = thread_end != nullptr ? env->FromReflectedMethod(thread_end) : nullptr; |
| |
| TraceData* old_data = nullptr; |
| if (JvmtiErrorToException(env, jvmti_env, |
| jvmti_env->GetEnvironmentLocalStorage( |
| reinterpret_cast<void**>(&old_data)))) { |
| return; |
| } else if (old_data != nullptr && old_data->test_klass != nullptr) { |
| ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); |
| env->ThrowNew(rt_exception.get(), "Environment already has local storage set!"); |
| return; |
| } |
| if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) { |
| return; |
| } |
| |
| current_callbacks.MethodEntry = methodEntryCB; |
| current_callbacks.MethodExit = methodExitCB; |
| current_callbacks.FieldAccess = fieldAccessCB; |
| current_callbacks.FieldModification = fieldModificationCB; |
| current_callbacks.ClassPrepare = classPrepareCB; |
| current_callbacks.SingleStep = singleStepCB; |
| current_callbacks.ThreadStart = threadStartCB; |
| current_callbacks.ThreadEnd = threadEndCB; |
| if (JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventCallbacks(¤t_callbacks, |
| sizeof(current_callbacks)))) { |
| return; |
| } |
| if (enter != nullptr && |
| JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_METHOD_ENTRY, |
| thr))) { |
| return; |
| } |
| if (exit != nullptr && |
| JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_METHOD_EXIT, |
| thr))) { |
| return; |
| } |
| if (field_access != nullptr && |
| JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_FIELD_ACCESS, |
| thr))) { |
| return; |
| } |
| if (field_modify != nullptr && |
| JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_FIELD_MODIFICATION, |
| thr))) { |
| return; |
| } |
| if (single_step != nullptr && |
| JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_SINGLE_STEP, |
| thr))) { |
| return; |
| } |
| if (thread_start != nullptr && |
| JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_THREAD_START, |
| thr))) { |
| return; |
| } |
| if (thread_end != nullptr && |
| JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_THREAD_END, |
| thr))) { |
| return; |
| } |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing( |
| JNIEnv* env, |
| jclass trace, |
| jclass klass, |
| jobject enter, |
| jobject exit, |
| jobject field_access, |
| jobject field_modify, |
| jobject single_step, |
| jthread thr) { |
| Java_art_Trace_enableTracing2(env, |
| trace, |
| klass, |
| enter, |
| exit, |
| field_access, |
| field_modify, |
| single_step, |
| /* thread_start */ nullptr, |
| /* thread_end */ nullptr, |
| thr); |
| return; |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing( |
| JNIEnv* env, [[maybe_unused]] jclass klass, jthread thr) { |
| TraceData* data = nullptr; |
| if (JvmtiErrorToException( |
| env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { |
| return; |
| } |
| // If data is null then we haven't ever enabled tracing so we don't need to do anything. |
| if (data == nullptr || data->test_klass == nullptr) { |
| return; |
| } |
| ScopedLocalRef<jthrowable> err(env, nullptr); |
| // First disable all the events. |
| if (JvmtiErrorToException(env, jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, |
| JVMTI_EVENT_FIELD_ACCESS, |
| thr))) { |
| env->ExceptionDescribe(); |
| err.reset(env->ExceptionOccurred()); |
| env->ExceptionClear(); |
| } |
| if (JvmtiErrorToException(env, jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, |
| JVMTI_EVENT_FIELD_MODIFICATION, |
| thr))) { |
| env->ExceptionDescribe(); |
| err.reset(env->ExceptionOccurred()); |
| env->ExceptionClear(); |
| } |
| if (JvmtiErrorToException(env, jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, |
| JVMTI_EVENT_METHOD_ENTRY, |
| thr))) { |
| env->ExceptionDescribe(); |
| err.reset(env->ExceptionOccurred()); |
| env->ExceptionClear(); |
| } |
| if (JvmtiErrorToException(env, jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, |
| JVMTI_EVENT_METHOD_EXIT, |
| thr))) { |
| env->ExceptionDescribe(); |
| err.reset(env->ExceptionOccurred()); |
| env->ExceptionClear(); |
| } |
| if (JvmtiErrorToException(env, jvmti_env, |
| jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, |
| JVMTI_EVENT_SINGLE_STEP, |
| thr))) { |
| env->ExceptionDescribe(); |
| err.reset(env->ExceptionOccurred()); |
| env->ExceptionClear(); |
| } |
| if (JvmtiErrorToException(env, jvmti_env, |
| jvmti_env->RawMonitorEnter(data->trace_mon))) { |
| return; |
| } |
| // Clear test_klass so we know this isn't being used |
| env->DeleteGlobalRef(data->test_klass); |
| data->test_klass = nullptr; |
| if (JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->RawMonitorExit(data->trace_mon))) { |
| return; |
| } |
| if (err.get() != nullptr) { |
| env->Throw(err.get()); |
| } |
| } |
| |
| } // namespace common_trace |
| |
| |
| } // namespace art |