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_breakpoint {
29 
30 struct BreakpointData {
31   jclass test_klass;
32   jmethodID breakpoint_method;
33   bool in_callback;
34   bool allow_recursive;
35 };
36 
breakpointCB(jvmtiEnv * jvmti,JNIEnv * jnienv,jthread thread,jmethodID method,jlocation location)37 extern "C" void breakpointCB(jvmtiEnv* jvmti,
38                              JNIEnv* jnienv,
39                              jthread thread,
40                              jmethodID method,
41                              jlocation location) {
42   BreakpointData* data = nullptr;
43   if (JvmtiErrorToException(jnienv, jvmti,
44                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
45     return;
46   }
47   if (data->in_callback && !data->allow_recursive) {
48     return;
49   }
50   data->in_callback = true;
51   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
52   jnienv->CallStaticVoidMethod(data->test_klass,
53                                data->breakpoint_method,
54                                thread,
55                                method_arg,
56                                static_cast<jlong>(location));
57   jnienv->DeleteLocalRef(method_arg);
58   data->in_callback = false;
59 }
60 
Java_art_Breakpoint_getLineNumberTableNative(JNIEnv * env,jclass k ATTRIBUTE_UNUSED,jobject target)61 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Breakpoint_getLineNumberTableNative(
62     JNIEnv* env,
63     jclass k ATTRIBUTE_UNUSED,
64     jobject target) {
65   jmethodID method = env->FromReflectedMethod(target);
66   if (env->ExceptionCheck()) {
67     return nullptr;
68   }
69   jint nlines;
70   jvmtiLineNumberEntry* lines = nullptr;
71   if (JvmtiErrorToException(env, jvmti_env,
72                             jvmti_env->GetLineNumberTable(method, &nlines, &lines))) {
73     return nullptr;
74   }
75   jintArray lines_array = env->NewIntArray(nlines);
76   if (env->ExceptionCheck()) {
77     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
78     return nullptr;
79   }
80   jlongArray locs_array = env->NewLongArray(nlines);
81   if (env->ExceptionCheck()) {
82     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
83     return nullptr;
84   }
85   ScopedLocalRef<jclass> object_class(env, env->FindClass("java/lang/Object"));
86   if (env->ExceptionCheck()) {
87     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
88     return nullptr;
89   }
90   jobjectArray ret = env->NewObjectArray(2, object_class.get(), nullptr);
91   if (env->ExceptionCheck()) {
92     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
93     return nullptr;
94   }
95   jint* temp_lines = env->GetIntArrayElements(lines_array, /*isCopy*/nullptr);
96   jlong* temp_locs = env->GetLongArrayElements(locs_array, /*isCopy*/nullptr);
97   for (jint i = 0; i < nlines; i++) {
98     temp_lines[i] = lines[i].line_number;
99     temp_locs[i] = lines[i].start_location;
100   }
101   env->ReleaseIntArrayElements(lines_array, temp_lines, 0);
102   env->ReleaseLongArrayElements(locs_array, temp_locs, 0);
103   env->SetObjectArrayElement(ret, 0, locs_array);
104   env->SetObjectArrayElement(ret, 1, lines_array);
105   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
106   return ret;
107 }
108 
Java_art_Breakpoint_getStartLocation(JNIEnv * env,jclass k ATTRIBUTE_UNUSED,jobject target)109 extern "C" JNIEXPORT jlong JNICALL Java_art_Breakpoint_getStartLocation(JNIEnv* env,
110                                                                         jclass k ATTRIBUTE_UNUSED,
111                                                                         jobject target) {
112   jmethodID method = env->FromReflectedMethod(target);
113   if (env->ExceptionCheck()) {
114     return 0;
115   }
116   jlong start = 0;
117   jlong end;
118   JvmtiErrorToException(env, jvmti_env, jvmti_env->GetMethodLocation(method, &start, &end));
119   return start;
120 }
121 
Java_art_Breakpoint_clearBreakpoint(JNIEnv * env,jclass k ATTRIBUTE_UNUSED,jobject target,jlocation location)122 extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_clearBreakpoint(JNIEnv* env,
123                                                                       jclass k ATTRIBUTE_UNUSED,
124                                                                       jobject target,
125                                                                       jlocation location) {
126   jmethodID method = env->FromReflectedMethod(target);
127   if (env->ExceptionCheck()) {
128     return;
129   }
130   JvmtiErrorToException(env, jvmti_env, jvmti_env->ClearBreakpoint(method, location));
131 }
132 
Java_art_Breakpoint_setBreakpoint(JNIEnv * env,jclass k ATTRIBUTE_UNUSED,jobject target,jlocation location)133 extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_setBreakpoint(JNIEnv* env,
134                                                                     jclass k ATTRIBUTE_UNUSED,
135                                                                     jobject target,
136                                                                     jlocation location) {
137   jmethodID method = env->FromReflectedMethod(target);
138   if (env->ExceptionCheck()) {
139     return;
140   }
141   JvmtiErrorToException(env, jvmti_env, jvmti_env->SetBreakpoint(method, location));
142 }
143 
Java_art_Breakpoint_startBreakpointWatch(JNIEnv * env,jclass k ATTRIBUTE_UNUSED,jclass method_klass,jobject method,jboolean allow_recursive,jthread thr)144 extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_startBreakpointWatch(
145     JNIEnv* env,
146     jclass k ATTRIBUTE_UNUSED,
147     jclass method_klass,
148     jobject method,
149     jboolean allow_recursive,
150     jthread thr) {
151   BreakpointData* data = nullptr;
152   if (JvmtiErrorToException(env,
153                             jvmti_env,
154                             jvmti_env->Allocate(sizeof(BreakpointData),
155                                                 reinterpret_cast<unsigned char**>(&data)))) {
156     return;
157   }
158   memset(data, 0, sizeof(BreakpointData));
159   data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(method_klass));
160   data->breakpoint_method = env->FromReflectedMethod(method);
161   data->in_callback = false;
162   data->allow_recursive = allow_recursive;
163 
164   void* old_data = nullptr;
165   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
166     return;
167   } else if (old_data != nullptr) {
168     ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
169     env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
170     return;
171   }
172   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
173     return;
174   }
175   current_callbacks.Breakpoint = breakpointCB;
176   if (JvmtiErrorToException(env,
177                             jvmti_env,
178                             jvmti_env->SetEventCallbacks(&current_callbacks,
179                                                          sizeof(current_callbacks)))) {
180     return;
181   }
182   if (JvmtiErrorToException(env,
183                             jvmti_env,
184                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
185                                                                 JVMTI_EVENT_BREAKPOINT,
186                                                                 thr))) {
187     return;
188   }
189 }
190 
Java_art_Breakpoint_stopBreakpointWatch(JNIEnv * env,jclass k ATTRIBUTE_UNUSED,jthread thr)191 extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_stopBreakpointWatch(
192     JNIEnv* env,
193     jclass k ATTRIBUTE_UNUSED,
194     jthread thr) {
195   if (JvmtiErrorToException(env, jvmti_env,
196                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
197                                                                 JVMTI_EVENT_BREAKPOINT,
198                                                                 thr))) {
199     return;
200   }
201 }
202 
203 }  // namespace common_breakpoint
204 
205 }  // namespace art
206