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