Android解讀Native崩潰棧信息的方法詳解
大部分的 Android 開(kāi)發(fā)者使用的主要語(yǔ)言都是 Kotlin / Java,他們的崩潰棧信息非常清晰,也非常好定位到問(wèn)題,如果是線上的崩潰通常還會(huì)對(duì)類名進(jìn)行混淆,還需要一個(gè)混淆文件對(duì)堆棧翻譯一下就能夠得到源碼中的類名。
但是很多人對(duì) C/C++ 的崩潰棧就無(wú)能為力了,今天這篇文章就來(lái)扒一扒 Native 的崩潰棧信息。
Native 崩潰棧信息
我們經(jīng)常能夠看到有類似下面的 Native 崩潰信息:
Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 17356 (tMediaPlayerDec), pid 15253 (ediaplayer.demo) pid: 15253, tid: 17356, name: tMediaPlayerDec >>> com.tans.tmediaplayer.demo <<< #01 pc 000000000001bd2c /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so #02 pc 000000000001ba98 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so #03 pc 000000000001b878 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so #04 pc 000000000001b878 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so #05 pc 000000000001b878 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so #06 pc 000000000001b878 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so #07 pc 000000000001b878 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so #08 pc 000000000001cf08 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so (Java_com_tans_tmediaplayer_tMediaPlayer_decodeNative+52) (BuildId: 58ab2061a06db613d9c3ca66a214872ad88636f7) #11 pc 000000000000952c [anon:dalvik-classes5.dex extracted in memory from /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/base.apk!classes5.dex] (com.tans.tmediaplayer.tMediaPlayer.decodeNativeInternal$tmediaplayer_debug+0) #13 pc 00000000000057f6 [anon:dalvik-classes5.dex extracted in memory from /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/base.apk!classes5.dex] (com.tans.tmediaplayer.tMediaPlayerDecoder$decoderHandler$2$1.dispatchMessage+850)
Native 中的崩潰是通過(guò)系統(tǒng)信號(hào)來(lái)實(shí)現(xiàn)的,比如我們上面的異常信號(hào)就是 SIGABRT,Android 的進(jìn)程在啟動(dòng)時(shí)就會(huì)添加一個(gè) SignalCatcher,來(lái)捕獲信號(hào),不同的信號(hào)有不同的處理方式,SIGABRT 就是會(huì)直接退出程序,也就是我們常說(shuō)的崩潰,Android 中還有一個(gè)非常重要的信號(hào)就是 SIGQUIT,在 Android 中表示發(fā)生了 ANR,默認(rèn)的處理邏輯是 dump 棧信息和內(nèi)存 GC 相關(guān)的信息到本地文件。
好了這里說(shuō)得有點(diǎn)遠(yuǎn)了,回到上面的問(wèn)題,我們剛開(kāi)始看到上面的數(shù)據(jù)可能有點(diǎn)懵逼,pc 后面有一串 16 進(jìn)制的數(shù)字表示程序計(jì)數(shù)器的位置 (簡(jiǎn)單理解就是執(zhí)行的機(jī)器碼對(duì)應(yīng)的位置),后面的文件表示崩潰的棧中相關(guān)的 .so 動(dòng)態(tài)鏈接庫(kù)。但是你又要說(shuō)了,這一串地址誰(shuí)能夠看出什么問(wèn)題啊???? 你先不要急,通常線上的用戶崩潰看到的棧是這樣的,因?yàn)?Android 的 Release 包默認(rèn)會(huì)抹掉一部分叫做符號(hào)表的東西,如果你看過(guò)我上面的文章你就會(huì)豁然開(kāi)朗,這個(gè)符號(hào)表描述了指令地址和對(duì)應(yīng)方法或者變量的映射(方法名,全局變量名都是符號(hào)),通常我們用的別人的 .so 包也會(huì)抹掉符號(hào)表(這可能就是不想讓你看,起到了一個(gè)混淆作用),少了一個(gè)表在線上的運(yùn)行中性能會(huì)更加好(至少這部分內(nèi)存不用消耗了)。
通常我們自己打的 Debug 包就沒(méi)有抹掉符號(hào)表,如果是有符號(hào)表信息,我們看到的上面異常信息通常是下面這樣的:
Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 17356 (tMediaPlayerDec), pid 15253 (ediaplayer.demo) pid: 15253, tid: 17356, name: tMediaPlayerDec >>> com.tans.tmediaplayer.demo <<< #01 pc 000000000001bd2c /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so (tMediaPlayerContext::parseDecodeAudioFrameToBuffer(tMediaDecodeBuffer*)+464) (BuildId: 58ab2061a06db613d9c3ca66a214872ad88636f7) #02 pc 000000000001ba98 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so (tMediaPlayerContext::decode(tMediaDecodeBuffer*)+1076) (BuildId: 58ab2061a06db613d9c3ca66a214872ad88636f7) #03 pc 000000000001b878 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so (tMediaPlayerContext::decode(tMediaDecodeBuffer*)+532) (BuildId: 58ab2061a06db613d9c3ca66a214872ad88636f7) #04 pc 000000000001b878 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so (tMediaPlayerContext::decode(tMediaDecodeBuffer*)+532) (BuildId: 58ab2061a06db613d9c3ca66a214872ad88636f7) #05 pc 000000000001b878 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so (tMediaPlayerContext::decode(tMediaDecodeBuffer*)+532) (BuildId: 58ab2061a06db613d9c3ca66a214872ad88636f7) #06 pc 000000000001b878 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so (tMediaPlayerContext::decode(tMediaDecodeBuffer*)+532) (BuildId: 58ab2061a06db613d9c3ca66a214872ad88636f7) #07 pc 000000000001b878 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so (tMediaPlayerContext::decode(tMediaDecodeBuffer*)+532) (BuildId: 58ab2061a06db613d9c3ca66a214872ad88636f7) #08 pc 000000000001cf08 /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/lib/arm64/libtmediaplayer.so (Java_com_tans_tmediaplayer_tMediaPlayer_decodeNative+52) (BuildId: 58ab2061a06db613d9c3ca66a214872ad88636f7) #11 pc 000000000000952c [anon:dalvik-classes5.dex extracted in memory from /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/base.apk!classes5.dex] (com.tans.tmediaplayer.tMediaPlayer.decodeNativeInternal$tmediaplayer_debug+0) #13 pc 00000000000057f6 [anon:dalvik-classes5.dex extracted in memory from /data/app/~~fIT1aTQ88Pxdg1-7Ax4AXQ==/com.tans.tmediaplayer.demo-8WVpgXB0od_2YBBfM4FnZQ==/base.apk!classes5.dex] (com.tans.tmediaplayer.tMediaPlayerDecoder$decoderHandler$2$1.dispatchMessage+850)
你看這里就有調(diào)用所對(duì)應(yīng)的方法了,因?yàn)槲疫@里的 decode() 方法是遞歸調(diào)用的,所以你看到上面的棧中有多個(gè),方法后面還有一個(gè) +532 表示該地址離方法開(kāi)始的地址的偏移量,如果你的 .so 文件里面還有 debug 信息,這個(gè) +532 能夠定位到某一行 C \ C++ 源碼,其實(shí)就是每條指令都映射了某一行代碼。
在 Android 的打包過(guò)程中如果你希望 Release 包也不要移除符號(hào)表信息,可以通過(guò)在 build.gradle 中添加以下配置來(lái)避免符號(hào)表被移除:
// ...
packagingOptions {
doNotStrip "*/arm64-v8a/*.so"
doNotStrip "*/armeabi-v7a/*.so"
doNotStrip "*/x86/*.so"
doNotStrip "*/x86_64/*.so"
}
// ...
如果符號(hào)表被移除了那我們不是永遠(yuǎn)都不知道崩潰的方法是什么了?當(dāng)然不是,被移除的符號(hào)會(huì)被保存到另外的文件中,線上的崩潰可以通過(guò)這個(gè)文件再次翻譯成對(duì)應(yīng)的方法。以下就是符號(hào)文件對(duì)應(yīng)的路徑:

它解壓后如下:

他們也是 ELF 格式的文件,每個(gè)都對(duì)應(yīng)了一個(gè) .so 庫(kù)。
那么我們?cè)趺磁袛嘁粋€(gè) .so 文件是不是有符號(hào)表呢?可以通過(guò) file 命令查看:
libtmediaplayer.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=58ab2061a06db613d9c3ca66a214872ad88636f7, with debug_info, not stripped
not stripped 就表示有符號(hào)表。
libtmediaplayer.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=58ab2061a06db613d9c3ca66a214872ad88636f7, stripped
stripped 就表示沒(méi)有符號(hào)表。
Tips: 如果你上架的應(yīng)用沒(méi)有這個(gè)符號(hào)表,Google Play 還會(huì)提醒你上傳,Google Play 是可以幫你捕獲 Native 崩潰的,它需要符號(hào)表解析這些地址信息。
符號(hào)表
我們了解 ELF 文件就知道,我們上面說(shuō)的符號(hào)表就是本地符號(hào)表對(duì)應(yīng)的就是 .symtab Section,這個(gè)表對(duì)我們的程序運(yùn)行沒(méi)有任何的影響,我們調(diào)用本地方法都是通過(guò)地址直接跳轉(zhuǎn),而不需要本地符號(hào)表。
還有一個(gè)符號(hào)表是 .dynsym,它是動(dòng)態(tài)鏈接的符號(hào)表,這個(gè)表是供 ld.so 使用的,因?yàn)檫@里面的符號(hào)都是暴露給其他的程序調(diào)用的,ld.so 需要通過(guò)這個(gè)表去查詢暴露給其他程序的符號(hào)的地址,所以不能刪除。
我們可以通過(guò) readelf -s [elf file] 讀取符號(hào)表:
以下是有符號(hào)表的數(shù)據(jù):
Symbol table '.dynsym' contains 447 entries: Num: Value Size Type Bind Vis Ndx Name // ... 441: 000000000001c494 40 FUNC GLOBAL DEFAULT 15 Java_com_tans_tmediaplayer_tMediaPlayer_durationNative 442: 000000000001cac4 136 FUNC GLOBAL DEFAULT 15 Java_com_tans_tmediaplayer_tMediaPlayer_getVideoFrameUBytesNative // ... Symbol table '.symtab' contains 2470 entries: Num: Value Size Type Bind Vis Ndx Name // ... 2067: 000000000001ae20 2116 FUNC GLOBAL DEFAULT 15 _ZN19tMediaPlayerContext29parseDecodeVideoFrameToBufferEP18tMediaDecodeBuffer // ... 2086: 000000000001bef4 36 FUNC WEAK DEFAULT 15 _ZN18tMediaDecodeBufferC2Ev 2087: 000000000001bf18 28 FUNC WEAK DEFAULT 15 _ZN17tMediaAudioBufferC2Ev 2088: 000000000001bf34 80 FUNC WEAK DEFAULT 15 _ZN17tMediaVideoBufferC2Ev 2089: 000000000001bf84 360 FUNC GLOBAL DEFAULT 15 _Z16freeDecodeBufferP18tMediaDecodeBuffer 2090: 000000000001c0ec 336 FUNC GLOBAL DEFAULT 15 _ZN19tMediaPlayerContext7releaseEv // ...
如果沒(méi)有本地符號(hào)表就只有以下信息(少了 .symtab):
Symbol table '.dynsym' contains 447 entries: Num: Value Size Type Bind Vis Ndx Name // ... 441: 000000000001c494 40 FUNC GLOBAL DEFAULT 15 Java_com_tans_tmediaplayer_tMediaPlayer_durationNative 442: 000000000001cac4 136 FUNC GLOBAL DEFAULT 15 Java_com_tans_tmediaplayer_tMediaPlayer_getVideoFrameUBytesNative // ...
我們?cè)賮?lái)看看上面的那個(gè)崩潰棧地址 000000000001bd2c,我在 反編譯 .text 代碼(.text Section 就是用來(lái)存儲(chǔ)代碼的,通過(guò) objdump --dissassemble --section=.text [elf file] 命令可以反編譯機(jī)器碼到匯編) 后找到了這個(gè)地址所在的方法:
000000000001bb5c: 1bb5c: ff 83 01 d1 sub sp, sp, #96 // ... 1bd2c: 11 7a 00 94 bl 0x3a570 <abort@plt> 1bd30: 44 79 00 94 bl 0x3a240 <__stack_chk_fail@plt>
方法的指令有點(diǎn)長(zhǎng),我省略了大部分,我們看到 1bd2c 處調(diào)用了 abort() 方法,這個(gè)方法就是用來(lái)發(fā)送 SIGABORT 的,這是我測(cè)試時(shí)添加的,我們?cè)賮?lái)看看 1bb5c 在符號(hào)表中對(duì)應(yīng)的是哪個(gè)符號(hào),正好就是 _ZN19tMediaPlayerContext29parseDecodeVideoFrameToBufferEP18tMediaDecodeBuffer 方法,哈哈。
最后
本篇文章介紹了符號(hào)表,還通過(guò)崩潰棧中的地址,在符號(hào)表中去查詢到了我們對(duì)應(yīng)的方法,希望你對(duì) Native 的崩潰信息和符號(hào)表有一個(gè)全新的認(rèn)識(shí)。
以上就是Android解讀Native崩潰棧信息的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Android解讀Native崩潰棧信息的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android雙重SurfaceView實(shí)現(xiàn)彈幕效果
這篇文章主要為大家詳細(xì)介紹了Android雙重SurfaceView實(shí)現(xiàn)彈幕效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Android高級(jí)組件Gallery畫廊視圖使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android高級(jí)組件Gallery畫廊視圖的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android 調(diào)用notifyDataSetChanged方法失敗解決辦法
這篇文章主要介紹了Android 調(diào)用notifyDataSetChanged方法失敗解決辦法的相關(guān)資料,需要的朋友可以參考下2017-07-07
Android仿知乎懸浮功能按鈕FloatingActionButton效果
前段時(shí)間在看屬性動(dòng)畫,恰巧這個(gè)按鈕的效果可以用屬性動(dòng)畫實(shí)現(xiàn),下面通過(guò)本文給大家分享adroid仿知乎懸浮功能按鈕FloatingActionButton效果,需要的朋友參考下吧2017-04-04
Android實(shí)現(xiàn)紅包雨動(dòng)畫效果
本篇文章主要介紹了Android實(shí)現(xiàn)紅包雨動(dòng)畫效果,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07
Android實(shí)現(xiàn)列表元素動(dòng)態(tài)效果
本文將利用AnimatedList組件實(shí)現(xiàn)列表元素的一些動(dòng)態(tài)效果,例如添加元素時(shí)的漸現(xiàn)效果,刪除元素逐漸消失的效果等,感興趣的小伙伴可以了解一下2022-03-03
Android Studio EditText點(diǎn)擊圖標(biāo)清除文本內(nèi)容的實(shí)例解析
這篇文章主要介紹了Android Studio EditText點(diǎn)擊圖標(biāo)清除文本內(nèi)容的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
Android實(shí)現(xiàn)用戶登錄記住密碼功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)用戶登錄記住密碼功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
深入android中The connection to adb is
本篇文章是對(duì)android中The connection to adb is down的問(wèn)題以及解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05

