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(¤t_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