xref: /aosp_15_r20/art/test/911-get-stack-trace/stack_trace.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2013 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include <inttypes.h>
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include <cstdio>
20*795d594fSAndroid Build Coastguard Worker #include <memory>
21*795d594fSAndroid Build Coastguard Worker 
22*795d594fSAndroid Build Coastguard Worker #include "android-base/logging.h"
23*795d594fSAndroid Build Coastguard Worker #include "android-base/stringprintf.h"
24*795d594fSAndroid Build Coastguard Worker 
25*795d594fSAndroid Build Coastguard Worker #include "jni.h"
26*795d594fSAndroid Build Coastguard Worker #include "jvmti.h"
27*795d594fSAndroid Build Coastguard Worker #include "scoped_local_ref.h"
28*795d594fSAndroid Build Coastguard Worker 
29*795d594fSAndroid Build Coastguard Worker // Test infrastructure
30*795d594fSAndroid Build Coastguard Worker #include "jni_binder.h"
31*795d594fSAndroid Build Coastguard Worker #include "jni_helper.h"
32*795d594fSAndroid Build Coastguard Worker #include "jvmti_helper.h"
33*795d594fSAndroid Build Coastguard Worker #include "test_env.h"
34*795d594fSAndroid Build Coastguard Worker #include "ti_macros.h"
35*795d594fSAndroid Build Coastguard Worker 
36*795d594fSAndroid Build Coastguard Worker namespace art {
37*795d594fSAndroid Build Coastguard Worker namespace Test911GetStackTrace {
38*795d594fSAndroid Build Coastguard Worker 
39*795d594fSAndroid Build Coastguard Worker using android::base::StringPrintf;
40*795d594fSAndroid Build Coastguard Worker 
FindLineNumber(jint line_number_count,jvmtiLineNumberEntry * line_number_table,jlocation location)41*795d594fSAndroid Build Coastguard Worker static jint FindLineNumber(jint line_number_count,
42*795d594fSAndroid Build Coastguard Worker                            jvmtiLineNumberEntry* line_number_table,
43*795d594fSAndroid Build Coastguard Worker                            jlocation location) {
44*795d594fSAndroid Build Coastguard Worker   if (line_number_table == nullptr) {
45*795d594fSAndroid Build Coastguard Worker     return -2;
46*795d594fSAndroid Build Coastguard Worker   }
47*795d594fSAndroid Build Coastguard Worker 
48*795d594fSAndroid Build Coastguard Worker   jint line_number = -1;
49*795d594fSAndroid Build Coastguard Worker   for (jint i = 0; i != line_number_count; ++i) {
50*795d594fSAndroid Build Coastguard Worker     if (line_number_table[i].start_location > location) {
51*795d594fSAndroid Build Coastguard Worker       return line_number;
52*795d594fSAndroid Build Coastguard Worker     }
53*795d594fSAndroid Build Coastguard Worker     line_number = line_number_table[i].line_number;
54*795d594fSAndroid Build Coastguard Worker   }
55*795d594fSAndroid Build Coastguard Worker   return line_number;
56*795d594fSAndroid Build Coastguard Worker }
57*795d594fSAndroid Build Coastguard Worker 
TranslateJvmtiFrameInfoArray(JNIEnv * env,jvmtiFrameInfo * frames,jint count)58*795d594fSAndroid Build Coastguard Worker static jobjectArray TranslateJvmtiFrameInfoArray(JNIEnv* env,
59*795d594fSAndroid Build Coastguard Worker                                                  jvmtiFrameInfo* frames,
60*795d594fSAndroid Build Coastguard Worker                                                  jint count) {
61*795d594fSAndroid Build Coastguard Worker   auto callback = [&](jint method_index) -> jobjectArray {
62*795d594fSAndroid Build Coastguard Worker     char* name;
63*795d594fSAndroid Build Coastguard Worker     char* sig;
64*795d594fSAndroid Build Coastguard Worker     char* gen;
65*795d594fSAndroid Build Coastguard Worker     {
66*795d594fSAndroid Build Coastguard Worker       jvmtiError result2 = jvmti_env->GetMethodName(frames[method_index].method, &name, &sig, &gen);
67*795d594fSAndroid Build Coastguard Worker       if (JvmtiErrorToException(env, jvmti_env, result2)) {
68*795d594fSAndroid Build Coastguard Worker         return nullptr;
69*795d594fSAndroid Build Coastguard Worker       }
70*795d594fSAndroid Build Coastguard Worker     }
71*795d594fSAndroid Build Coastguard Worker 
72*795d594fSAndroid Build Coastguard Worker     jint line_number_count;
73*795d594fSAndroid Build Coastguard Worker     jvmtiLineNumberEntry* line_number_table;
74*795d594fSAndroid Build Coastguard Worker     {
75*795d594fSAndroid Build Coastguard Worker       jvmtiError line_result = jvmti_env->GetLineNumberTable(frames[method_index].method,
76*795d594fSAndroid Build Coastguard Worker                                                              &line_number_count,
77*795d594fSAndroid Build Coastguard Worker                                                              &line_number_table);
78*795d594fSAndroid Build Coastguard Worker       if (line_result != JVMTI_ERROR_NONE) {
79*795d594fSAndroid Build Coastguard Worker         // Accept absent info and native method errors.
80*795d594fSAndroid Build Coastguard Worker         if (line_result != JVMTI_ERROR_ABSENT_INFORMATION &&
81*795d594fSAndroid Build Coastguard Worker             line_result != JVMTI_ERROR_NATIVE_METHOD) {
82*795d594fSAndroid Build Coastguard Worker           JvmtiErrorToException(env, jvmti_env, line_result);
83*795d594fSAndroid Build Coastguard Worker           return nullptr;
84*795d594fSAndroid Build Coastguard Worker         }
85*795d594fSAndroid Build Coastguard Worker         line_number_table = nullptr;
86*795d594fSAndroid Build Coastguard Worker         line_number_count = 0;
87*795d594fSAndroid Build Coastguard Worker       }
88*795d594fSAndroid Build Coastguard Worker     }
89*795d594fSAndroid Build Coastguard Worker 
90*795d594fSAndroid Build Coastguard Worker     auto inner_callback = [&](jint component_index) -> jstring {
91*795d594fSAndroid Build Coastguard Worker       switch (component_index) {
92*795d594fSAndroid Build Coastguard Worker         case 0:
93*795d594fSAndroid Build Coastguard Worker           return (name == nullptr) ? nullptr : env->NewStringUTF(name);
94*795d594fSAndroid Build Coastguard Worker         case 1:
95*795d594fSAndroid Build Coastguard Worker           return (sig == nullptr) ? nullptr : env->NewStringUTF(sig);
96*795d594fSAndroid Build Coastguard Worker         case 2:
97*795d594fSAndroid Build Coastguard Worker           return env->NewStringUTF(StringPrintf("%" PRId64, frames[method_index].location).c_str());
98*795d594fSAndroid Build Coastguard Worker         case 3: {
99*795d594fSAndroid Build Coastguard Worker           jint line_number = FindLineNumber(line_number_count,
100*795d594fSAndroid Build Coastguard Worker                                             line_number_table,
101*795d594fSAndroid Build Coastguard Worker                                             frames[method_index].location);
102*795d594fSAndroid Build Coastguard Worker           return env->NewStringUTF(StringPrintf("%d", line_number).c_str());
103*795d594fSAndroid Build Coastguard Worker         }
104*795d594fSAndroid Build Coastguard Worker       }
105*795d594fSAndroid Build Coastguard Worker       LOG(FATAL) << "Unreachable";
106*795d594fSAndroid Build Coastguard Worker       UNREACHABLE();
107*795d594fSAndroid Build Coastguard Worker     };
108*795d594fSAndroid Build Coastguard Worker     jobjectArray inner_array = CreateObjectArray(env, 4, "java/lang/String", inner_callback);
109*795d594fSAndroid Build Coastguard Worker 
110*795d594fSAndroid Build Coastguard Worker     if (name != nullptr) {
111*795d594fSAndroid Build Coastguard Worker       jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
112*795d594fSAndroid Build Coastguard Worker     }
113*795d594fSAndroid Build Coastguard Worker     if (sig != nullptr) {
114*795d594fSAndroid Build Coastguard Worker       jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
115*795d594fSAndroid Build Coastguard Worker     }
116*795d594fSAndroid Build Coastguard Worker     if (gen != nullptr) {
117*795d594fSAndroid Build Coastguard Worker       jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
118*795d594fSAndroid Build Coastguard Worker     }
119*795d594fSAndroid Build Coastguard Worker     if (line_number_table != nullptr) {
120*795d594fSAndroid Build Coastguard Worker       jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(line_number_table));
121*795d594fSAndroid Build Coastguard Worker     }
122*795d594fSAndroid Build Coastguard Worker 
123*795d594fSAndroid Build Coastguard Worker     return inner_array;
124*795d594fSAndroid Build Coastguard Worker   };
125*795d594fSAndroid Build Coastguard Worker   return CreateObjectArray(env, count, "[Ljava/lang/String;", callback);
126*795d594fSAndroid Build Coastguard Worker }
127*795d594fSAndroid Build Coastguard Worker 
Java_art_PrintThread_getStackTrace(JNIEnv * env,jclass klass,jthread thread,jint start,jint max)128*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jobjectArray JNICALL Java_art_PrintThread_getStackTrace(
129*795d594fSAndroid Build Coastguard Worker     JNIEnv* env, [[maybe_unused]] jclass klass, jthread thread, jint start, jint max) {
130*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]);
131*795d594fSAndroid Build Coastguard Worker 
132*795d594fSAndroid Build Coastguard Worker   jint count;
133*795d594fSAndroid Build Coastguard Worker   {
134*795d594fSAndroid Build Coastguard Worker     jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count);
135*795d594fSAndroid Build Coastguard Worker     if (JvmtiErrorToException(env, jvmti_env, result)) {
136*795d594fSAndroid Build Coastguard Worker       return nullptr;
137*795d594fSAndroid Build Coastguard Worker     }
138*795d594fSAndroid Build Coastguard Worker   }
139*795d594fSAndroid Build Coastguard Worker 
140*795d594fSAndroid Build Coastguard Worker   return TranslateJvmtiFrameInfoArray(env, frames.get(), count);
141*795d594fSAndroid Build Coastguard Worker }
142*795d594fSAndroid Build Coastguard Worker 
Java_art_AllTraces_getAllStackTraces(JNIEnv * env,jclass klass,jint max)143*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jobjectArray JNICALL Java_art_AllTraces_getAllStackTraces(
144*795d594fSAndroid Build Coastguard Worker     JNIEnv* env, [[maybe_unused]] jclass klass, jint max) {
145*795d594fSAndroid Build Coastguard Worker   jint thread_count;
146*795d594fSAndroid Build Coastguard Worker   jvmtiStackInfo* stack_infos;
147*795d594fSAndroid Build Coastguard Worker   {
148*795d594fSAndroid Build Coastguard Worker     jvmtiError result = jvmti_env->GetAllStackTraces(max, &stack_infos, &thread_count);
149*795d594fSAndroid Build Coastguard Worker     if (JvmtiErrorToException(env, jvmti_env, result)) {
150*795d594fSAndroid Build Coastguard Worker       return nullptr;
151*795d594fSAndroid Build Coastguard Worker     }
152*795d594fSAndroid Build Coastguard Worker   }
153*795d594fSAndroid Build Coastguard Worker 
154*795d594fSAndroid Build Coastguard Worker   auto callback = [&](jint thread_index) -> jobject {
155*795d594fSAndroid Build Coastguard Worker     auto inner_callback = [&](jint index) -> jobject {
156*795d594fSAndroid Build Coastguard Worker       if (index == 0) {
157*795d594fSAndroid Build Coastguard Worker         return stack_infos[thread_index].thread;
158*795d594fSAndroid Build Coastguard Worker       } else {
159*795d594fSAndroid Build Coastguard Worker         return TranslateJvmtiFrameInfoArray(env,
160*795d594fSAndroid Build Coastguard Worker                                             stack_infos[thread_index].frame_buffer,
161*795d594fSAndroid Build Coastguard Worker                                             stack_infos[thread_index].frame_count);
162*795d594fSAndroid Build Coastguard Worker       }
163*795d594fSAndroid Build Coastguard Worker     };
164*795d594fSAndroid Build Coastguard Worker     return CreateObjectArray(env, 2, "java/lang/Object", inner_callback);
165*795d594fSAndroid Build Coastguard Worker   };
166*795d594fSAndroid Build Coastguard Worker   jobjectArray ret = CreateObjectArray(env, thread_count, "[Ljava/lang/Object;", callback);
167*795d594fSAndroid Build Coastguard Worker   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(stack_infos));
168*795d594fSAndroid Build Coastguard Worker   return ret;
169*795d594fSAndroid Build Coastguard Worker }
170*795d594fSAndroid Build Coastguard Worker 
Java_art_ThreadListTraces_getThreadListStackTraces(JNIEnv * env,jclass klass,jobjectArray jthreads,jint max)171*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jobjectArray JNICALL Java_art_ThreadListTraces_getThreadListStackTraces(
172*795d594fSAndroid Build Coastguard Worker     JNIEnv* env, [[maybe_unused]] jclass klass, jobjectArray jthreads, jint max) {
173*795d594fSAndroid Build Coastguard Worker   jint thread_count = env->GetArrayLength(jthreads);
174*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<jthread[]> threads(new jthread[thread_count]);
175*795d594fSAndroid Build Coastguard Worker   for (jint i = 0; i != thread_count; ++i) {
176*795d594fSAndroid Build Coastguard Worker     threads[i] = env->GetObjectArrayElement(jthreads, i);
177*795d594fSAndroid Build Coastguard Worker   }
178*795d594fSAndroid Build Coastguard Worker 
179*795d594fSAndroid Build Coastguard Worker   jvmtiStackInfo* stack_infos;
180*795d594fSAndroid Build Coastguard Worker   {
181*795d594fSAndroid Build Coastguard Worker     jvmtiError result = jvmti_env->GetThreadListStackTraces(thread_count,
182*795d594fSAndroid Build Coastguard Worker                                                             threads.get(),
183*795d594fSAndroid Build Coastguard Worker                                                             max,
184*795d594fSAndroid Build Coastguard Worker                                                             &stack_infos);
185*795d594fSAndroid Build Coastguard Worker     if (JvmtiErrorToException(env, jvmti_env, result)) {
186*795d594fSAndroid Build Coastguard Worker       return nullptr;
187*795d594fSAndroid Build Coastguard Worker     }
188*795d594fSAndroid Build Coastguard Worker   }
189*795d594fSAndroid Build Coastguard Worker 
190*795d594fSAndroid Build Coastguard Worker   auto callback = [&](jint thread_index) -> jobject {
191*795d594fSAndroid Build Coastguard Worker     auto inner_callback = [&](jint index) -> jobject {
192*795d594fSAndroid Build Coastguard Worker       if (index == 0) {
193*795d594fSAndroid Build Coastguard Worker         return stack_infos[thread_index].thread;
194*795d594fSAndroid Build Coastguard Worker       } else {
195*795d594fSAndroid Build Coastguard Worker         return TranslateJvmtiFrameInfoArray(env,
196*795d594fSAndroid Build Coastguard Worker                                             stack_infos[thread_index].frame_buffer,
197*795d594fSAndroid Build Coastguard Worker                                             stack_infos[thread_index].frame_count);
198*795d594fSAndroid Build Coastguard Worker       }
199*795d594fSAndroid Build Coastguard Worker     };
200*795d594fSAndroid Build Coastguard Worker     return CreateObjectArray(env, 2, "java/lang/Object", inner_callback);
201*795d594fSAndroid Build Coastguard Worker   };
202*795d594fSAndroid Build Coastguard Worker   jobjectArray ret = CreateObjectArray(env, thread_count, "[Ljava/lang/Object;", callback);
203*795d594fSAndroid Build Coastguard Worker   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(stack_infos));
204*795d594fSAndroid Build Coastguard Worker   return ret;
205*795d594fSAndroid Build Coastguard Worker }
206*795d594fSAndroid Build Coastguard Worker 
Java_art_Frames_getFrameCount(JNIEnv * env,jclass klass,jthread thread)207*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Java_art_Frames_getFrameCount(
208*795d594fSAndroid Build Coastguard Worker     JNIEnv* env, [[maybe_unused]] jclass klass, jthread thread) {
209*795d594fSAndroid Build Coastguard Worker   jint count;
210*795d594fSAndroid Build Coastguard Worker   jvmtiError result = jvmti_env->GetFrameCount(thread, &count);
211*795d594fSAndroid Build Coastguard Worker   if (JvmtiErrorToException(env, jvmti_env, result)) {
212*795d594fSAndroid Build Coastguard Worker     return -1;
213*795d594fSAndroid Build Coastguard Worker   }
214*795d594fSAndroid Build Coastguard Worker   return count;
215*795d594fSAndroid Build Coastguard Worker }
216*795d594fSAndroid Build Coastguard Worker 
Java_art_Frames_getFrameLocation(JNIEnv * env,jclass klass,jthread thread,jint depth)217*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Frames_getFrameLocation(
218*795d594fSAndroid Build Coastguard Worker     JNIEnv* env, [[maybe_unused]] jclass klass, jthread thread, jint depth) {
219*795d594fSAndroid Build Coastguard Worker   jmethodID method;
220*795d594fSAndroid Build Coastguard Worker   jlocation location;
221*795d594fSAndroid Build Coastguard Worker 
222*795d594fSAndroid Build Coastguard Worker   jvmtiError result = jvmti_env->GetFrameLocation(thread, depth, &method, &location);
223*795d594fSAndroid Build Coastguard Worker   if (JvmtiErrorToException(env, jvmti_env, result)) {
224*795d594fSAndroid Build Coastguard Worker     return nullptr;
225*795d594fSAndroid Build Coastguard Worker   }
226*795d594fSAndroid Build Coastguard Worker 
227*795d594fSAndroid Build Coastguard Worker   auto callback = [&](jint index) -> jobject {
228*795d594fSAndroid Build Coastguard Worker     switch (index) {
229*795d594fSAndroid Build Coastguard Worker       case 0:
230*795d594fSAndroid Build Coastguard Worker       {
231*795d594fSAndroid Build Coastguard Worker         jclass decl_class;
232*795d594fSAndroid Build Coastguard Worker         jvmtiError class_result = jvmti_env->GetMethodDeclaringClass(method, &decl_class);
233*795d594fSAndroid Build Coastguard Worker         if (JvmtiErrorToException(env, jvmti_env, class_result)) {
234*795d594fSAndroid Build Coastguard Worker           return nullptr;
235*795d594fSAndroid Build Coastguard Worker         }
236*795d594fSAndroid Build Coastguard Worker         jint modifiers;
237*795d594fSAndroid Build Coastguard Worker         jvmtiError mod_result = jvmti_env->GetMethodModifiers(method, &modifiers);
238*795d594fSAndroid Build Coastguard Worker         if (JvmtiErrorToException(env, jvmti_env, mod_result)) {
239*795d594fSAndroid Build Coastguard Worker           return nullptr;
240*795d594fSAndroid Build Coastguard Worker         }
241*795d594fSAndroid Build Coastguard Worker         constexpr jint kStatic = 0x8;
242*795d594fSAndroid Build Coastguard Worker         return env->ToReflectedMethod(decl_class,
243*795d594fSAndroid Build Coastguard Worker                                       method,
244*795d594fSAndroid Build Coastguard Worker                                       (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
245*795d594fSAndroid Build Coastguard Worker       }
246*795d594fSAndroid Build Coastguard Worker       case 1:
247*795d594fSAndroid Build Coastguard Worker         return env->NewStringUTF(
248*795d594fSAndroid Build Coastguard Worker             android::base::StringPrintf("%x", static_cast<uint32_t>(location)).c_str());
249*795d594fSAndroid Build Coastguard Worker     }
250*795d594fSAndroid Build Coastguard Worker     LOG(FATAL) << "Unreachable";
251*795d594fSAndroid Build Coastguard Worker     UNREACHABLE();
252*795d594fSAndroid Build Coastguard Worker   };
253*795d594fSAndroid Build Coastguard Worker   jobjectArray ret = CreateObjectArray(env, 2, "java/lang/Object", callback);
254*795d594fSAndroid Build Coastguard Worker   return ret;
255*795d594fSAndroid Build Coastguard Worker }
256*795d594fSAndroid Build Coastguard Worker 
257*795d594fSAndroid Build Coastguard Worker }  // namespace Test911GetStackTrace
258*795d594fSAndroid Build Coastguard Worker }  // namespace art
259