【移動(dòng)應(yīng)用開發(fā)技術(shù)】干貨安卓APP崩潰捕獲方案-xCrash_第1頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】干貨安卓APP崩潰捕獲方案-xCrash_第2頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】干貨安卓APP崩潰捕獲方案-xCrash_第3頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】干貨安卓APP崩潰捕獲方案-xCrash_第4頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】干貨安卓APP崩潰捕獲方案-xCrash_第5頁(yè)
已閱讀5頁(yè),還剩4頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

【移動(dòng)應(yīng)用開發(fā)技術(shù)】干貨安卓APP崩潰捕獲方案——xCrash

導(dǎo)讀2019年,愛奇藝在GitHub上開源了xCrash。這是一個(gè)比較完整的安卓APP崩潰捕獲SDK,它能在App進(jìn)程崩潰時(shí),在你指定的目錄中生成tombstone文件(格式與系統(tǒng)的tombstone文件類似)。它支持捕獲native崩潰和Java崩潰;支持安卓4.0-9.0;支持armeabi,armeabi-v7a,arm64-v8a,x86和x86_64。依托于愛奇藝安卓APP上億的日活用戶數(shù)據(jù),xCrash在兼容性、穩(wěn)定性、功能完整性等方面不斷地自我完善。目前xCrash已被應(yīng)用于愛奇藝、愛奇藝極速版、愛奇藝動(dòng)畫屋、奇秀、愛奇藝VR影院、叭噠漫畫等20余款愛奇藝的安卓APP中。問題概述在移動(dòng)端APP的各種質(zhì)量問題中,最嚴(yán)重的可能就是APP崩潰閃退了。從安卓APP開發(fā)的角度,Java崩潰捕獲相對(duì)比較容易,JVM給Java字節(jié)碼提供了一個(gè)受控的運(yùn)行環(huán)境,同時(shí)也提供了完善的Java崩潰捕獲機(jī)制。Native崩潰的捕獲和處理相對(duì)比較困難,安卓系統(tǒng)的debuggerd

守護(hù)進(jìn)程會(huì)為native崩潰自動(dòng)生成詳細(xì)的崩潰描述文件(tombstone)。在開發(fā)調(diào)試階段,可以通過系統(tǒng)提供的

bugreport

工具獲取tombstone文件(或者將設(shè)備root后也可以拿到)。但是對(duì)于發(fā)布到線上的安卓APP,如何獲取tombstone文件,安卓操作系統(tǒng)本身并沒有提供這樣的功能。這個(gè)問題一直是安卓native崩潰分析和移動(dòng)端APM系統(tǒng)的痛點(diǎn)之一。Native崩潰介紹信號(hào)Native崩潰發(fā)生在機(jī)器指令運(yùn)行的層面。比如:APP中的so庫(kù)、系統(tǒng)的so庫(kù)、JVM本身等等。如果這部分程序做了Linuxkernel認(rèn)為不可接受的事情(比如:除數(shù)為零、讓CPU執(zhí)行它無法識(shí)別的指令等),kernel就會(huì)向APP中對(duì)應(yīng)的線程發(fā)送相應(yīng)的信號(hào)(signal),這些信號(hào)的默認(rèn)處理方式是殺死整個(gè)進(jìn)程。用戶態(tài)進(jìn)程也可以發(fā)送signal終止其他進(jìn)程或自身。這些致命的信號(hào)分為2類,主要有:1、kernel發(fā)出的:SIGFPE:除數(shù)為零。SIGILL:無法識(shí)別的CPU指令。SIGSYS:無法識(shí)別的系統(tǒng)調(diào)用(systemcall)。SIGSEGV:錯(cuò)誤的虛擬內(nèi)存地址訪問。SIGBUS:錯(cuò)誤的物理設(shè)備地址訪問。2、用戶態(tài)進(jìn)程發(fā)出的:SIGABRT:調(diào)用

abort()

/

kill()

/

tkill()

/

tgkill()

