stack_trace.cc revision eba32fbff82bf135090c121d2126bef7b4ee5c3b
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 "base/logging.h" 24#include "base/macros.h" 25#include "jni.h" 26#include "openjdkjvmti/jvmti.h" 27#include "ScopedLocalRef.h" 28#include "ti-agent/common_helper.h" 29#include "ti-agent/common_load.h" 30 31namespace art { 32namespace Test911GetStackTrace { 33 34using android::base::StringPrintf; 35 36static jint FindLineNumber(jint line_number_count, 37 jvmtiLineNumberEntry* line_number_table, 38 jlocation location) { 39 if (line_number_table == nullptr) { 40 return -2; 41 } 42 43 jint line_number = -1; 44 for (jint i = 0; i != line_number_count; ++i) { 45 if (line_number_table[i].start_location > location) { 46 return line_number; 47 } 48 line_number = line_number_table[i].line_number; 49 } 50 return line_number; 51} 52 53static jobjectArray TranslateJvmtiFrameInfoArray(JNIEnv* env, 54 jvmtiFrameInfo* frames, 55 jint count) { 56 auto callback = [&](jint method_index) -> jobjectArray { 57 char* name; 58 char* sig; 59 char* gen; 60 { 61 jvmtiError result2 = jvmti_env->GetMethodName(frames[method_index].method, &name, &sig, &gen); 62 if (JvmtiErrorToException(env, result2)) { 63 return nullptr; 64 } 65 } 66 67 jint line_number_count; 68 jvmtiLineNumberEntry* line_number_table; 69 { 70 jvmtiError line_result = jvmti_env->GetLineNumberTable(frames[method_index].method, 71 &line_number_count, 72 &line_number_table); 73 if (line_result != JVMTI_ERROR_NONE) { 74 // Accept absent info and native method errors. 75 if (line_result != JVMTI_ERROR_ABSENT_INFORMATION && 76 line_result != JVMTI_ERROR_NATIVE_METHOD) { 77 char* err; 78 jvmti_env->GetErrorName(line_result, &err); 79 printf("Failure running GetLineNumberTable: %s\n", err); 80 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); 81 return nullptr; 82 } 83 line_number_table = nullptr; 84 line_number_count = 0; 85 } 86 } 87 88 auto inner_callback = [&](jint component_index) -> jstring { 89 switch (component_index) { 90 case 0: 91 return (name == nullptr) ? nullptr : env->NewStringUTF(name); 92 case 1: 93 return (sig == nullptr) ? nullptr : env->NewStringUTF(sig); 94 case 2: 95 return env->NewStringUTF(StringPrintf("%" PRId64, frames[method_index].location).c_str()); 96 case 3: { 97 jint line_number = FindLineNumber(line_number_count, 98 line_number_table, 99 frames[method_index].location); 100 return env->NewStringUTF(StringPrintf("%d", line_number).c_str()); 101 } 102 } 103 LOG(FATAL) << "Unreachable"; 104 UNREACHABLE(); 105 }; 106 jobjectArray inner_array = CreateObjectArray(env, 4, "java/lang/String", inner_callback); 107 108 if (name != nullptr) { 109 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name)); 110 } 111 if (sig != nullptr) { 112 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig)); 113 } 114 if (gen != nullptr) { 115 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen)); 116 } 117 if (line_number_table != nullptr) { 118 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(line_number_table)); 119 } 120 121 return inner_array; 122 }; 123 return CreateObjectArray(env, count, "[Ljava/lang/String;", callback); 124} 125 126extern "C" JNIEXPORT jobjectArray JNICALL Java_PrintThread_getStackTrace( 127 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) { 128 std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]); 129 130 jint count; 131 { 132 jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count); 133 if (JvmtiErrorToException(env, result)) { 134 return nullptr; 135 } 136 } 137 138 return TranslateJvmtiFrameInfoArray(env, frames.get(), count); 139} 140 141extern "C" JNIEXPORT jobjectArray JNICALL Java_AllTraces_getAllStackTraces( 142 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint max) { 143 jint thread_count; 144 jvmtiStackInfo* stack_infos; 145 { 146 jvmtiError result = jvmti_env->GetAllStackTraces(max, &stack_infos, &thread_count); 147 if (JvmtiErrorToException(env, result)) { 148 return nullptr; 149 } 150 } 151 152 auto callback = [&](jint thread_index) -> jobject { 153 auto inner_callback = [&](jint index) -> jobject { 154 if (index == 0) { 155 return stack_infos[thread_index].thread; 156 } else { 157 return TranslateJvmtiFrameInfoArray(env, 158 stack_infos[thread_index].frame_buffer, 159 stack_infos[thread_index].frame_count); 160 } 161 }; 162 return CreateObjectArray(env, 2, "java/lang/Object", inner_callback); 163 }; 164 jobjectArray ret = CreateObjectArray(env, thread_count, "[Ljava/lang/Object;", callback); 165 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(stack_infos)); 166 return ret; 167} 168 169extern "C" JNIEXPORT jobjectArray JNICALL Java_ThreadListTraces_getThreadListStackTraces( 170 JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobjectArray jthreads, jint max) { 171 jint thread_count = env->GetArrayLength(jthreads); 172 std::unique_ptr<jthread[]> threads(new jthread[thread_count]); 173 for (jint i = 0; i != thread_count; ++i) { 174 threads[i] = env->GetObjectArrayElement(jthreads, i); 175 } 176 177 jvmtiStackInfo* stack_infos; 178 { 179 jvmtiError result = jvmti_env->GetThreadListStackTraces(thread_count, 180 threads.get(), 181 max, 182 &stack_infos); 183 if (JvmtiErrorToException(env, result)) { 184 return nullptr; 185 } 186 } 187 188 auto callback = [&](jint thread_index) -> jobject { 189 auto inner_callback = [&](jint index) -> jobject { 190 if (index == 0) { 191 return stack_infos[thread_index].thread; 192 } else { 193 return TranslateJvmtiFrameInfoArray(env, 194 stack_infos[thread_index].frame_buffer, 195 stack_infos[thread_index].frame_count); 196 } 197 }; 198 return CreateObjectArray(env, 2, "java/lang/Object", inner_callback); 199 }; 200 jobjectArray ret = CreateObjectArray(env, thread_count, "[Ljava/lang/Object;", callback); 201 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(stack_infos)); 202 return ret; 203} 204 205} // namespace Test911GetStackTrace 206} // namespace art 207