01 | 崩潰優化(上)——學習總結

Android 崩潰分爲 Java 崩潰和 Native 崩潰

 

1.Native 崩潰的捕獲流程

https://mp.weixin.qq.com/s/g-WzYF3wWAljok1XjPoo7w?

完整的Native崩潰從補貨到解析需要經歷哪些流程:

編譯端。需要將帶符號信息的文件保留下來。

客戶端。捕獲到崩潰時,收集儘可能多的有用信息,然後選擇合適的時機上傳到服務器。

服務端。讀取上報的日誌文件,尋找合適的符號文件,生成可讀的 C/C++調用棧。

 

2、Native崩潰捕獲的難點

Chromium 的Breakpad是目前 Native 最成熟的捕獲崩潰方案。

最核心的是怎麼樣保證客戶端在各種極端情況下依然可以生成崩潰日誌。因爲在崩潰時,程序會處於一個不安全的狀態,如果處理不當,非常容易發生二次崩潰。

生成崩潰日誌時會有哪些比較棘手的情況?

情況一:文件句柄泄漏,導致創建日誌文件失敗,怎麼辦?應對方式:

我們需要提前申請文件句柄 fd 預留,防止出現這種情況。

情況二:因爲棧溢出了,導致日誌生成失敗,怎麼辦?,替換當前棧,或者在堆裏預留空間。

情況三:整個堆的內存都耗盡了,導致日誌生成失敗,怎麼辦?

Breakpad 做的比較徹底,重新封裝了Linux Syscall Support,來避免直接調用 libc。

https://time.geekbang.org/column/article/70602

情況四:堆破壞或二次崩潰導致日誌生成失敗,怎麼辦?

Breakpad 會從原進程 fork 出子進程去收集崩潰現場。即使出現二次崩潰,只是這部分的信息丟失部分信息。

 

3. 選擇合適的崩潰服務

Bugly、阿里的啄木鳥平臺、網易雲捕、Google 的 Firebase 等等。

當然,在平臺的選擇方面,我認爲,從產品化跟社區維護來說,Bugly 在國內做的最好;從技術深度跟捕獲能力來說,阿里 UC 瀏覽器內核團隊打造的啄木鳥平臺最佳。

 

如何客觀地衡量崩潰

UV 崩潰率 = 發生崩潰的 UV / 登錄 UV

PV 崩潰率、啓動崩潰率、重複崩潰率這些指標,計算方法都大同小異。

啓動崩潰對用戶帶來的傷害最大,應用無法啓動往往通過熱修復也無法拯救。閃屏廣告、運營活動,很多應用啓動過程異常複雜,又涉及各種資源、配置下發,極其容易出現問題。微信讀書、蘑菇街、淘寶、天貓這些“重運營”的應用都有使用一種叫作“安全模式”的技術來保障客戶端的啓動流程,在監控到客戶端啓動失敗後,給用戶自救的機會。

 

如何客觀地衡量穩定性

處理了崩潰,我們還會經常遇到 ANR .

我們怎麼去發現應用中的 ANR 異常呢?

1. 使用 FileObserver 監聽 /data/anr/traces.txt 的變化。

非常不幸的是,很多高版本的 ROM,已經沒有讀取這個文件的權限了。

海外可以使用 Google Play 服務,而國內微信利用Hardcoder框架(HC 框架是一套獨立於安卓系統實現的通信框架,它讓 App 和廠商 ROM 能夠實時“對話”了)向廠商獲取了更大的權限

 

2. 監控消息隊列的運行時間。

這個方案無法準確地判斷是否真正出現了 ANR 異常,也無法得到完整的 ANR 日誌。在我看來,更應該放到卡頓的性能範疇。

Tinker 在補丁的加載流程也設計了簡單的“安全模式”,在啓動時會檢查上次應用的退出類型,如果檢查連續三次異常退出的情況。

