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 
exceptionCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thread,jmethodID throw_method,jlocation throw_location,jobject throwable,jmethodID catch_method,jlocation catch_location)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 
exceptionCatchCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thread,jmethodID catch_method,jlocation catch_location,jobject throwable)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 
Java_art_Exceptions_setupExceptionTracing(JNIEnv * env,jclass exception ATTRIBUTE_UNUSED,jclass klass,jclass except,jobject exception_event,jobject exception_catch_event)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(&current_callbacks,
155                                                          sizeof(current_callbacks)))) {
156     return;
157   }
158 }
159 
Java_art_Exceptions_enableExceptionCatchEvent(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jthread thr)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 
Java_art_Exceptions_enableExceptionEvent(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jthread thr)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 
Java_art_Exceptions_disableExceptionCatchEvent(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jthread thr)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 
Java_art_Exceptions_disableExceptionEvent(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jthread thr)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