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