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 
     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