Android中Crash Debug技巧
工作中经常会遇到Android中Crash的情况,之前没有一个很清晰的思路,现在总结一下:
一般情况下,Android中发生了Crash,会以“beginning of crash”为开头,记录crash发生的相关信息,比如是哪个进程(pid)发生的crash,如下所示
--------- beginning of crash
08-02 17:11:45.761 10549 10655 F libc : Fatal signal -1447396355 (?), code -1862269955 (? from pid -1459396616, uid -145) in tid 10655 (Runner: gl_5_hi), pid 10549 (50005.corporate)
08-02 17:11:45.761 938 983 I libPowerHal: [perfLockRel] hdl:60244, idx:4
08-02 17:11:45.761 938 983 I libPowerHal: [PD] fpsgo update cmd:1408300 param:0
因此,从adb logcat的角度判断是否发生了crash,在log中搜索"beginning of crash"则可以,如果搜索到了,就证明该进程发生了crash。
那么如何debug呢?需要看log中的crash这部分的log有没有记录下backtrace
1.crash记录了backtrace
直接搜"backtrace",会看到类似下面的log片段:
07-08 08:33:43.062 2091 2091 F DEBUG : Cmdline: /vendor/bin/hw/rild -l /vendor/lib64/libquectel-ril.so
07-08 08:33:43.062 2091 2091 F DEBUG : pid: 2087, tid: 2087, name: rild >>> /vendor/bin/hw/rild <<<
07-08 08:33:43.062 2091 2091 F DEBUG : uid: 0
07-08 08:33:43.062 2091 2091 F DEBUG : tagged_addr_ctrl: 0000000000000001
07-08 08:33:43.062 2091 2091 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
07-08 08:33:43.062 2091 2091 F DEBUG : Cause: null pointer dereference
07-08 08:33:43.062 2091 2091 F DEBUG : x0 0000007038a8afa8 x1 0000007fd7cd33d8 x2 0000000000000000 x3 0000007fd7cd343a
07-08 08:33:43.062 2091 2091 F DEBUG : x4 0000000000000000 x5 0000000000000000 x6 726b68737473646d x7 7f7f7f7f7f7f7f7f
07-08 08:33:43.062 2091 2091 F DEBUG : x8 3a07c997d0de8c07 x9 3a07c997d0de8c07 x10 0000007fd7cd2c08 x11 fffffffffffffffd
07-08 08:33:43.062 2091 2091 F DEBUG : x12 0000007fd7cd2d20 x13 00000000000000cb x14 0000007fd7cd2ea8 x15 000000008aac1451
07-08 08:33:43.062 2091 2091 F DEBUG : x16 00000072ca56a530 x17 00000072ca6d3610 x18 00000072ce3e4000 x19 0000007fd7cd3438
07-08 08:33:43.062 2091 2091 F DEBUG : x20 0000007038aae000 x21 0000007038aaf010 x22 0000007038a86000 x23 0000007038aaf000
07-08 08:33:43.062 2091 2091 F DEBUG : x24 0000007fd7cd33d8 x25 0000000000000003 x26 0000005a616d9368 x27 0000000000000000
07-08 08:33:43.062 2091 2091 F DEBUG : x28 0000007038a8af40 x29 0000007fd7cd38a0
07-08 08:33:43.062 2091 2091 F DEBUG : lr 0000007038a5c76c sp 0000007fd7cd3300 pc 0000000000000000 pst 0000000060001000
07-08 08:33:43.062 2091 2091 F DEBUG : backtrace:
07-08 08:33:43.062 2091 2091 F DEBUG : #00 pc 0000000000000000 <unknown>
07-08 08:33:43.062 2091 2091 F DEBUG : #01 pc 000000000001b768 /vendor/lib64/libquectel-ril.so (RIL_Init+720)
07-08 08:33:43.062 2091 2091 F DEBUG : #02 pc 0000000000002378 /vendor/bin/hw/rild (main+804) (BuildId: e9dcddf3d5bf74361d31231eafbe6a08)
07-08 08:33:43.062 2091 2091 F DEBUG : #03 pc 00000000000488c8 /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+96) (BuildId: ba489d4985c0cf173209da67405662f9)
其中有三个地方需要注意:
- Cmdline:指示代码是运行什么样的命令/应用时发生crash的,该处是运行"/vendor/bin/hw/rild -l /vendor/lib64/libquectel-ril.so"
- Cause:指示发生Crash的原因是什么,该处是"Cause: null pointer dereference",即发生了空指针的解引用。
- backtrace:指示调用的顺序
1.1如何debug:
1.1.1找到发生Crash的函数,然后手动在源代码中加log,一步步debug
backtrace中,对应的log中会提示是跑在哪个函数中。比如:
07-08 08:33:43.062 2091 2091 F DEBUG : #01 pc 000000000001b768 /vendor/lib64/libquectel-ril.so (RIL_Init+720)
指示跑在libquectel-ril.so中的RIL_Init函数中。则我们可以检查这个RIL_Init这个函数中的代码并加log进行debug。
1.1.2利用addr2line这个工具,精准定位到哪个文件的哪一行
比如我们想定位发生crash的RIL_Init函数在哪个文件的哪一行,则可以先找到上述信息中的偏移地址“01 pc 000000000001b768”
$ cd <Android_Source_Code>/out/target/product/<specific_product_name>/symbols/
$ find . -name libquectel-ril.so
$ cd <path_libquectel-ril.so>
$ addr2line -C -f -e libquectel-ril.so 000000000001b768
//上述命令找到symbol的位置,利用addr2line命令把偏移量转换为函数所在的文件与代码行,可以让我们知道crash在代码中的具体位置
可能会遇到"addr2line: DWARF error: invalid or unhandled FORM value"的错误,无法成功拿到正确的输出,则应该用Android Source Code中自带的addr2line工具就可以解决:
prebuilts/clang/host/linux-x86/clang-r487747c/bin/llvm-addr2line
补充:我们发布xxxx.so文件的时候,可以连同xxxx.so.symbols一起发布,这样即使我们的编译环境更改了,也可以通过 xxxx.so.symbols 文件定位错误发生在哪一行,用法跟xxxx.so一样,以上面的命令为例,即:
$ addr2line -C -f -e libquectel-ril.so.symbols 000000000001b768
脚本:
exelist=(exe1 exe2)
addrlist=(addr1 addr2)
exe1=out/target/product/xxxx/symbols/vendor/lib64/egl/libGLES_mali.so
addr1=(
0000000001aa1990
0000000001926ab4
0000000001996188
0000000001ab2e1c
0000000001ab0fa0
0000000001ab1030
0000000001aaefd0
)
exe2=out/target/product/mahakala/symbols/data/nativetest64/vendor/mali_tests64/mali_vulkan_integration_suite
addr2=(
)
echo ${addr[*]}
for i in ${addr1[*]}; do
llvm-addr2line -e ${exe1} 0x${i}
done
for i in ${addr2[*]}; do
llvm-addr2line -e ${exe2} 0x${i}
done
2. crash未记录backtrace
没有backtrace意味着不知道是哪里发生了crash,这一般是由于对空指针进行解引用,导致不能记录backtrace。也就是说你只能在log里搜到"beginning of crash",而搜不到"backtrace"。这种情况需要去查找tombstone。Android的tombstone会帮助记录这些信息。
$ adb shell
$ cd /data/tombstone/
tombstone中会记录的信息跟前面的情况一样,包含:Cmdline, Cause和backtrace。Debug方法同crash记录了backtrace的情景。