自殺,或被其他進(jìn)程通過

kill()

/

tkill()

/

tgkill()

他殺。信號(hào)處理函數(shù)Naive崩潰捕獲需要注冊(cè)這些信號(hào)的處理函數(shù)(signalhandler),然后在信號(hào)處理函數(shù)中收集數(shù)據(jù)。因?yàn)樾盘?hào)是以“中斷”的方式出現(xiàn)的,可能中斷任何CPU指令序列的執(zhí)行,所以在信號(hào)處理函數(shù)中,只能調(diào)用“異步信號(hào)安全(async-signal-safe)”的函數(shù)。例如malloc()、calloc()、free()、snprintf()、gettimeofday()

等等都是不能使用的,C++STL/boost也是不能使用的。所以,在信號(hào)處理函數(shù)中我們只能不分配堆內(nèi)存,需要使用堆內(nèi)存只能在初始化時(shí)預(yù)分配。如果要使用不在異步信號(hào)安全白名單中的libc/bionic函數(shù),只能直接調(diào)用systemcall或者自己實(shí)現(xiàn)。進(jìn)程崩潰前的極端情況當(dāng)崩潰捕獲邏輯開始運(yùn)行時(shí),會(huì)面對(duì)很多糟糕的情況,比如:棧溢出、堆內(nèi)存不可用、虛擬內(nèi)存地址耗盡、FD耗盡、Flash空間耗盡等。有時(shí),這些極端情況的出現(xiàn),本身就是導(dǎo)致進(jìn)程崩潰的間接原因。1、棧溢出我們需要預(yù)先用

sigaltstack()

為signalhandler分配專門的棧內(nèi)存空間,否則當(dāng)遇到棧溢出時(shí),signalhandler將無法正常運(yùn)行。2、虛擬內(nèi)存地址耗盡內(nèi)存泄露很容易導(dǎo)致虛擬內(nèi)存地址耗盡,特別是在32位環(huán)境中。這意味著在signalhandler中也不能使用類似

mmap()

的調(diào)用。3、FD耗盡FD泄露是常見的導(dǎo)致進(jìn)程崩潰的間接原因。這意味著在signalhandler中無法正常的使用依賴于FD的操作,比如無法

open()

+

read()

讀取/proc

中的各種信息。為了不干擾APP的正常運(yùn)行,我們僅僅預(yù)留了一個(gè)FD,用于在崩潰時(shí)可靠的創(chuàng)建出“崩潰信息記錄文件”。4、Flash空間耗盡在16G/32G存儲(chǔ)空間的安卓設(shè)備中,這種情況經(jīng)常發(fā)生。這意味著signalhandler無法把崩潰信息記錄到本地文件中。我們只能嘗試在初始化時(shí)預(yù)先創(chuàng)建一些“占坑”文件,然后一直循環(huán)使用這些“占坑”文件來記錄崩潰信息。如果“占坑”文件也創(chuàng)建失敗,我們需要把最重要的一些崩潰信息(比如backtrace)保存在內(nèi)存中,然后立刻回調(diào)和發(fā)送這些信息。xCrash架構(gòu)與實(shí)現(xiàn)信號(hào)處理函數(shù)與子進(jìn)程在信號(hào)處理函數(shù)(signalhandler)代碼執(zhí)行的開始階段,我們只能“忍辱偷生”:1、遵守它的各種限制。2、不使用堆內(nèi)存。3、自己實(shí)現(xiàn)需要的調(diào)用的“異步信號(hào)安全版本”,比如:snprintf()、gettimeofday()。4、必要時(shí)直接調(diào)用systemcall。但這并非長(zhǎng)久之計(jì),我們要盡快在信號(hào)處理函數(shù)中執(zhí)行“逃逸”,即使用clone()

+

execl()

創(chuàng)建新的子進(jìn)程,然后在子進(jìn)程中繼續(xù)收集崩潰信息。這樣做的目的是:1、避開async-signal-safe的限制。2、避開虛擬內(nèi)存地址耗盡的問題。3、避開FD耗盡的問題。4、使用

