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