NDK撩妹三部曲(四)—NDK 开发如何优雅的定位 Native 异常,看这篇就够了
NDK 開(kāi)發(fā)如何優(yōu)雅的定位 Native 異常,看這篇就夠了
- 從何說(shuō)起?
- 摘要
- 案例實(shí)操
- aaddr2line
- objdump
- ndk-stack
- 1、假設(shè)我們已經(jīng)通過(guò) adb logcat 拿到了程序崩潰的日志信息。
- 2、我們沒(méi)有 logcat 日志,但是有錯(cuò)誤堆棧
- 集成了騰訊 bugly 的 Native 項(xiàng)目線上崩潰了怎么辦
從何說(shuō)起?
??上周拿出 1/10 的本領(lǐng)教會(huì)妹子入門(mén)了 NDK之后,妹子QQ上留言給我說(shuō)她還不滿(mǎn)足。一瞬間我有點(diǎn)懷疑自己了,“年少有為,血?dú)夥絼?#xff0c;8塊腹肌,一頭濃發(fā)都滿(mǎn)足不了妹子,難道我沒(méi)有自己想的那么猛嗎”?臥槽跑題了,咳咳。
??被妹子這么一說(shuō)我肯定不舒服,就跑去問(wèn)妹子了。“小愛(ài),上周我們學(xué)的東西你都消化啦”?“嗯嗯,Q哥,我已經(jīng)學(xué)得差不多了,你快教我一點(diǎn)新東西嘛~”,雖然聽(tīng)的我是一身的雞皮疙瘩,但是本著助人為樂(lè),共同學(xué)習(xí),分享快樂(lè)的宗旨,我決定再?gòu)奈乙呀?jīng)不多的庫(kù)存中忍痛割?lèi)?ài),好歹也要對(duì)的起妹子8塊錢(qián)的“奈雪の茶”。
摘要
??喏,不管你是 Android 新手,或者說(shuō)已經(jīng)是職場(chǎng)老司機(jī),如果你還不知道怎么定位工作學(xué)習(xí)中遇到的 Native 異常,別怕,看完這篇后就再也不怕定位不到問(wèn)題了。
??在 Android 開(kāi)發(fā)中我們常常碰見(jiàn)程序閃退的情況,應(yīng)用層的異常最常見(jiàn)的就是 java.lang.NullPointerException,java.lang.IndexOutOfBoundsException,java.lang.NumberFormatException 等等,這些問(wèn)題都很好定位,日志也全。而假如某次使用第三方 so 庫(kù)的時(shí)候報(bào)錯(cuò)了,那就很讓人抓狂了。因?yàn)?so 庫(kù)一般都是 C 或 C++ 寫(xiě)的,對(duì)內(nèi)存管理不好的同學(xué),就會(huì)莫名其妙的出現(xiàn)野指針錯(cuò)誤、內(nèi)存訪問(wèn)錯(cuò)誤、越界錯(cuò)誤等等。那今天,學(xué)會(huì)下面這幾個(gè)方法后,你就可以找到 so 庫(kù)的開(kāi)發(fā),高傲的吐槽一波他們了(狗頭)。
??其實(shí) NDK 早已經(jīng)幫我們想到了,在它的安裝目錄下有3款工具:arm-linux-androideabi-addr2line.exe ,arm-linux-androideabi-objdump.exe,ndk-stack.exe。前兩個(gè)工具的前綴 “arm”根據(jù)不同的 ABI 平臺(tái)不同,比如我們項(xiàng)目打的 armeabi-v8a 包,就是 aarch64-linux-android-addr2line.exe 和 aarch64-linux-android-objdump.exe。
- aaddr2line 是標(biāo)準(zhǔn) GNU tools 工具家族中的一部分,常用來(lái)將指令的地址和可執(zhí)行映像轉(zhuǎn)換成文件名、函數(shù)名和源代碼行數(shù)。
- objdump 是 linux 下的反匯編工具,常常用來(lái)反匯編二進(jìn)制文件以分析其中的附加信息。
- ndk-stack 從 ndk r6版本就已經(jīng)引入,從 ndk r20 開(kāi)始不再已 exe的形式存在,而是借助 python 腳本的形式存在,ndk-stack 可以幫助開(kāi)發(fā)者 過(guò)濾 adb logcat 的堆棧跟蹤信息,并可以把不認(rèn)識(shí)的內(nèi)存地址信息轉(zhuǎn)換成可讀的信息。
有了這3個(gè)神器,接下來(lái)就讓我們跟著 Demo 實(shí)操一波。
案例實(shí)操
aaddr2line
下面的代碼中我們定義了一個(gè)簡(jiǎn)單的test方法,在第三行有一個(gè)空指針 pvalue ,然后在第4行去訪問(wèn)這個(gè)空指針的下標(biāo),然后導(dǎo)出 so 庫(kù),到 android 下執(zhí)行。
1 #include"test.h" 2 #include<iostream> 3 using namespace std; 4 int TESTSHARED_EXPORT test() 5 { 6 int *pvalue = nullptr; 7 int temp = value[5]; 8 return temp; 9 }很明顯這是一個(gè)空指針錯(cuò)誤,不出意外會(huì)看到下面的錯(cuò)誤信息:
06-09 10:19:07.202 27963-27963/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***Build fingerprint: 'Xiaomi/umi/umi:10/QKQ1.191117.002/V11.0.24.0.QJBCNXM:user/release-keys'Revision: '0'ABI: 'arm64'Timestamp: 2020-06-09 10:19:07+0800pid: 27929, tid: 27929, name: com.qht.jnatest >>> com.qht.jnatest <<<uid: 10227signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x16Cause: null pointer dereferencex0 0000000000000014 x1 00000000000001f4 x2 0000000000000000 x3 0000000000000000x4 0000000000000000 x5 0000000000000000 x6 0000000000000000 x7 0000000000000000x8 0000000000000002 x9 86bc442885ebe4dc x10 0000000000430000 x11 000000000000001ax12 000000000c25b618 x13 000000000048ed40 x14 0000000000000006 x15 ffffffffffffffffx16 0000007c7defdf70 x17 0000007d6cb81440 x18 0000007d6f318000 x19 0000007ff52e9278x20 0000007ff52e9700 x21 0000007ff52e9290 x22 0000007ff52e9278 x23 0000000000000000x24 0000007c7dea05cc x25 0000000000000010 x26 00000000000000c9 x27 0000007c7defe218x28 0000007d6eaccb80 x29 0000007ff52e9200sp 0000007ff52e91b0 lr 0000007c7dee7e74 pc 0000007c7dea05dc 06-09 10:19:07.516 27963-27963/? A/DEBUG: backtrace:#00 pc 00000000000005dc /data/app/com.qht.jnatest-olKAAEfG2oMHNZvfzn8-SQ==/lib/arm64/libsoTest.so (test+16) (BuildId: 11d09b93a1d89acbc83015d06a5fca2c3bc72adf)#01 pc 000000000000fe70 /data/app/com.qht.jnatest-olKAAEfG2oMHNZvfzn8-SQ==/lib/arm64/libjnidispatch.so (ffi_call_SYSV+96)#02 pc 000000000000f660 /data/app/com.qht.jnatest-olKAAEfG2oMHNZvfzn8-SQ==/lib/arm64/libjnidispatch.so (ffi_call+292)#03 pc 0000000000005b80 /data/app/com.qht.jnatest-olKAAEfG2oMHNZvfzn8-SQ==/lib/arm64/libjnidispatch.so#04 pc 00000000000079ec /data/app/com.qht.jnatest-olKAAEfG2oMHNZvfzn8-SQ==/lib/arm64/libjnidispatch.so (Java_com_sun_jna_Native_invokeInt+32)#05 pc 0000000000140350 /apex/com.android.runtime/lib64/libart.so (art_quick_generic_jni_trampoline+144) (BuildId: 112fa750f6a9adbd7b599e735b27a900)#06 pc 00000000001375b8 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_static_stub+568) (BuildId: 112fa750f6a9adbd7b599e735b27a900)其實(shí)從上面的信息中已經(jīng)能看到 Cause: null pointer dereference 字眼,但是卻無(wú)法知道錯(cuò)誤代碼的行數(shù)。
平臺(tái)是“arm64”,SIGSEGV 信號(hào)是 linux 系統(tǒng)發(fā)出的,錯(cuò)誤碼 11 一般代表了 空指針引用或者多次釋放,linux 錯(cuò)誤信號(hào)查看方法:
雖然我們知道 libsoTest.so 發(fā)生了錯(cuò)誤,但還是不知道出錯(cuò)在哪行,什么函數(shù)。
找到你安裝ndk中 addr2line 的路徑,打開(kāi) cmd 輸入下面的命令(aarch64-linux-android-addr2line.exe --help 查看幫助):
D:/Java/android-ndk-r20/toolchains/aarch64-linux-android-4.9/prebuilt/windows-x86_64/bin/aarch64-linux-android-addr2line.exe -e libsoTest.so 00000000000005dc-e:出錯(cuò) so 庫(kù)的路徑
00000000000005dc: so 庫(kù)出錯(cuò)的匯編地址,上面以 0000000000 開(kāi)頭的就是,后面都會(huì)用到這個(gè)地址
按下回車(chē)后,
addr2line 已經(jīng)很明確的告訴我們出錯(cuò)在第 7 行了。
另外建議將 aarch64-linux-android-addr2line.exe 的絕對(duì)路徑添加到環(huán)境變量,這樣以后就不用去找這個(gè)東西了,
objdump
找到 objdump 的目錄,和 addr2line 在同級(jí)目錄下,cmd 輸入如下指令(aarch64-linux-android-objdump.exe --help查看幫助):
D:/Java/android-ndk-r20/toolchains/aarch64-linux-android-4.9/prebuilt/windows-x86_64/bin/aarch64-linux-android-objdump.exe -S libsoTest.so > libsoTest.txt
??在上面輸出的匯編指令中找到出錯(cuò)so庫(kù)的匯編地址 00000000000005dc,可以看到出錯(cuò)地址介于空指針引用錯(cuò)誤和 return 語(yǔ)句之前。
??我們這個(gè)案例屬于比較簡(jiǎn)單的情況,大多數(shù)生產(chǎn)環(huán)境的代碼比這要復(fù)雜的多,因此生成的匯編代碼比較難懂,但是宗旨就是找出錯(cuò)的匯編地址,找到后,離具體的方法名和函數(shù)就不遠(yuǎn)了。
ndk-stack
ndk-stack 的目的是幫助我們將晦澀難懂的匯編內(nèi)存信息轉(zhuǎn)換成能看懂的文件名+類(lèi)名+函數(shù)名+行號(hào)信息。
ndk-stack 有兩種用法:
1、假設(shè)我們已經(jīng)通過(guò) adb logcat 拿到了程序崩潰的日志信息。
這種情況一般是在開(kāi)發(fā)中,或者是在測(cè)試過(guò)程中測(cè)試同學(xué)幫助我們保存了 logcat 日志。
D:/Java/android-ndk-r16-windows-x86_64/android-ndk-r16/prebuilt/windows-x86_64/bin/ndk-stack --sym D:/WorkSoftware/AndroidWorkSpace/jnatest/app/libs/arm64-v8a --dump log1.txt
可以看到出錯(cuò)的文件名和行號(hào)。
2、我們沒(méi)有 logcat 日志,但是有錯(cuò)誤堆棧
比如線上環(huán)境,是沒(méi)有 logcat 日志的,但是假如集成了 bugly 等工具,也是可以拿到錯(cuò)誤堆棧的。然后將堆棧信息復(fù)制到 txt 文件中,并在文件的開(kāi)頭加上:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***因?yàn)?ndk-stack 會(huì)在開(kāi)始解析 logcat 輸出時(shí)查找第一行星號(hào)。
集成了騰訊 bugly 的 Native 項(xiàng)目線上崩潰了怎么辦
我們的項(xiàng)目集成了騰訊的 bugly ,在線上版本發(fā)生崩潰后會(huì)上報(bào)堆棧信息到頁(yè)面,但和本地一樣,某些堆棧信息根本看不出來(lái)問(wèn)題出到哪兒了。
bugly 支持 so 庫(kù)符號(hào)表上傳,具體參考:
Bugly Android 符號(hào)表配置
1、查看線上版本 so庫(kù)的 UUID
2、找到本地對(duì)應(yīng)版本的 so 庫(kù),并通過(guò) bugly 提供的工具查看 UUID 是否對(duì)應(yīng)
這兒有個(gè)前提條件,就是每次發(fā)版本前都要把我們打包的 so 庫(kù)的 debug 版本保存一份,這樣以后出現(xiàn)問(wèn)題才能定位到問(wèn)題在哪兒,否則 so 庫(kù)版本不對(duì)應(yīng)符號(hào)表就查不到了。
3、下載上圖中的符號(hào)表工具,并解壓,然后在 setting.txt 中配置 bugly 的 ID 和 Key。
4、cmd 輸入如下指令:
上面的命令是生成 so 庫(kù)對(duì)應(yīng)的符號(hào)表文件,解壓生成的 zip 后得到后綴是 .symbol 的文件,即為 bugly要求的符號(hào)表文件。然后使用文本打開(kāi)此文件,可以看到這個(gè) so 庫(kù)的 UUID。
5、如果 UUID 和 bugly 頁(yè)面上的 UUID 對(duì)應(yīng),即代表此 so 庫(kù)版本為線上的版本。
然后將上面生成的 zip 文件上傳到 bugly 符號(hào)表頁(yè)面:
完成稍等一會(huì)兒應(yīng)用成功后,再次打開(kāi)我們的崩潰信息,可以看到已經(jīng)可以定位到具體的類(lèi)和行數(shù)了。
csdn地址:http://blog.csdn.net/u012534831
github地址:https://github.com/qht1003077897
如有幫助,請(qǐng)多多點(diǎn)贊支持。
總結(jié)
以上是生活随笔為你收集整理的NDK撩妹三部曲(四)—NDK 开发如何优雅的定位 Native 异常,看这篇就够了的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python打九九乘法表上三角下三角_p
- 下一篇: echarts 制作图表固定的三个步骤