ptrace()

suspend崩潰進(jìn)程中所有的線程。與iOS不同,Linux/Android不支持suspend本進(jìn)程內(nèi)的線程。(如果不做suspend,則其他未崩潰的線程還在繼續(xù)執(zhí)行,還在繼續(xù)寫logcat,當(dāng)我們收集logcat時(shí),崩潰時(shí)間點(diǎn)附近的logcat可能早已被淹沒。類似的,其他的業(yè)務(wù)logbuffers也存在被淹沒的問題。)5、除了崩潰線程本身的registers、backtrace等,還能用

ptrace()收集到進(jìn)程中其他所有線程的registers、backtrace等信息,這對(duì)于某些崩潰問題的分析是有意義的。6、更安全的讀取內(nèi)存數(shù)據(jù)。(ptrace

讀數(shù)據(jù)失敗會(huì)返回錯(cuò)誤碼,但是在崩潰線程內(nèi)直接讀內(nèi)存數(shù)據(jù),如果內(nèi)存地址非法,會(huì)導(dǎo)致段錯(cuò)誤)由此可以看出“逃逸”是必然的選擇,整個(gè)過程如下圖所示:整體架構(gòu)xCrash整體分為兩部分:運(yùn)行于崩潰的APP進(jìn)程內(nèi)的部分,和獨(dú)立進(jìn)程的部分(我們稱為dumper)。1、APP進(jìn)程內(nèi)這部分可以再分為Java和native兩個(gè)部分。(1)Java部分:①Java崩潰捕獲。直接使用JVM提供的機(jī)制來完成,最后生成兼容tombstone格式的dump文件。②Native崩潰捕獲機(jī)制的注冊(cè)器。通過JNI激活native層的對(duì)應(yīng)機(jī)制。③Tombstone文件解析器??梢詫ombstone文件解析成json格式。④Tombstone文件管理器。可以檢索設(shè)備上已經(jīng)生成的tombstone文件。(2)Native部分:①JNIBridge。負(fù)責(zé)與Java層的交互。(傳參與回調(diào))②Signalhandlers。負(fù)責(zé)信號(hào)捕獲,以及啟動(dòng)獨(dú)立進(jìn)程dumper。③Fallbackmode。負(fù)責(zé)當(dāng)dumper捕獲崩潰信息失敗時(shí),嘗試在崩潰進(jìn)行的signalhandler中收集崩潰信息。2、Dumper獨(dú)立進(jìn)程這部分是純native的實(shí)現(xiàn):①Process。負(fù)責(zé)崩潰進(jìn)程中各個(gè)線程的控制(attach和detach),以及進(jìn)程層面的信息收集,比如FD列表、logcat等等。②Threads。負(fù)責(zé)崩潰進(jìn)程中的線程相關(guān)數(shù)據(jù)的收集,比如registers、backtrace、stack等等。③MemoryLayout。負(fù)責(zé)

maps

smaps

的解析。④Memory。負(fù)責(zé)各種內(nèi)存數(shù)據(jù)的讀寫。比如來自本地buffer、來自mmap()

的ELF文件、或者通過

ptrace()

遠(yuǎn)程訪問的崩潰進(jìn)程的內(nèi)存。⑤Registers。負(fù)責(zé)各種處理機(jī)架構(gòu)相關(guān)的數(shù)據(jù)處理。⑥ELF。負(fù)責(zé)ELF信息的解析。需要解析各種unwindtable和symbols信息,有時(shí)需要使用LZMA解壓.gnu_debugdata中的minidebuginfo信息做進(jìn)一步的處理。獲取backtrace獲取backtrace是崩潰捕獲中比較復(fù)雜和重要的部分,這也恰恰是安卓native開發(fā)中最混亂和不一致的地方之一。1、libc對(duì)backtrace的支持在Linux服務(wù)器環(huán)境中,當(dāng)那些致命的signal發(fā)生時(shí),系統(tǒng)可以為我們產(chǎn)生標(biāo)準(zhǔn)的coredump文件,之后我們可以用gdb調(diào)試和恢復(fù)崩潰現(xiàn)場(chǎng),我們俗稱“驗(yàn)尸”。在Linux嵌入式環(huán)境中,由于flash空間有限,我們一般可以注冊(cè)signalhandler,然后調(diào)用libc的

backtrace()

和backtrace_symbols_fd()

獲取backtrace。(注意:不能使用backtrace_symbols(),它不是異步信號(hào)安全的)2、NDK對(duì)backtrace的支持NDK中目前沒有提供可靠的unwindAPI。安卓使用bionic替代了libc,bionic中沒有

backtrace()

和backtrace_symbols_fd()。(它們不在POSIX標(biāo)準(zhǔn)中)unwind.h

中的

_Unwind_Backtrace

系列函數(shù)對(duì)于高版本Android系統(tǒng)庫(kù)幾乎無效。(NDK中的unwind實(shí)現(xiàn),已經(jīng)無法跟上Android系統(tǒng)快速的迭代優(yōu)化)3、GoogleAOSP的backtrace實(shí)現(xiàn)各版本的AOSP都有系統(tǒng)自用的backtrace庫(kù),主要作用是配合系統(tǒng)debuggerd進(jìn)程和調(diào)試器的工作。(1)libcorkscrew:只用于Android4.1-4.4W。(2)libunwind:只用于Android5.0-7.1.1。(3)libunwindstack:只用于Android8.0及以上版本。如果APP直接使用這些庫(kù),會(huì)遇到以下的問題:(1)系統(tǒng)debuggerd是以root權(quán)限運(yùn)行的,而我們的APP沒有root權(quán)限,所以某些操作會(huì)受到限制。(2)NDK沒有暴露這些系統(tǒng)庫(kù)的對(duì)外調(diào)用接口。Android7.0以后APP無法直接

dlopen()

系統(tǒng)庫(kù),所以其中的libunwind和libunwindstack只能自己編譯源碼后放到APP中使用。(3)使用這些庫(kù)的localunwind接口比較容易,但是使用remoteunwind接口時(shí)適配比較復(fù)雜。(原因還是這些庫(kù)是為了debuggerd和調(diào)試器設(shè)計(jì)的,不是為APP設(shè)計(jì)的)(4)高版本系統(tǒng)的backtrace庫(kù)無法直接編譯用于低版本的系統(tǒng),libunwind和libunwindstack中使用了大量低版本系統(tǒng)所沒有的系統(tǒng)函數(shù)。所以作為APP只能分別編譯這些系統(tǒng)backtrace庫(kù),然后在運(yùn)行時(shí)根據(jù)系統(tǒng)APIlevel動(dòng)態(tài)判斷需要使用哪個(gè)庫(kù)。這顯著的增加了APP包體積。4、xCrash的backtrace實(shí)現(xiàn)xCrash參考了一部分AOSP和BreakPad的實(shí)現(xiàn)思路,在不需要root權(quán)限和兼容Android4.0-9.0的前提下,自己實(shí)現(xiàn)了unwind邏輯。這樣做的好處是unwind過程不再是一個(gè)黑盒,細(xì)節(jié)完全可控,遇到問題完全可調(diào)試。Backtraceunwind依賴于三部分?jǐn)?shù)據(jù):寄存器、棧內(nèi)存、各ELF中的unwindtable。xCrash目前能處理Android4.0-9.0中可能出現(xiàn)的所有格式的unwindtable,它們來自于ELF中的以下section:(1).ARM.exidx(只存在于32位ARM架構(gòu))(2).eh_frame和.eh_frame_hdr(3).debug_frame(4).gnu_debugdata(LZMA壓縮的minidebuginfo,其中可能包含其他的unwindtable,比如:.debug_frame)xCrash的其他功能除了獲取常見的設(shè)備信息、registers、backtrace、stack、memorynear、maps、logcat等基本信息,xCrash還提供以下的功能:1、完整的FD列表讓你知道崩潰時(shí)進(jìn)程中的每一個(gè)FD具體都用在了哪里。2、詳細(xì)的內(nèi)存使用統(tǒng)計(jì)獲取了操作系統(tǒng)全局的物理內(nèi)存使用統(tǒng)計(jì)、崩潰進(jìn)程的虛擬內(nèi)存使用統(tǒng)計(jì)、崩潰進(jìn)程的內(nèi)存詳細(xì)使用信息(類似

dumpsysmeminfo)。讓你對(duì)進(jìn)程崩潰時(shí)的內(nèi)存狀態(tài)有全面的了解。3、用正則白名單設(shè)置需要獲取哪些線程的信息APP的線程數(shù)超過100個(gè)是很常見的,如果像系統(tǒng)tombstone那樣總是獲取全部線程的registers、backtrace等信息,在大多數(shù)情況下是沒有必要的;這也容易導(dǎo)致unwind時(shí)間過長(zhǎng),崩潰捕獲邏輯還沒有走完,APP就被系統(tǒng)強(qiáng)殺了。xCrash讓你能通過一組正則表達(dá)式白名單來設(shè)置需要獲取哪些線程的信息。4、零權(quán)限需求xCrash不需要root權(quán)限,也不需要任何的APP系統(tǒng)權(quán)限,這讓使用xCrash的APP沒有任何權(quán)限方面的負(fù)擔(dān)。5、監(jiān)測(cè)設(shè)備是否已被root監(jiān)測(cè)的過程是完全透明和無感知的。在后期分析數(shù)據(jù)時(shí),如果發(fā)現(xiàn)某個(gè)崩潰只發(fā)生在已被root的設(shè)備上,就有理由懷疑是否是一些特別的原因造成的。6、極高的崩潰信息捕獲成功率xCrash通過FD預(yù)留;Flash“占坑”文件;寫文件失敗時(shí)通過預(yù)分配內(nèi)存保存backtrace等重要信息做緊急回調(diào)、clone()

+

execl()

失敗后進(jìn)入fallback模式執(zhí)行本地unwind等一系列保護(hù)措施,最大程度的保證了崩潰信息捕獲的成功率。7、擴(kuò)展性支持xCrash支持崩潰后附加用戶自定義信息。目前在愛奇藝APP中,已經(jīng)通過xCrash的擴(kuò)展能力,在崩潰時(shí)投遞了大播放日志、彈幕日志、NLE視頻編輯日志、APPLifeCycleTrace等信息。為排查特定業(yè)務(wù)的崩潰問題提供支持。xCrash與BreakPad比較BreakPad是Google開發(fā)的跨平臺(tái)崩潰捕獲方案,目前主要用于Chromium。安卓APP也可以使用BreakPad來捕獲異常。BreakPad是一種“以后期調(diào)試為目的的崩潰捕獲方案”,BreakPad的崩潰捕獲結(jié)果是一個(gè)二進(jìn)制的minidump文件,需要后期拿到崩潰相關(guān)的所有ELF原始文件(包括系統(tǒng)動(dòng)態(tài)庫(kù)文件),然后開始進(jìn)行類似gdb的調(diào)試過程,才能定位問題。拿到每個(gè)崩潰機(jī)型上需要的系統(tǒng)庫(kù)文件,這會(huì)是一個(gè)耗時(shí)的過程;再加上復(fù)雜的APP自身可能包含數(shù)十個(gè)native庫(kù),這些native庫(kù)由不同的業(yè)務(wù)團(tuán)隊(duì)開發(fā),并且在APP發(fā)版后還可能熱更新。如果要把這整個(gè)過程自動(dòng)化的完成,需要一個(gè)非常復(fù)雜的系統(tǒng)來支持。在我們開發(fā)移動(dòng)端

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論