NDK撩妹三部曲(四)—NDK 开发如何优雅的定位 Native 异常,看这篇就够了
NDK 開發(fā)如何優(yōu)雅的定位 Native 異常,看這篇就夠了
- 從何說起?
- 摘要
- 案例實操
- aaddr2line
- objdump
- ndk-stack
- 1、假設(shè)我們已經(jīng)通過 adb logcat 拿到了程序崩潰的日志信息。
- 2、我們沒有 logcat 日志,但是有錯誤堆棧
- 集成了騰訊 bugly 的 Native 項目線上崩潰了怎么辦
從何說起?
??上周拿出 1/10 的本領(lǐng)教會妹子入門了 NDK之后,妹子QQ上留言給我說她還不滿足。一瞬間我有點懷疑自己了,“年少有為,血?dú)夥絼?#xff0c;8塊腹肌,一頭濃發(fā)都滿足不了妹子,難道我沒有自己想的那么猛嗎”?臥槽跑題了,咳咳。
??被妹子這么一說我肯定不舒服,就跑去問妹子了。“小愛,上周我們學(xué)的東西你都消化啦”?“嗯嗯,Q哥,我已經(jīng)學(xué)得差不多了,你快教我一點新東西嘛~”,雖然聽的我是一身的雞皮疙瘩,但是本著助人為樂,共同學(xué)習(xí),分享快樂的宗旨,我決定再從我已經(jīng)不多的庫存中忍痛割愛,好歹也要對的起妹子8塊錢的“奈雪の茶”。
摘要
??喏,不管你是 Android 新手,或者說已經(jīng)是職場老司機(jī),如果你還不知道怎么定位工作學(xué)習(xí)中遇到的 Native 異常,別怕,看完這篇后就再也不怕定位不到問題了。
??在 Android 開發(fā)中我們常常碰見程序閃退的情況,應(yīng)用層的異常最常見的就是 java.lang.NullPointerException,java.lang.IndexOutOfBoundsException,java.lang.NumberFormatException 等等,這些問題都很好定位,日志也全。而假如某次使用第三方 so 庫的時候報錯了,那就很讓人抓狂了。因為 so 庫一般都是 C 或 C++ 寫的,對內(nèi)存管理不好的同學(xué),就會莫名其妙的出現(xiàn)野指針錯誤、內(nèi)存訪問錯誤、越界錯誤等等。那今天,學(xué)會下面這幾個方法后,你就可以找到 so 庫的開發(fā),高傲的吐槽一波他們了(狗頭)。
??其實 NDK 早已經(jīng)幫我們想到了,在它的安裝目錄下有3款工具:arm-linux-androideabi-addr2line.exe ,arm-linux-androideabi-objdump.exe,ndk-stack.exe。前兩個工具的前綴 “arm”根據(jù)不同的 ABI 平臺不同,比如我們項目打的 armeabi-v8a 包,就是 aarch64-linux-android-addr2line.exe 和 aarch64-linux-android-objdump.exe。
- aaddr2line 是標(biāo)準(zhǔn) GNU tools 工具家族中的一部分,常用來將指令的地址和可執(zhí)行映像轉(zhuǎn)換成文件名、函數(shù)名和源代碼行數(shù)。
- objdump 是 linux 下的反匯編工具,常常用來反匯編二進(jìn)制文件以分析其中的附加信息。
- ndk-stack 從 ndk r6版本就已經(jīng)引入,從 ndk r20 開始不再已 exe的形式存在,而是借助 python 腳本的形式存在,ndk-stack 可以幫助開發(fā)者 過濾 adb logcat 的堆棧跟蹤信息,并可以把不認(rèn)識的內(nèi)存地址信息轉(zhuǎn)換成可讀的信息。
有了這3個神器,接下來就讓我們跟著 Demo 實操一波。
案例實操
aaddr2line
下面的代碼中我們定義了一個簡單的test方法,在第三行有一個空指針 pvalue ,然后在第4行去訪問這個空指針的下標(biāo),然后導(dǎo)出 so 庫,到 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 }很明顯這是一個空指針錯誤,不出意外會看到下面的錯誤信息:
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)其實從上面的信息中已經(jīng)能看到 Cause: null pointer dereference 字眼,但是卻無法知道錯誤代碼的行數(shù)。
平臺是“arm64”,SIGSEGV 信號是 linux 系統(tǒng)發(fā)出的,錯誤碼 11 一般代表了 空指針引用或者多次釋放,linux 錯誤信號查看方法:
雖然我們知道 libsoTest.so 發(fā)生了錯誤,但還是不知道出錯在哪行,什么函數(shù)。
找到你安裝ndk中 addr2line 的路徑,打開 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:出錯 so 庫的路徑
00000000000005dc: so 庫出錯的匯編地址,上面以 0000000000 開頭的就是,后面都會用到這個地址
按下回車后,
addr2line 已經(jīng)很明確的告訴我們出錯在第 7 行了。
另外建議將 aarch64-linux-android-addr2line.exe 的絕對路徑添加到環(huán)境變量,這樣以后就不用去找這個東西了,
objdump
找到 objdump 的目錄,和 addr2line 在同級目錄下,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
??在上面輸出的匯編指令中找到出錯so庫的匯編地址 00000000000005dc,可以看到出錯地址介于空指針引用錯誤和 return 語句之前。
??我們這個案例屬于比較簡單的情況,大多數(shù)生產(chǎn)環(huán)境的代碼比這要復(fù)雜的多,因此生成的匯編代碼比較難懂,但是宗旨就是找出錯的匯編地址,找到后,離具體的方法名和函數(shù)就不遠(yuǎn)了。
ndk-stack
ndk-stack 的目的是幫助我們將晦澀難懂的匯編內(nèi)存信息轉(zhuǎn)換成能看懂的文件名+類名+函數(shù)名+行號信息。
ndk-stack 有兩種用法:
1、假設(shè)我們已經(jīng)通過 adb logcat 拿到了程序崩潰的日志信息。
這種情況一般是在開發(fā)中,或者是在測試過程中測試同學(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
可以看到出錯的文件名和行號。
2、我們沒有 logcat 日志,但是有錯誤堆棧
比如線上環(huán)境,是沒有 logcat 日志的,但是假如集成了 bugly 等工具,也是可以拿到錯誤堆棧的。然后將堆棧信息復(fù)制到 txt 文件中,并在文件的開頭加上:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***因為 ndk-stack 會在開始解析 logcat 輸出時查找第一行星號。
集成了騰訊 bugly 的 Native 項目線上崩潰了怎么辦
我們的項目集成了騰訊的 bugly ,在線上版本發(fā)生崩潰后會上報堆棧信息到頁面,但和本地一樣,某些堆棧信息根本看不出來問題出到哪兒了。
bugly 支持 so 庫符號表上傳,具體參考:
Bugly Android 符號表配置
1、查看線上版本 so庫的 UUID
2、找到本地對應(yīng)版本的 so 庫,并通過 bugly 提供的工具查看 UUID 是否對應(yīng)
這兒有個前提條件,就是每次發(fā)版本前都要把我們打包的 so 庫的 debug 版本保存一份,這樣以后出現(xiàn)問題才能定位到問題在哪兒,否則 so 庫版本不對應(yīng)符號表就查不到了。
3、下載上圖中的符號表工具,并解壓,然后在 setting.txt 中配置 bugly 的 ID 和 Key。
4、cmd 輸入如下指令:
上面的命令是生成 so 庫對應(yīng)的符號表文件,解壓生成的 zip 后得到后綴是 .symbol 的文件,即為 bugly要求的符號表文件。然后使用文本打開此文件,可以看到這個 so 庫的 UUID。
5、如果 UUID 和 bugly 頁面上的 UUID 對應(yīng),即代表此 so 庫版本為線上的版本。
然后將上面生成的 zip 文件上傳到 bugly 符號表頁面:
完成稍等一會兒應(yīng)用成功后,再次打開我們的崩潰信息,可以看到已經(jīng)可以定位到具體的類和行數(shù)了。
csdn地址:http://blog.csdn.net/u012534831
github地址:https://github.com/qht1003077897
如有幫助,請多多點贊支持。
總結(jié)
以上是生活随笔為你收集整理的NDK撩妹三部曲(四)—NDK 开发如何优雅的定位 Native 异常,看这篇就够了的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python打九九乘法表上三角下三角_p
- 下一篇: echarts 制作图表固定的三个步骤