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

目录
上一篇 深入浅出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());
}

把调用堆栈拼凑成崩溃日志的样子,主要是为了方便我们使用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();
}

编译运行,得到以下输出:

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)

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

很方便,很完美!

下一篇 深入浅出Android NDK之重写JNIEnv函数

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值