深入浅出Android NDK之打印调用堆栈

随声附和 提交于 2020-08-12 08:12:53

目录
上一篇 深入浅出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:\Users\zy>adb logcat|ndk-stack -sym D:\ndk-demo\StrTest\app\build\intermediates\ndkBuild\debug\obj\local\armeabi-v7a\
********** Crash dump: **********
pid: 17980, tid: 17980, name: callstack.dump  >>> callstack.dump <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Stack frame 10-16 12:55:04.839 20856 20856 D MD_DEBUG: #00 pc 00009fef  /data/app/com.example.strtest-1/lib/arm/libstrtest.so: Routine callstackDump(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >&) at D:/ndk-demo/StrTest/app/src/main/jni/CallStack.cpp:19
Stack frame 10-16 12:55:04.839 20856 20856 D MD_DEBUG: #01 pc 0000a207  /data/app/com.example.strtest-1/lib/arm/libstrtest.so: Routine callstackLogcat(int, char const*) at D:/ndk-demo/StrTest/app/src/main/jni/CallStack.cpp:48
Stack frame 10-16 12:55:04.839 20856 20856 D MD_DEBUG: #02 pc 00009f91  /data/app/com.example.strtest-1/lib/arm/libstrtest.so: Routine fun1() at D:/ndk-demo/StrTest/app/src/main/jni/PThread.cpp:47
Stack frame 10-16 12:55:04.839 20856 20856 D MD_DEBUG: #03 pc 00009f9f  /data/app/com.example.strtest-1/lib/arm/libstrtest.so: Routine fun2() at D:/ndk-demo/StrTest/app/src/main/jni/PThread.cpp:51
Stack frame 10-16 12:55:04.839 20856 20856 D MD_DEBUG: #04 pc 00009fa9  /data/app/com.example.strtest-1/lib/arm/libstrtest.so: Routine fun3() at D:/ndk-demo/StrTest/app/src/main/jni/PThread.cpp:55
Stack frame 10-16 12:55:04.839 20856 20856 D MD_DEBUG: #05 pc 00009fc1  /data/app/com.example.strtest-1/lib/arm/libstrtest.so (Java_com_example_threadtest_PThread_start+00): Routine Java_com_example_threadtest_PThread_start at D:/ndk-demo/StrTest/app/src/main/jni/PThread.cpp:59
1
2
3
4
5
6
7
8
9
10
很方便,很完美!

下一篇 深入浅出Android NDK之重写JNIEnv函数
————————————————
版权声明:本文为CSDN博主「taohongtaohuyiwei」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/taohongtaohuyiwei/article/details/105147933

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!