利用系统提供的崩溃日志解Native层Bug
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
對(duì)Android開(kāi)發(fā)者來(lái)講,尤其是使用NDK編寫(xiě)Native層代碼的開(kāi)發(fā)者,在編碼過(guò)程中通常會(huì)碰到各種各樣的問(wèn)題。追蹤問(wèn)題的方式有很多,除了在代碼中添加日志,來(lái)觀察程序運(yùn)行過(guò)程中產(chǎn)生的異常外,對(duì)崩潰后產(chǎn)生的日志進(jìn)行分析也是一種重要的定位問(wèn)題的方式。
?
Android系統(tǒng)自帶一個(gè)非常實(shí)用的Native層代碼崩潰監(jiān)測(cè)進(jìn)程debuggerd。該進(jìn)程可以監(jiān)聽(tīng)到應(yīng)用程序的崩潰,并將崩潰后的信息輸出到文件中,供開(kāi)發(fā)人員調(diào)試分析。在開(kāi)發(fā)過(guò)程中,我們可以通過(guò)logcat來(lái)查看debuggerd為我們生成的應(yīng)用程序崩潰日志。
?
接下來(lái),就跟大家來(lái)探討一下如何利用debuggerd為我們生成的應(yīng)用程序崩潰日志來(lái)定位并解決程序中存在的問(wèn)題。
?
首先通過(guò)這個(gè)例子來(lái)介紹如何使用系統(tǒng)提供的崩潰日志。代碼如圖1所示。
?
圖1 ?代碼示例
?
可以看到,JNI_OnLoad函數(shù)中除了做一些常規(guī)性的操作以外,有這樣兩行特殊的代碼:
[代碼]java代碼:
?
| 12 | int?*p =?0;*p =?1; |
p”是個(gè)int*類(lèi)型的空指針,而后面的這個(gè)賦值操作是向這個(gè)空指針的位置寫(xiě)入1。毫無(wú)疑問(wèn),這個(gè)典型的空指針賦值操作會(huì)造成應(yīng)用程序直接崩潰。
?
將以上代碼編譯成SO文件,并運(yùn)行APP。發(fā)現(xiàn)程序產(chǎn)生了崩潰,通過(guò)logcat就可以看到如圖2所示的崩潰日志:
?
圖2 ?系統(tǒng)生成的崩潰日志
?
從這一段崩潰日志中,我們可以看到:
?
(1)崩潰手機(jī)的Build fingerprint是:
'Huawei/H30-T00/hwH30-T00:4.4.2/HuaweiH30-T00/C00B246SP02:user/ota-rel-keys,release-keys'。
?
(2)崩潰發(fā)生的進(jìn)程進(jìn)程號(hào)pid為14077,線(xiàn)程號(hào)tid為14077,進(jìn)程名稱(chēng)為com.example.nativecrash。
(3)signal告訴我們:崩潰信號(hào)為SIGSEGV,當(dāng)進(jìn)程中執(zhí)行了一個(gè)無(wú)效的內(nèi)存引用時(shí)會(huì)觸發(fā)這個(gè)類(lèi)型的崩潰信號(hào)。fault addr代表發(fā)生崩潰的地址為0,這與代碼中的空指針賦值相吻合。
?
(4)signal下面的4行信息其實(shí)就是進(jìn)程崩潰時(shí)所有寄存器的一個(gè)快照。
?
(5)最后是backtrace,這部分對(duì)于定位產(chǎn)生崩潰的原因是非常重要的,它反應(yīng)了進(jìn)程崩潰時(shí)的函數(shù)調(diào)用棧,通過(guò)它,就可以知道在哪里觸發(fā)了代碼崩潰,稍后會(huì)詳細(xì)分析一下這個(gè)部分。
?
簡(jiǎn)單地解析了崩潰日志中的信息之后,我們就來(lái)看看怎么定位崩潰產(chǎn)生的位置?從上面的分析可以知道backtrace是接下來(lái)的分析重點(diǎn)。
圖3 ?backtrace中函數(shù)調(diào)用過(guò)程
?
如圖3所示,backtrace其實(shí)就是一個(gè)函數(shù)調(diào)用棧。最上面一行是崩潰發(fā)生的地方。從第一行可以看出:崩潰發(fā)生在自己so中的JNI_OnLoad函數(shù)里面,崩潰點(diǎn)是在so的偏移為0x47d2的地方(需要注意的是這個(gè)偏移是指二進(jìn)制文件中的偏移,并非源碼中的偏移)。
?
那么JNI_OnLoad函數(shù)是由誰(shuí)調(diào)用的呢?從第二行就可以知道是系統(tǒng)的libdvm.so中的dvmLoadNativeCode這個(gè)函數(shù)調(diào)用了該so中的JNI_OnLoad函數(shù)。綜上可以看出,backtrace中的函數(shù)調(diào)用關(guān)系其實(shí)是由下而上,如圖3中的箭頭所示。
?
雖然已經(jīng)定位崩潰發(fā)生在so的JNI_OnLoad函數(shù)中,但是依然不知道具體發(fā)生在源碼文件的哪一行。為了能夠定位到崩潰發(fā)生在源碼文件的哪一行,故需要利用Android提供的工具:addr2line。利用該工具可以將backtrace中顯示的崩潰點(diǎn)與源碼聯(lián)系起來(lái),這樣就能還原崩潰在源碼中的位置。具體的還原步驟如下:
?
(1)取obj目錄下的libtest.so
我們知道,在利用ndk-build編譯so的時(shí)候會(huì)在jni的同級(jí)目錄下產(chǎn)生libs、obj兩個(gè)目錄,如圖4所示:
圖4 ?libs、obj目錄
這兩個(gè)目錄下面各有一個(gè)so,但是為什么要取obj目錄的,而不取libs目錄里面的呢?這是因?yàn)閚dk-build在生成so的時(shí)候,會(huì)生成兩份,一份是包含調(diào)試信息的so,放在obj目錄下面;一份是不包含調(diào)試信息的so,放在libs目錄下面。為了利用addr2line工具還原崩潰點(diǎn)在源碼中的位置,我們必須使用包含調(diào)試信息的so,所以需要obj目錄中的so。
?
(2)在cmd中運(yùn)行下面的命令
[代碼]java代碼:
?
| 1 | addr2line? -f? -e? libtest.so??0x47d2 |
其中,0x47d2就是我們之前所看到的崩潰點(diǎn)。
?
運(yùn)行完可以看到如圖5所示的結(jié)果:
圖5 ?addr2line運(yùn)行結(jié)果
從以上結(jié)果中可以看到:崩潰發(fā)生在JNI_OnLoad函數(shù)中,崩潰點(diǎn)對(duì)應(yīng)的源碼位置在test.cpp的第17行。那么結(jié)果真的是在17行嗎,我們可以驗(yàn)證一下:
?
圖6 ?C源碼中的崩潰點(diǎn)
如圖6所示,空指針賦值的操作正好就是在第17行,正是這個(gè)操作導(dǎo)致了崩潰。
?
至此,我們就基本知道了如何利用系統(tǒng)提供的崩潰日志來(lái)定位和解決Native層代碼的崩潰了。但是實(shí)際的情況比我們想象要復(fù)雜得多。以上方式只適用于開(kāi)發(fā)者在調(diào)試或測(cè)試階段來(lái)獲取崩潰日志,但當(dāng)開(kāi)發(fā)者將APP分發(fā)出去之后,APP運(yùn)行在用戶(hù)手機(jī)上時(shí)發(fā)生了崩潰,開(kāi)發(fā)者是看不到日志的。所以通常開(kāi)發(fā)者通過(guò)自己搭建崩潰日志服務(wù)平臺(tái),或集成第三方SDK的形式獲取應(yīng)用崩潰信息。每種方式各有利弊,今天介紹一個(gè)免SDK集成的工具——360加固保
360加固保推出了“崩潰日志”功能主要特點(diǎn)有:
?
(1)免SDK集成
目前有不少第三方Crash Report SDK,開(kāi)發(fā)者需要在自己的apk源碼中集成SDK,那么作為一個(gè)開(kāi)發(fā)者,他的開(kāi)發(fā)成本就相應(yīng)的增加了很多:
首先,開(kāi)發(fā)者必須學(xué)會(huì)如何使用SDK,閱讀SDK文檔,了解其中的API接口,之后才能調(diào)用SDK中的API。除此之外,開(kāi)發(fā)者必須得在Android Studio或者eclipse等IDE中配置好SDK的一些使用參數(shù)等,但是,這有時(shí)并不是一件簡(jiǎn)單的事,需要一定開(kāi)發(fā)成本。
?
再次,由于SDK本身也會(huì)存在一些問(wèn)題(比如Bug,需要優(yōu)化等),所以必然需要升級(jí)更新,那么開(kāi)發(fā)者為了能夠使用更優(yōu)質(zhì)的服務(wù),就必須緊跟SDK的版本升級(jí),不斷在自己的源碼中更改SDK,這其實(shí)是比較麻煩的事。
?
為了免去開(kāi)發(fā)者的開(kāi)發(fā)成本,360加固保推出免SDK應(yīng)用崩潰日志分析服務(wù)。無(wú)需任何開(kāi)發(fā)過(guò)程,只需上傳APP進(jìn)行應(yīng)用加固,2分鐘左右,即可掌握最全面的應(yīng)用崩潰信息。同時(shí),APP具備了防反編譯、防破解的能力,輕松提升APP安全性。
?
(2)提供Native層崩潰日志收集功能,且兼容性強(qiáng)
由于Native層崩潰日志收集的功能涉及Android系統(tǒng)較低層的一些系統(tǒng)機(jī)制,其內(nèi)容較復(fù)雜,實(shí)現(xiàn)難度較大。所以目前市場(chǎng)上能夠提供針對(duì)Native層的崩潰收集功能的廠商并不多。即使能夠提供Native層的崩潰日志收集,在兼容性方面也存在各種問(wèn)題。
?
360加固保自己實(shí)現(xiàn)了一套收集崩潰日志的接口,并不依賴(lài)系統(tǒng)本身提供的崩潰日志相關(guān)接口。這樣,即便某款手機(jī)上的Android系統(tǒng)是基于手機(jī)廠商特殊定制的,不提供崩潰日志相關(guān)接口,360加固保也能正常收集到此款手機(jī)上面的崩潰信息。
?
(3)Native層收集的崩潰數(shù)據(jù)更加全面
與其他第三方應(yīng)用崩潰信息廠商相比,360加固保收集的Native層崩潰日志數(shù)據(jù)更全面,更方便開(kāi)發(fā)者根據(jù)這些崩潰信息進(jìn)行Bug追蹤和修復(fù)。360加固保收集的崩潰日志信息見(jiàn)圖7至圖11。
?
圖8 ?加固保收集的應(yīng)用崩潰信息
?
圖8 ?加固保收集的應(yīng)用崩潰信息
?
圖9 ?加固保收集的應(yīng)用崩潰信息
?
圖10 ?加固保收集的應(yīng)用崩潰信息
?
圖11 ?360加固保收集的應(yīng)用崩潰信息
?
(4)Native層崩潰日志模擬Android系統(tǒng)收集的崩潰日志,開(kāi)發(fā)者閱讀時(shí)更方便
Android系統(tǒng)本身自帶崩潰日志收集功能,圖12至圖14是Android系統(tǒng)為崩潰進(jìn)程收集的崩潰日志詳情:
圖12 ?Android系統(tǒng)收集的崩潰日志詳情
?
圖13 ?Android系統(tǒng)收集的崩潰日志詳情
?
?
圖14 ?Android系統(tǒng)收集的崩潰日志詳情
通過(guò)與360加固保收集到的崩潰信息對(duì)比,可以發(fā)現(xiàn),360加固保提供的崩潰日志詳情和Android系統(tǒng)收集的基本沒(méi)有區(qū)別,甚至比某些手機(jī)系統(tǒng)上面收集的崩潰日志還要詳細(xì)。由于開(kāi)發(fā)者在平時(shí)調(diào)試Native層代碼時(shí),習(xí)慣看到的是Android系統(tǒng)打出來(lái)的Log,所以如果與Android系統(tǒng)收集上來(lái)崩潰日志詳情的相似,可以說(shuō)開(kāi)發(fā)者看上去會(huì)非常親切,免去開(kāi)發(fā)者重新學(xué)習(xí)如何查看的煩惱。
?
(5)所需要獲取的apk權(quán)限最少
因?yàn)槭占降膽?yīng)用崩潰日志需要上傳至服務(wù)器,所以APP必須有一些網(wǎng)絡(luò)相關(guān)權(quán)限。圖15是360加固保所需獲取的apk權(quán)限,只有三個(gè)。更少的權(quán)限對(duì)用戶(hù)來(lái)講,就意味著少了很多的安全風(fēng)險(xiǎn);對(duì)于開(kāi)發(fā)者來(lái)講,也不必因?yàn)槭占罎⑿畔⒍砑舆^(guò)多的apk本身并不使用的權(quán)限。
?
圖14 ??360加固保所需獲取的apk權(quán)限
?
(6)Native層支持多進(jìn)程的崩潰日志收集
360加固保提供的崩潰日志分析服務(wù),無(wú)論是Android中動(dòng)態(tài)鏈接庫(kù)so中fork出來(lái)的進(jìn)程,還是Service組件的進(jìn)程,凡是Native層出現(xiàn)了崩潰,都會(huì)被Native層崩潰信息收集功能察覺(jué),并生成相應(yīng)的崩潰信息。
原文鏈接:http://www.apkbus.com/blog-705730-62583.html
作者:白衣染霜花
鏈接:http://www.imooc.com/article/247064
來(lái)源:慕課網(wǎng)
轉(zhuǎn)載于:https://my.oschina.net/u/4000302/blog/3027721
總結(jié)
以上是生活随笔為你收集整理的利用系统提供的崩溃日志解Native层Bug的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: npm 包安装位置
- 下一篇: python爬取主播信息