1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "common_helper.h" 18 19 #include "jni.h" 20 #include "jvmti.h" 21 22 #include "jvmti_helper.h" 23 #include "scoped_local_ref.h" 24 #include "test_env.h" 25 26 namespace art { 27 28 namespace common_exceptions { 29 30 struct ExceptionsData { 31 jclass test_klass; 32 jclass exception_klass; 33 jmethodID exception_event; 34 jmethodID exception_catch_event; 35 }; 36 37 static void exceptionCB(jvmtiEnv* jvmti, 38 JNIEnv* jnienv, 39 jthread thread, 40 jmethodID throw_method, 41 jlocation throw_location, 42 jobject throwable, 43 jmethodID catch_method, 44 jlocation catch_location) { 45 ExceptionsData* data = nullptr; 46 if (JvmtiErrorToException(jnienv, jvmti, 47 jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { 48 return; 49 } 50 DCHECK(throwable != nullptr); 51 if (!jnienv->IsInstanceOf(throwable, data->exception_klass)) { 52 return; 53 } 54 jthrowable e = jnienv->ExceptionOccurred(); 55 jnienv->ExceptionClear(); 56 CHECK(data->exception_event != nullptr); 57 jobject throw_method_arg = GetJavaMethod(jvmti, jnienv, throw_method); 58 jobject catch_method_arg = 59 catch_method != nullptr ? GetJavaMethod(jvmti, jnienv, catch_method) : nullptr; 60 jnienv->CallStaticVoidMethod(data->test_klass, 61 data->exception_event, 62 thread, 63 throw_method_arg, 64 static_cast<jlong>(throw_location), 65 throwable, 66 catch_method_arg, 67 static_cast<jlong>(catch_location)); 68 jnienv->DeleteLocalRef(throw_method_arg); 69 if (catch_method_arg != nullptr) { 70 jnienv->DeleteLocalRef(catch_method_arg); 71 } 72 if (e != nullptr) { 73 jnienv->Throw(e); 74 } 75 } 76 77 78 static void exceptionCatchCB(jvmtiEnv* jvmti, 79 JNIEnv* jnienv, 80 jthread thread, 81 jmethodID catch_method, 82 jlocation catch_location, 83 jobject throwable) { 84 ExceptionsData* data = nullptr; 85 if (JvmtiErrorToException(jnienv, jvmti, 86 jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { 87 return; 88 } 89 if (!jnienv->IsSameObject(data->exception_klass, jnienv->GetObjectClass(throwable))) { 90 return; 91 } 92 jthrowable e = jnienv->ExceptionOccurred(); 93 jnienv->ExceptionClear(); 94 CHECK(data->exception_catch_event != nullptr); 95 jobject catch_method_arg = GetJavaMethod(jvmti, jnienv, catch_method); 96 jnienv->CallStaticVoidMethod(data->test_klass, 97 data->exception_catch_event, 98 thread, 99 catch_method_arg, 100 static_cast<jlong>(catch_location), 101 throwable); 102 jnienv->DeleteLocalRef(catch_method_arg); 103 if (e != nullptr) { 104 jnienv->Throw(e); 105 } 106 } 107 108 extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_setupExceptionTracing( 109 JNIEnv* env, 110 jclass exception ATTRIBUTE_UNUSED, 111 jclass klass, 112 jclass except, 113 jobject exception_event, 114 jobject exception_catch_event) { 115 ExceptionsData* data = nullptr; 116 if (JvmtiErrorToException(env, 117 jvmti_env, 118 jvmti_env->Allocate(sizeof(ExceptionsData), 119 reinterpret_cast<unsigned char**>(&data)))) { 120 return; 121 } 122 jvmtiCapabilities caps; 123 memset(&caps, 0, sizeof(caps)); 124 caps.can_generate_exception_events = 1; 125 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) { 126 return; 127 } 128 memset(data, 0, sizeof(ExceptionsData)); 129 data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass)); 130 data->exception_klass = reinterpret_cast<jclass>(env->NewGlobalRef(except)); 131 data->exception_event = 132 exception_event != nullptr ? env->FromReflectedMethod(exception_event) : nullptr; 133 data->exception_catch_event = 134 exception_catch_event != nullptr ? env->FromReflectedMethod(exception_catch_event) : nullptr; 135 136 ExceptionsData* old_data = nullptr; 137 if (JvmtiErrorToException(env, jvmti_env, 138 jvmti_env->GetEnvironmentLocalStorage( 139 reinterpret_cast<void**>(&old_data)))) { 140 return; 141 } else if (old_data != nullptr && old_data->test_klass != nullptr) { 142 ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); 143 env->ThrowNew(rt_exception.get(), "Environment already has local storage set!"); 144 return; 145 } 146 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) { 147 return; 148 } 149 150 current_callbacks.Exception = exceptionCB; 151 current_callbacks.ExceptionCatch = exceptionCatchCB; 152 if (JvmtiErrorToException(env, 153 jvmti_env, 154 jvmti_env->SetEventCallbacks(¤t_callbacks, 155 sizeof(current_callbacks)))) { 156 return; 157 } 158 } 159 160 extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_enableExceptionCatchEvent( 161 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) { 162 JvmtiErrorToException(env, 163 jvmti_env, 164 jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 165 JVMTI_EVENT_EXCEPTION_CATCH, 166 thr)); 167 } 168 169 extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_enableExceptionEvent( 170 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) { 171 JvmtiErrorToException(env, 172 jvmti_env, 173 jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 174 JVMTI_EVENT_EXCEPTION, 175 thr)); 176 } 177 178 extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_disableExceptionCatchEvent( 179 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) { 180 JvmtiErrorToException(env, 181 jvmti_env, 182 jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, 183 JVMTI_EVENT_EXCEPTION_CATCH, 184 thr)); 185 } 186 187 extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_disableExceptionEvent( 188 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) { 189 JvmtiErrorToException(env, 190 jvmti_env, 191 jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, 192 JVMTI_EVENT_EXCEPTION, 193 thr)); 194 } 195 196 } // namespace common_exceptions 197 198 199 } // namespace art 200