深入浅出Android NDK之打印调用堆栈
时间: 2020-06-29来源:OSCHINA
前景提要
目录
上一篇 深入浅出Android NDK之崩溃分析
为了能在native层打印函数的调用堆栈,找了好久的资料,最后终于找到一个靠谱的链接:
https://www.jianshu.com/p/4a5eeeee6d29
主要通过调用_Unwind_Backtrace函数来获得函数的调用堆栈,但是原文的并不好用,地址通过addr2line转换以后得不到函数名和行号,主要原因我们得到的地址是运行时地址,应该减去SO的基地址再来转换,下面看我改造后的例子,更好用。
#include <unwind.h>
#include <dlfcn.h>
#include <vector>
#include <string>
#include <android/log.h>
static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
{
std::vector<_Unwind_Word> &stack = *(std::vector<_Unwind_Word>*)arg;
stack.push_back(_Unwind_GetIP(context));
return _URC_NO_REASON;
}
void callstackDump(std::string &dump) {
std::vector<_Unwind_Word> stack;
_Unwind_Backtrace(unwindCallback, (void*)&stack);
dump.append(" \n"
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
"pid: 17980, tid: 17980, name: callstack.dump >>> callstack.dump <<<\n"
"signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0\n"
"r0 00000000 r1 00000000 r2 00000001 r3 00000001\n"
"r4 e8efe008 r5 e0537b99 r6 ff970b88 r7 ff970a98\n"
"r8 ff970de0 r9 e7904400 sl e790448c fp ff970b14\n"
"ip e8ef985c sp ff970a60 lr e8eca00f pc e0537d86 cpsr 200b0030\n"
"backtrace:\n");
char buff[256];
for (size_t i = 0; i < stack.size(); i++) {
Dl_info info;
if (!dladdr((void*)stack[i], &info)) {
continue;
}
int addr = (char*)stack[i] - (char*)info.dli_fbase - 1;
if (info.dli_sname == NULL || strlen(info.dli_sname) == 0) {
sprintf(buff, "#%02x pc %08x %s\n", i, addr, info.dli_fname);
} else {
sprintf(buff, "#%02x pc %08x %s (%s+00)\n", i, addr, info.dli_fname, info.dli_sname);
}
dump.append(buff);
}
}
void callstackLogcat(int prio, const char* tag) {
std::string dump;
callstackDump(dump);
__android_log_print(prio, tag, "%s", dump.c_str());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
把调用堆栈拼凑成崩溃日志的样子,主要是为了方便我们使用ndk-stack来解析。
下面来测试一下:
void fun1() {
callstackLogcat(ANDROID_LOG_DEBUG, "MD_DEBUG");
}
void fun2() {
fun1();
}
void fun3() {
fun2();
}
extern "C" JNIEXPORT void Java_com_example_threadtest_PThread_start(JNIEnv *env, jclass clazz) {
fun3();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
编译运行,得到以下输出:
2019-10-16 12:55:04.839 20856-20856/? D/MD_DEBUG:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
pid: 17980, tid: 17980, name: callstack.dump >>> callstack.dump <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
r0 00000000 r1 00000000 r2 00000001 r3 00000001
r4 e8efe008 r5 e0537b99 r6 ff970b88 r7 ff970a98
r8 ff970de0 r9 e7904400 sl e790448c fp ff970b14
ip e8ef985c sp ff970a60 lr e8eca00f pc e0537d86 cpsr 200b0030
backtrace:
#00 pc 00009fef /data/app/com.example.strtest-1/lib/arm/libstrtest.so
#01 pc 0000a207 /data/app/com.example.strtest-1/lib/arm/libstrtest.so
#02 pc 00009f91 /data/app/com.example.strtest-1/lib/arm/libstrtest.so
#03 pc 00009f9f /data/app/com.example.strtest-1/lib/arm/libstrtest.so
#04 pc 00009fa9 /data/app/com.example.strtest-1/lib/arm/libstrtest.so
#05 pc 00009fc1 /data/app/com.example.strtest-1/lib/arm/libstrtest.so (Java_com_example_threadtest_PThread_start+00)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
通过ndk-stack转换一下,得到:
C: