如何分析、定位Android Native Crash


轉自:http://crash.163.com/index.do#news/!newsId=2

 

出於執行效率、業務安全、復用已有代碼的需求,目前市場上越來越多的 Android App 采用 C/C++ 來實現其關鍵邏輯。C/C++ 有內存管理靈活、與 linux 底層聯系更緊密、多種編程范式等特點,但也正是由於這些特點,使得普通開發人員在使用 C/C++ 開發時,更容易出讓進程直接崩潰的 bug。所以能分析 C/C++ 崩潰日志並能從日志中分析出原因,成為 Android 開發人員一項必備技能。本文介紹如何通過分析 Native 崩潰日志來定位出錯的 C/C++ 代碼及出錯原因。 

一、Native 崩潰日志格式

                        extern "C" JNIEXPORT int gen_stack(int i) { if (i > 2) return gen_stack(i - 2) + gen_stack(i - 1); else { int *p = NULL; *p = 123; return 1; } }

當調用 gen_stack(4) 發生 Native 崩潰時,一般 logcat 會打印如下格式的日志:

#00 pc 00000c1c /data/app-lib/com.testbugrpt-1/libtestNDKCrash.so (gen_stack+27)

#00 表示堆棧序號
pc 00000c1c 表示崩潰發生時 程序計數器 位於 libtestNDKCrash.so 偏移 0xc1c 處
gen_stack+27 表示0xc1c處正好是 gen_stack 符號(此處為函數名)偏移為27的一條指令

#01 pc 00000c0f /data/app-lib/com.testbugrpt-1/libtestNDKCrash.so (gen_stack+14) 

這是第二層堆棧,表示在離 libtestNDKCrash.so 0xc0f(也就是gen_stack + 14)位置的指令發生了一次函數調用,產生了第一層堆棧。

二、Native崩潰分析工具

在介紹工具之前,先簡單講一下有調試與無調試信息的兩個版本 so 。 一個含有 native 代碼的 app 項目的典型結構是這樣的: 


                    --jni --Android.mk --其它源文件 --libs --armeabi --armeabi-v7a --arm64-v8a .... --obj --local --armeabi --armeabi-v7a --arm64-v8a .... 

通常一次編譯會先生成一個有含有調試信息的 so, 路徑通常是在 obj/local/ 各 abi 目錄下,其中還有一些中間文件(比如.o文件);再通過對這些含有調試信息的 so 進行一次 strip , 產生對應的無調試信息 so, 放到 libs 目錄下各 abi 目錄中, 發布產品時,我們都是用這些 strip 后的 so。

一般的分析崩潰日志的工具都是利用含有調試信息的 so, 結合崩潰信息,分析崩潰點在源代碼中的行號。

  1. 1、ndk-stack

    ndk-stack.exe位於ndk根目錄。運行以下命令:

    D:\Android\android-ndk-r10c\ndk-stack.exe -sym E:\workspace\TestBugrpt\app\src\main\obj\local\armeabi-v7a\ -dump log.txt

    其中 log.txt 為崩潰日志,可以從 monitor 中點擊保存得到。或者運行:

    adb logcat | ndk-stack.exe -sym E:\workspace\TestBugrpt\app\src\main\obj\local\armeabi-v7a\

    這樣再運行程序,當崩潰發生時,ndk-stack.exe 會自動從 logcat 中獲取崩潰日志。

    運行以上命令時,要 注意 -sym 參數指示的路徑都是 obj\local\ 目錄,同時要匹配對應機器的 abi 目錄。可以得到:

    表明gen_stack + 27對應testNDKCrash.cpp的第13行,即*p = 123; 查看對應的源代碼,可以發現是此處的寫空指針導致崩潰。

  2. 2、addr2line

    addr2line 一般位於 android-ndk-r10c\toolchains\arm-linux-androideabi-4.9\prebuilt\windows\bin\ ,其路徑與文件名因操作系統、 abi 不同而有所不同。

    可以運行如下命令:

    arm-linux-androideabi-addr2line.exe -e E:\workspace\TestBugrpt\app\src\main\obj\local\armeabi-v7a\libtestNDKCrash.so 00000c1c 00000c0f

    與 ndk-stack 不同的是,ndk-stack 接受一個 obj/local/abi 目錄為參數,而 addr2line 接受 local 下一個具體的 so 文件路徑為參數。其中 00000c1c 00000c0f 就是上面第一節中分析的崩潰點離libtestNDKCrash.so的偏移量,即

    得到輸出:

    E:/workspace/TestBugrpt/app/src/main//jni/testNDKCrash.cpp:13

    E:/workspace/TestBugrpt/app/src/main//jni/testNDKCrash.cpp:9

    分別對應兩個偏移在源碼中的位置。

  3. 3、objdump

    上面兩種工具都是將崩潰點對應到源碼再進行分析,objdump 則是可以在匯編層對崩潰原因進行分析。當然這要求開發人員了解一些 arm/x86 匯編知識。

    objdump 也是 ndk 自帶的一個工具,通常與 addr2line 在同一目錄。運行如下命令:

    arm-linux-androideabi-objdump.exe -S -D E:\workspace\TestBugrpt\app\src\main\obj\local\armeabi-v7a\libtestNDKCrash.so > e:\dump.txt

    由於輸出比較多,將輸出重定位到 e:\\dump.txt 便於查看。打開 dump.txt , 定位到 00000c1c :

                                int *p = NULL; *p = 123; c16: 2300 movs r3, #0 c18: 227b movs r2, #123 ; 0x7b c1a: 1c68 adds r0, r5, #1 c1c: 601a str r2, [r3, #0]

    上面兩句是源代碼,下是對應的Arm匯編。

    如果要分析的 so 沒有調試信息, ndk-stack 與 addr2line 就無能為力了,只有 objdump 還能派上用場。當然,這種情況下有更好用的工具,比如 IDA Pro。不過那又是另外一個故事了。

三、常見崩潰類型及原因

  1. 1、SIGSEGV 段錯誤
    SEGV_MAPERR 要訪問的地址沒有映射到內存空間。 比如上面對空指針的寫操作, 當指針被意外復寫為一個較小的數值時。
    SEGV_ACCERR 訪問的地址沒有權限。比如試圖對代碼段進行寫操作。
  2. 2、SIGFPE 浮點錯誤,一般發生在算術運行出錯時。
    FPE_INTDIV 除以0
    FPE_INTOVE 整數溢出
  3. 3、SIGBUS 總線錯誤
    BUS_ADRALN 地址對齊出錯。arm cpu比x86 cpu 要求更嚴格的對齊機制,所以在 arm cpu 機器中比較常見。
  4. 4、SIGILL 發生這種錯誤一般是由於某處內存被意外改寫了。
    ILL_ILLOPC 非法的指令操作碼
    ILL_ILLOPN 非法的指令操作數
  5. 5、當調用堆棧中出現 stack_chk_fail 函數時,一般是由於比如 strcpy 之類的函數調用將棧上的內容覆蓋,而引起棧檢查失敗。

更多信號信息請參考文獻 [1]。 

四、線上Native崩潰處理

對於第二節中的分析方法,前提是可以得到 Native 層崩潰日志。由於 Android 設備的碎片化,必然存在在測試時覆蓋不到的機型。如果 App 在用戶機器上發生了崩潰,如何獲取 Native 崩潰日志?

目前網易雲捕已經實現了對 Java、Native 層崩潰日志的獲取,並能自動上傳到服務器進行分析。具體功能及接入方法請參考網易雲捕集成說明


參考文獻:
[1] http://man7.org/linux/man-pages/man2/sigaction.2.html
[2] http://blog.csdn.net/xyang81/article/details/42319789


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM