Android解讀Native崩潰棧信息的方法詳解
大部分的 Android
開發(fā)者使用的主要語言都是 Kotlin / Java
,他們的崩潰棧信息非常清晰,也非常好定位到問題,如果是線上的崩潰通常還會對類名進行混淆,還需要一個混淆文件對堆棧翻譯一下就能夠得到源碼中的類名。
但是很多人對 C/C++
的崩潰棧就無能為力了,今天這篇文章就來扒一扒 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
中的崩潰是通過系統(tǒng)信號來實現(xiàn)的,比如我們上面的異常信號就是 SIGABRT
,Android
的進程在啟動時就會添加一個 SignalCatcher
,來捕獲信號,不同的信號有不同的處理方式,SIGABRT
就是會直接退出程序,也就是我們常說的崩潰,Android
中還有一個非常重要的信號就是 SIGQUIT
,在 Android
中表示發(fā)生了 ANR
,默認的處理邏輯是 dump
棧信息和內(nèi)存 GC
相關(guān)的信息到本地文件。
好了這里說得有點遠了,回到上面的問題,我們剛開始看到上面的數(shù)據(jù)可能有點懵逼,pc
后面有一串 16 進制的數(shù)字表示程序計數(shù)器的位置 (簡單理解就是執(zhí)行的機器碼對應(yīng)的位置),后面的文件表示崩潰的棧中相關(guān)的 .so
動態(tài)鏈接庫。但是你又要說了,這一串地址誰能夠看出什么問題?????? 你先不要急,通常線上的用戶崩潰看到的棧是這樣的,因為 Android
的 Release
包默認會抹掉一部分叫做符號表的東西,如果你看過我上面的文章你就會豁然開朗,這個符號表描述了指令地址和對應(yīng)方法或者變量的映射(方法名,全局變量名都是符號),通常我們用的別人的 .so
包也會抹掉符號表(這可能就是不想讓你看,起到了一個混淆作用),少了一個表在線上的運行中性能會更加好(至少這部分內(nèi)存不用消耗了)。
通常我們自己打的 Debug
包就沒有抹掉符號表,如果是有符號表信息,我們看到的上面異常信息通常是下面這樣的:
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)用所對應(yīng)的方法了,因為我這里的 decode()
方法是遞歸調(diào)用的,所以你看到上面的棧中有多個,方法后面還有一個 +532
表示該地址離方法開始的地址的偏移量,如果你的 .so
文件里面還有 debug
信息,這個 +532
能夠定位到某一行 C \ C++
源碼,其實就是每條指令都映射了某一行代碼。
在 Android
的打包過程中如果你希望 Release
包也不要移除符號表信息,可以通過在 build.gradle
中添加以下配置來避免符號表被移除:
// ... packagingOptions { doNotStrip "*/arm64-v8a/*.so" doNotStrip "*/armeabi-v7a/*.so" doNotStrip "*/x86/*.so" doNotStrip "*/x86_64/*.so" } // ...
如果符號表被移除了那我們不是永遠都不知道崩潰的方法是什么了?當然不是,被移除的符號會被保存到另外的文件中,線上的崩潰可以通過這個文件再次翻譯成對應(yīng)的方法。以下就是符號文件對應(yīng)的路徑:
它解壓后如下:
他們也是 ELF
格式的文件,每個都對應(yīng)了一個 .so
庫。
那么我們怎么判斷一個 .so
文件是不是有符號表呢?可以通過 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
就表示有符號表。
libtmediaplayer.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=58ab2061a06db613d9c3ca66a214872ad88636f7, stripped
stripped
就表示沒有符號表。
Tips: 如果你上架的應(yīng)用沒有這個符號表,Google Play
還會提醒你上傳,Google Play
是可以幫你捕獲 Native
崩潰的,它需要符號表解析這些地址信息。
符號表
我們了解 ELF
文件就知道,我們上面說的符號表就是本地符號表對應(yīng)的就是 .symtab
Section
,這個表對我們的程序運行沒有任何的影響,我們調(diào)用本地方法都是通過地址直接跳轉(zhuǎn),而不需要本地符號表。
還有一個符號表是 .dynsym
,它是動態(tài)鏈接的符號表,這個表是供 ld.so
使用的,因為這里面的符號都是暴露給其他的程序調(diào)用的,ld.so
需要通過這個表去查詢暴露給其他程序的符號的地址,所以不能刪除。
我們可以通過 readelf -s [elf file]
讀取符號表:
以下是有符號表的數(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 // ...
如果沒有本地符號表就只有以下信息(少了 .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 // ...
我們再來看看上面的那個崩潰棧地址 000000000001bd2c
,我在 反編譯 .text
代碼(.text
Section
就是用來存儲代碼的,通過 objdump --dissassemble --section=.text [elf file]
命令可以反編譯機器碼到匯編) 后找到了這個地址所在的方法:
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>
方法的指令有點長,我省略了大部分,我們看到 1bd2c
處調(diào)用了 abort()
方法,這個方法就是用來發(fā)送 SIGABORT
的,這是我測試時添加的,我們再來看看 1bb5c
在符號表中對應(yīng)的是哪個符號,正好就是 _ZN19tMediaPlayerContext29parseDecodeVideoFrameToBufferEP18tMediaDecodeBuffer
方法,哈哈。
最后
本篇文章介紹了符號表,還通過崩潰棧中的地址,在符號表中去查詢到了我們對應(yīng)的方法,希望你對 Native
的崩潰信息和符號表有一個全新的認識。
以上就是Android解讀Native崩潰棧信息的方法詳解的詳細內(nèi)容,更多關(guān)于Android解讀Native崩潰棧信息的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android中CountDownTimer 實現(xiàn)倒計時功能
本篇文章主要介紹了Android中CountDownTimer 實現(xiàn)倒計時功能,CountDownTimer 是android 自帶的一個倒計時類,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05Android 7.0以上版本實現(xiàn)應(yīng)用內(nèi)語言切換的方法
本篇文章主要介紹了Android 7.0以上版本實現(xiàn)應(yīng)用內(nèi)語言切換的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02android顯示TextView文字的倒影效果實現(xiàn)代碼
這篇文章主要介紹了android顯示TextView文字的倒影效果實現(xiàn)代碼,需要的朋友可以參考下2014-02-02Android抽屜導航Navigation Drawer實例解析
這篇文章主要為大家詳細介紹了Android抽屜導航NavigationDrawer實例,感興趣的小伙伴們可以參考一下2016-05-05Android ContentProvider實現(xiàn)手機聯(lián)系人讀取和插入
這篇文章主要為大家詳細介紹了Android ContentProvider實現(xiàn)手機聯(lián)系人讀取和插入,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-05-05flutter TextField換行自適應(yīng)的實現(xiàn)
這篇文章主要介紹了flutter TextField換行自適應(yīng)的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-02-02Android使用onCreateOptionsMenu()創(chuàng)建菜單Menu的方法詳解
這篇文章主要介紹了Android使用onCreateOptionsMenu()創(chuàng)建菜單Menu的方法,結(jié)合實例形式較為詳細的分析了Android基于onCreateOptionsMenu創(chuàng)建菜單的具體步驟與相關(guān)操作技巧,需要的朋友可以參考下2016-11-11