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