都有哪些應用退出的情形

  • 主動自殺。Process.killProcess()、exit() 等。
  • 崩潰。出現了 Java 或 Native 崩潰。
  • 系統重啓;系統出現異常、斷電、用戶主動重啓等,我們可以通過比較應用開機運行時間是否比之前記錄的值更小。
  • 被系統殺死。被 low memory killer 殺掉、從系統的任務管理器中劃掉等。
  • ANR。

在應用啓動的時候設定一個標誌,在主動自殺或崩潰後更新標誌,這樣下次啓動時通過檢測這個標誌就能確認運行期間是否發生過異常退出。

對應上面的五種退出場景,我們排除掉主動自殺和崩潰(崩潰會單獨的統計)這兩種場景,希望可以監控到剩下三種的異常退出,理論上這個異常捕獲機制是可以達到 100% 覆蓋的。

衡量應用的穩定性:異常率

UV 異常率 = 發生異常退出或崩潰的 UV / 登錄 UV

我們不應該盲目追求崩潰率這一個數字,應該以用戶體驗爲先,我們不應該隨意使用 try catch 去隱藏真正的問題。瞭解崩潰的本質原因,保證後面的運行流程。

 

Breakpad是一個跨平臺的開源項目,今天的課後作業是使用 Breakpad 來捕獲一個 Native崩潰。

https://github.com/AndroidAdvanceWithGeektime/Chapter01

 

windows 下解析 breakpad 產生的dump

分析dump參考:https://www.jianshu.com/p/0bfe7800bdef0

利用了androidstudio 自帶的 工具

 


 

 好,從.dump文件解析出來的信息中,根據文章Android 平臺 Native 代碼的崩潰捕獲機制及實現的介紹,我們可知Crash reason: SIGSEGV代表哪種類型的錯誤:

SIGSEGV 是當一個進程執行了一個無效的內存引用,或發生段錯誤時發送給它的信號。
Thread 0 (crashed) // crash 發生時候的線程
 0  libcrash-lib.so + 0x526 // 發生 crash 的位置和寄存器信息 

有了具體的寄存器信息,我們進行符號解析,可以使用Android NDK中提供的addr2line來根據地址進行一個符號反解的過程,該工具在Android SDK目錄下可以找到。
  工具鏈的選擇要根據.so的類型來決定,看解析後的文件,有顯示CPU信息。下面是NDK 18的工具鏈目錄:


  如果是arm-64位的so,解析是需要使用aarch64-linux-android-4.9下的工具鏈。
  如果是arm-32位的so,解析是需要使用arm-linux-androideabi-4.9下的工具鏈。
  如果是x86-64位的so,解析是需要使用x86_64-4.9下的工具鏈。
  如果是x86-32位的so,解析是需要使用x86-4.9下的工具鏈。
  這裏,因爲CPU信息是arm,所以選擇 arm-linux-androideabi-4.9下的工具鏈。我們將項目中build目錄下對應的

libcrash-lib.so 地址:

(app\build\intermediates\transforms\mergeJniLibs\debug\0\lib\x86\libcrash-lib.so)

拷貝到x86-4.9下的工具鏈目錄(D:\Android\sdk\ndk-bundle\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin)中,然後執行如下命令:

具體得到 哪個類,哪一行報錯。

總結:

可能由於環境不同,無法啓動,建議同學自行編譯來獲取工具,具體教程可見 https://github.com/google/breakpad

回過頭來看Google Breakpad在Github上的說明,是針對沒有minidump_stackwalk執行文件的開發者而言的,打個比方(可能不恰當,假設做Java後臺的沒有minidump_stackwalk):做Java Web的,利用JNI實現了一個需求,想捕獲異常並分析,那麼他就必須在他開發的平臺上(Windows、Mac或Linux下)利用Google Breakpad源碼生成一個minidump_stackwalk文件。只是,Android已經提供這個文件,只是有些人不知道而已。
  另外,我推測:在Linux、Mac下,只要安裝了Android Studio應該都不需要編譯Google Breakpad源碼。

 

極客時間版權所有: https://time.geekbang.org/column/article/70602

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章