安卓 JNI crash 日志分析 及问题定位 方法

目录标题

  • 例子
  • 抓取crash 日志
  • 日志分析
  • 写到最后

例子

void crash_test_func() {
     
    int* ptr = nullptr; //  行号173
    *ptr = 99;  //  行号174
}

// net 在 jni中调用
void Net::initEngine(const char* data) {
     
	crash_test_func();  //  行号179
}

以上程序,对一个空指针解引用做赋值操作,会抛出一个S异常。

抓取crash 日志

对于SIGSEGV等错误,安卓ndk都会对这些错误进行映射捕获,并且打印在logcat里。

adb logcat *:F

抓取全部 fault等级的日志也就是 crash的日志,自己可以按需自行添加tag 进行过滤。
抓取日志如下(部分)

--------- beginning of system
--------- beginning of main
--------- beginning of crash
12-02 16:55:40.806  4714  4714 F libc    : Fatal signal 4 (SIGILL), code 1 (ILL_ILLOPC), fault addr 0x81f98308 in tid 4714 (posetracksample), pid 4714 (posetracksample)
12-02 16:55:40.871  4983  4983 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
12-02 16:55:40.871  4983  4983 F DEBUG   : Build fingerprint: 'Droidlogic/w400/w400:9/PPR1.180610.011/20210407:userdebug/test-keys'
12-02 16:55:40.871  4983  4983 F DEBUG   : Revision: '0'
12-02 16:55:40.871  4983  4983 F DEBUG   : ABI: 'arm'
12-02 16:55:40.871  4983  4983 F DEBUG   : pid: 4714, tid: 4714, name: posetracksample  >>> com.orbbec.posetracksample <<<
12-02 16:55:40.871  4983  4983 F DEBUG   : signal 4 (SIGILL), code 1 (ILL_ILLOPC), fault addr 0x81f98308
12-02 16:55:40.871  4983  4983 F DEBUG   :     r0  00000061  r1  006fe152  r2  00000011  r3  00000000
12-02 16:55:40.871  4983  4983 F DEBUG   :     r4  bbc2c0e8  r5  bbc2c0b0  r6  a1e28d8c  r7  8584abc0
12-02 16:55:40.871  4983  4983 F DEBUG   :     r8  8584ab30  r9  a1e28d8c  r10 00000030  r11 bbc2c0d0
12-02 16:55:40.871  4983  4983 F DEBUG   :     ip  80b00000  sp  bbc2c0a0  lr  81f8bf50  pc  81f98308
12-02 16:55:40.999  4983  4983 F DEBUG   :
12-02 16:55:40.999  4983  4983 F DEBUG   : backtrace:
12-02 16:55:40.999  4983  4983 F DEBUG   :     #00 pc 000be308  /data/app/com.tt.posetracksample-eOLYiNeTZs8obQCsIZ24Ow==/lib/arm/libobt.so
12-02 16:55:40.999  4983  4983 F DEBUG   :     #01 pc 000bab18  /data/app/com.tt.posetracksample-eOLYiNeTZs8obQCsIZ24Ow==/lib/arm/libobt.so
12-02 16:55:40.999  4983  4983 F DEBUG   :     #02 pc 000aeba0  /data/app/com.tt.posetracksample-eOLYiNeTZs8obQCsIZ24Ow==/lib/arm/libobt.so (obt_initialize+580)

日志分析

在日志里面,有价值的是backtrace 部分,他会将崩溃现场的函数调用堆栈保存下来了。
由日志可知,执行到libobt.so 的 0x000be308处抛出了一个SIGILL错误,由#01 处的 地址0x000bab18进行调用,依次类推。

根据这个地址我们通过addr2line(linux 自带)就可以知道具体对应代码在哪。

addr2line -e libobt.so  000be308
/mnt/library/shanrx/src/net.cpp:174

这里有几点需要注意:

  1. 一般来说我们的so库都是release版本的,他将调试信息去除了,根据这些调试信息,这些调试信息存储着地址及代码行号的对应关系;直接addr2line无法显示
addr2line -e libobt.so  000be308
??:?

对于这个问题也很好解决,在cmakelist添加编译选项-g即可

add_compile_options(-g)

再对这些新生成的动态库进行addr2line即可,-g 并不会修改代码地址,只是附加上了一些调试信息。

  1. 由于是release版本,编译器优化会让函数的调用并不是一定按照代码进行,例如以上第二层栈针的地址为0x000bab18,但转出来的地址是并不是net.cpp:179,而是更上一层函数调用,也就是crash_test_func并没有做函数调用,而是内联优化了。
addr2line -e libobt.so  000bab18
/mnt/library/shanrx/src/model.cpp:45

写到最后

虽然例子里面写的很简单,实际工作里面遇到的crash不会像例子这么一目了然,但知道crash在那一行多多少少能给开发人员解决bug给提供些许思路,方便定位问题。这也算是不需要另外额外成本的解决方法。

你可能感兴趣的:(C\C++,jni,安卓,c++,sdk)