1 /* 2 * Copyright (C) 2013 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 <inttypes.h> 18 19 #include <cstdio> 20 #include <memory> 21 22 #include "android-base/logging.h" 23 #include "android-base/stringprintf.h" 24 25 #include "jni.h" 26 #include "jvmti.h" 27 #include "scoped_local_ref.h" 28 29 // Test infrastructure 30 #include "jni_binder.h" 31 #include "jni_helper.h" 32 #include "jvmti_helper.h" 33 #include "test_env.h" 34 #include "ti_macros.h" 35 36 namespace art { 37 namespace Test911GetStackTrace { 38 39 using android::base::StringPrintf; 40 41 static jint FindLineNumber(jint line_number_count, 42 jvmtiLineNumberEntry* line_number_table, 43 jlocation location) { 44 if (line_number_table == nullptr) { 45 return -2; 46 } 47 48 jint line_number = -1; 49 for (jint i = 0; i != line_number_count; ++i) { 50 if (line_number_table[i].start_location > location) { 51 return line_number; 52 } 53 line_number = line_number_table[i].line_number; 54 } 55 return line_number; 56 } 57 58 static jobjectArray TranslateJvmtiFrameInfoArray(JNIEnv* env, 59 jvmtiFrameInfo* frames, 60 jint count) { 61 auto callback = [&](jint method_index) -> jobjectArray { 62 char* name; 63 char* sig; 64 char* gen; 65 { 66 jvmtiError result2 = jvmti_env->GetMethodName(frames[method_index].method, &name, &sig, &gen); 67 if (JvmtiErrorToException(env, jvmti_env, result2)) { 68 return nullptr; 69 } 70 } 71 72 jint line_number_count; 73 jvmtiLineNumberEntry* line_number_table; 74 { 75 jvmtiError line_result = jvmti_env->GetLineNumberTable(frames[method_index].method, 76 &line_number_count, 77 &line_number_table); 78 if (line_result != JVMTI_ERROR_NONE) { 79 // Accept absent info and native method errors. 80 if (line_result != JVMTI_ERROR_ABSENT_INFORMATION && 81 line_result != JVMTI_ERROR_NATIVE_METHOD) { 82 JvmtiErrorToException(env, jvmti_env, line_result); 83 return nullptr; 84 } 85 line_number_table = nullptr; 86 line_number_count = 0; 87 } 88 } 89 90 auto inner_callback = [&](jint component_index) -> jstring { 91 switch (component_index) { 92 case 0: 93 return (name == nullptr) ? nullptr : env->NewStringUTF(name); 94 case 1: 95 return (sig == nullptr) ? nullptr : env->NewStringUTF(sig); 96 case 2: 97 return env->NewStringUTF(StringPrintf("%" PRId64, frames[method_index].location).c_str()); 98 case 3: { 99 jint line_number = FindLineNumber(line_number_count, 100 line_number_table, 101 frames[method_index].location); 102 return env->NewStringUTF(StringPrintf("%d", line_number).c_str()); 103 } 104 } 105 LOG(FATAL) << "Unreachable"; 106 UNREACHABLE(); 107 }; 108 jobjectArray inner_array = CreateObjectArray(env, 4, "java/lang/String", inner_callback); 109 110 if (name != nullptr) { 111 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name)); 112 } 113 if (sig != nullptr) { 114 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig)); 115 } 116 if (gen != nullptr) { 117 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen)); 118 } 119 if (line_number_table != nullptr) { 120 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(line_number_table)); 121 } 122 123 return inner_array; 124 }; 125 return CreateObjectArray(env, count, "[Ljava/lang/String;", callback); 126 } 127 128 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_PrintThread_getStackTrace( 129 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) { 130 std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]); 131 132 jint count; 133 { 134 jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count); 135 if (JvmtiErrorToException(env, jvmti_env, result)) { 136 return nullptr; 137 } 138 } 139 140 return TranslateJvmtiFrameInfoArray(env, frames.get(), count); 141 } 142 143 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_AllTraces_getAllStackTraces( 144 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint max) { 145 jint thread_count; 146 jvmtiStackInfo* stack_infos; 147 { 148 jvmtiError result = jvmti_env->GetAllStackTraces(max, &stack_infos, &thread_count); 149 if (JvmtiErrorToException(env, jvmti_env, result)) { 150 return nullptr; 151 } 152 } 153 154 auto callback = [&](jint thread_index) -> jobject { 155 auto inner_callback = [&](jint index) -> jobject { 156 if (index == 0) { 157 return stack_infos[thread_index].thread; 158 } else { 159 return TranslateJvmtiFrameInfoArray(env, 160 stack_infos[thread_index].frame_buffer, 161 stack_infos[thread_index].frame_count); 162 } 163 }; 164 return CreateObjectArray(env, 2, "java/lang/Object", inner_callback); 165 }; 166 jobjectArray ret = CreateObjectArray(env, thread_count, "[Ljava/lang/Object;", callback); 167 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(stack_infos)); 168 return ret; 169 } 170 171 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_ThreadListTraces_getThreadListStackTraces( 172 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobjectArray jthreads, jint max) { 173 jint thread_count = env->GetArrayLength(jthreads); 174 std::unique_ptr<jthread[]> threads(new jthread[thread_count]); 175 for (jint i = 0; i != thread_count; ++i) { 176 threads[i] = env->GetObjectArrayElement(jthreads, i); 177 } 178 179 jvmtiStackInfo* stack_infos; 180 { 181 jvmtiError result = jvmti_env->GetThreadListStackTraces(thread_count, 182 threads.get(), 183 max, 184 &stack_infos); 185 if (JvmtiErrorToException(env, jvmti_env, result)) { 186 return nullptr; 187 } 188 } 189 190 auto callback = [&](jint thread_index) -> jobject { 191 auto inner_callback = [&](jint index) -> jobject { 192 if (index == 0) { 193 return stack_infos[thread_index].thread; 194 } else { 195 return TranslateJvmtiFrameInfoArray(env, 196 stack_infos[thread_index].frame_buffer, 197 stack_infos[thread_index].frame_count); 198 } 199 }; 200 return CreateObjectArray(env, 2, "java/lang/Object", inner_callback); 201 }; 202 jobjectArray ret = CreateObjectArray(env, thread_count, "[Ljava/lang/Object;", callback); 203 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(stack_infos)); 204 return ret; 205 } 206 207 extern "C" JNIEXPORT jint JNICALL Java_art_Frames_getFrameCount( 208 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread) { 209 jint count; 210 jvmtiError result = jvmti_env->GetFrameCount(thread, &count); 211 if (JvmtiErrorToException(env, jvmti_env, result)) { 212 return -1; 213 } 214 return count; 215 } 216 217 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Frames_getFrameLocation( 218 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint depth) { 219 jmethodID method; 220 jlocation location; 221 222 jvmtiError result = jvmti_env->GetFrameLocation(thread, depth, &method, &location); 223 if (JvmtiErrorToException(env, jvmti_env, result)) { 224 return nullptr; 225 } 226 227 auto callback = [&](jint index) -> jobject { 228 switch (index) { 229 case 0: 230 { 231 jclass decl_class; 232 jvmtiError class_result = jvmti_env->GetMethodDeclaringClass(method, &decl_class); 233 if (JvmtiErrorToException(env, jvmti_env, class_result)) { 234 return nullptr; 235 } 236 jint modifiers; 237 jvmtiError mod_result = jvmti_env->GetMethodModifiers(method, &modifiers); 238 if (JvmtiErrorToException(env, jvmti_env, mod_result)) { 239 return nullptr; 240 } 241 constexpr jint kStatic = 0x8; 242 return env->ToReflectedMethod(decl_class, 243 method, 244 (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE); 245 } 246 case 1: 247 return env->NewStringUTF( 248 android::base::StringPrintf("%x", static_cast<uint32_t>(location)).c_str()); 249 } 250 LOG(FATAL) << "Unreachable"; 251 UNREACHABLE(); 252 }; 253 jobjectArray ret = CreateObjectArray(env, 2, "java/lang/Object", callback); 254 return ret; 255 } 256 257 } // namespace Test911GetStackTrace 258 } // namespace art 259