...
Linux开发中,底层经常会用到静态链接库(*.a)或动态链接库(*.so)来实现某一功能。
在应用层调用so文件时,也会经常遇到因为so内部的问题导致应用crash的现象。
crash发生后,系统会产生一个日志。日志大体内容如下:
01 |
I/DEBUG(349): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** |
02 |
I/DEBUG(349): Build fingerprint: |
03 |
'Android/jileniao.net/jileniao:5.0.1/LRX22G/root03231947:userdebug/test-keys' |
04 |
I/DEBUG(349): Revision: '33696'
|
05 |
I/DEBUG(349): ABI: 'arm'
|
06 |
I/DEBUG(349): pid: 2686, tid: 3065, name: gps_proc >>> /system/bin/gpsserver <<< |
07 |
I/DEBUG(349): signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0xb170a219 |
08 |
W/NativeCrashListener(856): Couldn't find ProcessRecord for pid 2686
|
09 |
I/DEBUG(349): r0 000fde18 r1 ae3f4248 r2 0007ef0c r3 00008000 |
10 |
E/DEBUG(349): AM write failure (32 / Broken pipe) I/DEBUG(349): r4 001fb799 r5 ffffa7e4 r6 ae4d00c0 r7 0001c5a2 |
11 |
I/DEBUG(349): r8 00000081 r9 00000001 sl b160c400 fp 00000001 |
12 |
I/DEBUG(349): ip ae4d00c0 sp af0bb8a0 lr b160c401 pc b5d08680 cpsr 280b0010 |
13 |
I/DEBUG(349): backtrace: |
14 |
I/DEBUG(349): #00 pc 0011d680 /system/lib/gps_ma87.so (Method0+984) |
15 |
I/DEBUG(349): #01 pc 0007694c /system/lib/gps_ma87.so (Method1+724) |
16 |
I/DEBUG(349): #02 pc 00069c2c /system/lib/gps_ma87.so (Method2+1116) |
17 |
I/DEBUG(349): #03 pc 000676f4 /system/lib/gps_ma87.so (Method3+224) |
18 |
I/DEBUG(349): #04 pc 00066bef /system/lib/gps_ma87.so (GPSCPPClassName::Method4(char*, int)+390) |
19 |
I/DEBUG(349): #05 pc 000438bf /system/lib/gps_ma87.so (gps::Method5(void)+134) |
20 |
I/DEBUG(349): #06 pc 00042fe1 /system/lib/gps_ma87.so (gps::Method6(void*)+40) |
21 |
I/DEBUG(349): #07 pc 00042429 /system/lib/gps_ma87.so (gps::Method7(void*)+172) |
22 |
I/DEBUG(349): #08 pc 000162e3 /system/lib/libc.so (__pthread_start(void*)+30) |
23 |
I/DEBUG(349): #09 pc 000142d3 /system/lib/libc.so (__start_thread+6) |
看到这些log之后,直接找最有用的backtrace信息。 backtrace描述了crash发生时函数的调用关系及其它地址信息等,可谓是真实地还原了crash的现场。接下来就看下现场的具体情况是怎么样的。
backtrace中,从#00到#09 共十行信息代表是crash时函数调用关系,从下往上倒着看,#09行的方法调用了#08行的方法;#08行的方法调用了#07行的方法;向上类推,最后就是#01行的方法调用了#00行的方法。而最终出现的crash就是在#00行中。
通过上面我们知道了crash发生时是哪个方法的问题了。但具体某一个方法还是范围有些大,对于排查问题来说,我们还希望能得到更细的结果。
在backtrace信息中,#00到#09每行标注的方法后面还有个+XXX的字样,最初我对此的理解,认为这就是行号了。但回到源代码中却发现不是这样子的。
要想知道确切的行号,就要用到每行的pc 后面的地址。
Linux为我们提供了addr2line的命令。通过这个命令可以根据地址得到行号。
先看addr2line命令的具体用法。
这里我们主要指定两个选项就可以达到看函数以及行号的目的。
- -e 后面加上so的文件名
- -f 同时输出函数名称
例子: addr2line -e test gps_ma87.so 0x0011d680 -f
这样即可得到函数名称以及具体哪个文件的多少行了。
addr2line得到的行号确定不可能出错
如果使用addr2line命令得到的行号对应的源代码中就是一句很简单的赋值语句这样的情况,也就是我们非常确定这行代码不可能出什么错误异常的。此时就要看调用该方法的地方是否有错,很有可能是调用该方法的地方就出错了,所以系统报错时候就定位到这一行了。
addr2line得到行号为??:?或??:0的原因
如果遇到addr2line得到??:?或??:0的情况,原因就是编译得到的so文件没有附加上符号表(symbolic)信息。
如果在android源码环境下,android编译环境会自动生成附带有符号表(symbolic)的so文件。标准android5.0中,附带有符号表(symbolic)的so文件在下面路径:
out/target/product/[productname]/symbols/system/lib/****.so
如果是非android环境,需要在makefile中指定生成附带有符号表(symbolic)的so文件的选项,具体关于动态链接库以及符号表等,请参阅如下网站: http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html