中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android的NativeCrash有什么用

發布時間:2021-06-18 13:47:20 來源:億速云 閱讀:159 作者:chen 欄目:開發技術

本篇內容主要講解“Android的NativeCrash有什么用”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Android的NativeCrash有什么用”吧!

    一、NE 簡介

    NE全稱 NativeException,就是C或者C++運行過程中產生的錯誤,NE不同于普通的 Java 錯誤,普通的logcat無法直接還原成可閱讀的堆棧,一般沒有源碼也無法調試。

    所以日常應用層的工程師,即使我們內部有云診斷的日志,一般也會忽略NE的錯誤,那么遇到這些問題,作為應用層、對C++不甚了解的工程師能否解決還原堆棧,能否快速定位或者解決NE的問題呢?

    下面將著重介紹:

    1.1、so 組成

    我們先了解一下 so 的組成,一個完整的 so 由C代碼加一些 debug 信息組成,這些debug信息會記錄 so 中所有方法的對照表,就是方法名和其偏移地址的對應表,也叫做符號表,這種 so 也叫做未 strip 的,通常體積會比較大。

    通常release的 so 都是需要經過一個strip操作的,這樣strip之后的 so 中的debug信息會被剝離,整個 so 的體積也會縮小。

    如下圖所示:

    Android的NativeCrash有什么用

    如下可以看到strip之前和之后的大小對比。

    Android的NativeCrash有什么用

    如果對 NE 或者 so 不了解的,可以簡單將這個debug信息理解為Java代碼混淆中的mapping文件,只有擁有這個mapping文件才能進行堆棧分析。

    如果堆棧信息丟了,基本上堆棧無法還原,問題也無法解決。

    所以,這些debug信息尤為重要,是我們分析NE問題的關鍵信息,那么我們在編譯 so 時候務必保留一份未被strip的so 或者剝離后的符號表信息,以供后面問題分析,并且每次編譯的so 都需要保存,一旦產生代碼修改重新編譯,那么修改前后的符號表信息會無法對應,也無法進行分析。

    1.2、查看 so 狀態

    事實上,也可以通過命令行來查看 so 的狀態,Mac下使用 file 命令即可,在命令返回值里面可以查看到so的一些基本信息。

    如下圖所示,stripped代表是沒有debug信息的so,with debug_info, not stripped代表攜帶debug信息的so。

    file libbreakpad-core-s.so

    libbreakpad-core-s.so: *******, BuildID[sha1]=54ad86d708f4dc0926ad220b098d2a9e71da235a, stripped

    file libbreakpad-core.so

    libbreakpad-core.so: ******, BuildID[sha1]=54ad86d708f4dc0926ad220b098d2a9e71da235a, with debug_info, not stripped

    如果你是 Windows系統的話,那么我勸你裝一個 Linux子系統,然后在 Linux執行同樣的命令,同樣也可以得到該信息。

    接下來看下我們如何獲取兩種狀態下的so。

    1.3、獲取 strip 和未被 strip 的 so

    目前Android Studio無論是使用mk或者Cmake編譯的方式都會同時輸出strip和未strip的so,如下圖是Cmake編譯so產生的兩個對應的so。

    strip之前的so路徑:build/intermediates/transforms/mergeJniLibs

    strip之后的so路徑:build/intermediates/transforms/stripDebugSymbol

    Android的NativeCrash有什么用

    另外也可以通過Android SDK提供的工具aarch74-linux-android-strip手動進行strip,aarch74-linux-android-strip這個工具位于/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains目錄下。

    這個工具有多種版本,主要針對不同的手機CPU架構,如果不知道手機的CPU架構,可以連接手機使用以下命令查看:

    adb shell cat /proc/cpuinfo

    Processor   : AArch74 Processor rev 12 (aarch74)

    如上圖可以看到我的手機CPU用的是aarch74,所以使用aarch74對應的工具aarch74-linux-android-strip,由于NDK提供了很多工具,后續都依照此原則使用即可:

    aarch74架構

    /Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/aarch74-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch74-linux-android-strip

    arm架構

    /Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-android-strip

    使用如下命令可以直接將debug的so進行strip

    aarch74-linux-android-strip --strip-all libbreakpad-core.so

    使用Cmake進行編譯的時候,可以增加如下命令,可以直接編譯出strip的so

    #set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")

    #set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")

    使用mk文件進行編譯的時候,可以增加如下命令,也可以直接編譯出strip的so

    -fvisibility=hidden

    二、NE 捕獲與解析

    NE解析顧名思議就是堆棧解析,當然所有的前提就是需要保存一份帶符號表、也就是未被strip的so,如果你只有strip之后的so,那就無能為力了,堆棧基本無法還原了。

    一般有以下三種方式可以捕獲和還原堆棧。

    2.1、logcat捕獲

    顧名思義,就是通過logcat進行捕獲,我們通過Android Studio打開logcat,制造一個NE,只能看到很多類似#00 pc 00000000000161a0的符號,并沒有一個可以直接閱讀的日志,我們想通過logcat直接輸出一份可以直接閱讀的log。

    可以使用Android/SDK/NDK下面提供的一個工具ndk-stack,它可以直接將NE輸出的log解析為可閱讀的日志。

    ndk-stack一般是位于ndk的工具下面,Mac下的地址為

    /Users/XXXX/Library/Android/sdk/ndk/21.3.6528147/ndk-stack

    然后在該目錄下執行控制臺命令,或者在 Android Studio的terminal中執行也可

    adb shell logcat | androidsdk絕對路徑/ndk-stack -sym so所在目錄

    如此控制臺在應用發生NE的時候便會輸出如下日志,由日志可以看出,崩潰對應的so以及對應的方法名,如果有c的源碼,那么就很容易定位問題。

    promote:~ njvivo$ adb shell logcat | ndk-stack -sym libbreakpad-core.so

    ********** Crash dump: **********

    Build fingerprint: 'vivo/PD1809/PD1809:8.1.0/OPM1.171019.026/compil04252203:user/release-keys'

    #00 0x00000000000161a0 /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/lib/arm64/libbreakpad-core.so (Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16)

    #01 0x00000000000090cc /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/oat/arm64/base.odex (offset 0x9000)

    Crash dump is completed

    其實ndk-stack這個工具原理就是內部集成利用了addr2line來實時解析堆棧并且顯示在控制臺中。

    看到這里有的小伙伴就覺得那這個不是很簡單,但是實際的崩潰場景一是不容易復現,二是用戶的場景有時候很難模擬,那么線上的NE崩潰又該如何監測和定位呢,有兩種方式。

    2.2、通過DropBox日志解析--適用于系統應用

    這個很簡單,DropBox會記錄JE,NE,ANR的各種日志,只需要將DropBox下面的日志傳上來即可進行分析解決,下面貼上一份日志示例。

    Android的NativeCrash有什么用

    解析方案1:

    借助上述的ndk-stack工具,可以直接將DropBox下面的日志解析成堆棧,從中可以看出,崩潰在breakpad.cpp第111行的Crash()方法中。

    ndk-stack -sym /Users/njvivo/Desktop/NE -dump data_app_native_crash@1605531663898.txt

    ********** Crash dump: **********

    Build fingerprint: 'vivo/PD1809/PD1809:8.1.0/OPM1.171019.026/compil04252203:user/release-keys'

    #00 0x00000000000161a0 /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/lib/arm64/libbreakpad-core.so (Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16)

    Crash()

    /Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:111:8

    Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo

    /Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:122:0

    #01 0x00000000000090cc /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/oat/arm64/base.odex (offset 0x9000)

    Crash dump is completed

    解析方案2:

    還是利用Android/SDK/NDK提供的工具linux-android-addr2line,這個工具位于/Users/njvivo/Library/Android/sdk/ndk目錄下,有兩個版本。

    aarch74架構

    /Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/aarch74-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch74-linux-android-addr2line

    arm架構

    /Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line

    命令使用方法如下,結合未被strip的so以及日志里面出現的堆棧符號00000000000161a0,同樣可以解析出崩潰地址和方法

    aarch74-linux-android-addr2line -f -C -e libbreakpad-core.so 00000000000161a0

    Crash()

    /Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:111

    基于以上,看似也很簡單,但是有一個致命的問題就是DropBox只有系統應用能訪問,非系統應用根本拿不到日志,那么,非系統應用該怎么辦呢?

    2.3、通過BreakPad捕獲解析--適用于所有應用

    非系統應用可以通過google提供的開源工具BreakPad進行監測分析,CrashSDK也是采用的此種方式,可以實時監聽到NE的發生,并且記錄相關的文件, 從而可以將崩潰和相應的應用崩潰時的啟動、場景等結合起來上報。

    下面簡單介紹一下BreakPad的使用方式。

    2.3.1、BreakPad的實現功能

    BreakPad主要提供兩個個功能,NE的監聽和回調,生成minidump文件,也就是dmp結尾的文件,另外提供兩個工具,符號表工具和堆棧還原工具。

    Android的NativeCrash有什么用

    • 符號表工具:用于從so中提取出debug信息,獲取到堆棧對應的符號表。

    • 堆棧還原工具:用于將BreakPad生成的dump文件還原成符號,也就是堆棧偏移值。

    這兩個工具會在編譯BreakPad源碼的時候產生。

    編譯完之后會產生minidump_stackwalk工具,有些同學不想編譯的話,Android Studio本身也提供了這個工具。

    這個minidump_stackwalk程序在Android Studio的目錄下面也存在,可以拿出來直接使用,如果不想編譯的話,直接到該目錄下面取即可,Mac路徑為:

    /Applications/Android Studio.app/Contents/bin/lldb/bin/minidump_stackwalk

    2.3.2、BreakPad的捕獲原理

    由上述可以得知,BreakPad在應用發生NE崩潰時,可以將NE對應的minidump文件寫入到本地,同時會回調給應用層,應用層可以針對本次崩潰做一些處理,達到捕獲統計的作用,后續將minidump文件上傳之后結合minidump_stackwalk以及addr2line工具可以還原出實際堆棧,示意圖如下:

    Android的NativeCrash有什么用

    在應用發生NE時,BreakPad會在手機本地生成一個dump文件,如圖所示:

    Android的NativeCrash有什么用

    得到了以上文件,我們只能知道應用發生了NE,但是這些文件其實是不可讀的,需要解析這些文件。

    下面著重講一下如何分析上面產生的NE:

    2.3.3、解析dump文件

    1、獲取NE崩潰的dump文件,將剛才得到的minidump_stackwalk和dump文件放在同一個目錄,也可以不放,填寫路徑的時候填寫絕對路徑即可。

    然后在該目錄下的終端窗口執行以下命令,該命令表示用minidump_stackwalk解析dump文件,解析后的信息輸出到當前目錄下的crashLog.txt文件。

    ./minidump_stackwalk xxxxxxxx.dmp >crashLog.txt

    2、執行完之后,minidump_stackwalk會將NE的相關信息寫到crashLog.txt里面,詳細信息如圖所示:

    Android的NativeCrash有什么用

    3、根據解析出的NE信息,關注圖中紅框,可以得知,這個崩潰發生的libbreakpad-core.so里面,0x161a0代表崩潰發生在相對根位置偏移161a0的位置

    2.3.4、獲取崩潰堆棧

    1、利用之前提到的addr2line工具,可以根據發生Crash的so文件以及偏移地址(0x161a0)可以得出產生crash的方法、行數和調用堆棧關系。

    2、在其根目錄對的終端窗口運行以下命令。

    arm-linux-androideabi-addr2line -C -f -e ${SOPATH} ${Address}

    -C -f           //打印錯誤行數所在的函數名稱

    -e                //打印錯誤地址的對應路徑及行數

    ${SOPATH}         //so庫路徑

    ${Address}        //需要轉換的堆棧錯誤信息地址,可以添加多個,但是中間要用空格隔開,例如:0x161a0

    3、如下圖是真實運行的示例

    aarch74-linux-android-addr2line -f -C -e libbreakpad-core.so 0x161a0

    Crash()

    /Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:111

    由上圖可以知道,該崩潰發生在breakpad.cpp文件的第111行,函數名是Crash(),與真實的文件一致,崩潰代碼如下:

    void Crash() {
        volatile int *a = (int *) (NULL);
        *a = 1; //此處在代碼里是111行
    }
      
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo(JNIEnv *env, jobject instance,
                                                            jstring mLaunchInfoStr_) {
      
      
        DO_TRY
        {
            Crash();
            const char *mLaunchInfoStr = env->GetStringUTFChars(mLaunchInfoStr_, 0);
            launch_info = (char *) mLaunchInfoStr;
    //        env->ReleaseStringUTFChars(mLaunchInfoStr_, mLaunchInfoStr);
        }
        DO_CATCH("updateLaunchInfo");
      
    }

    基于以上,便可以通過應用收集的dump文件解析的NE的詳細堆棧信息。

    三、so符號表的提取

    3.1、提取 so 的符號表

    通過以上內容,我們知道,so中包含了一些debug信息,又叫做符號表,那么我們如何將這些debug信息單獨剝離出來呢,ndk也給我們提供了相關的工具。

    aarch74架構

    /Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/aarch74-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch74-linux-android-objdump

    arm架構

    /Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-android-objdump

    如下是命令運行的方式,通過此命令,可以將so中的debug信息提取到文件中。

    promote:~ njvivo$ aarch74-linux-android-objdump -S libbreakpad-core.so > breakpad.asm

    3.2、符號表分析

    3.2.1、直接分析

    如下圖所示就是輸出的符號表文件,結合上面的log以及下面的符號表文件,我們同樣可以分析出堆棧。

    如log中所示,已經表明了崩潰地址是161a0,而161a0對應的代碼是*a=1,由上面的分析我們已經知道該崩潰是在breakpad.cpp的111行,也就是*a=1的位置,完全符合預期。

    backtrace:

    #00 pc 00000000000161a0 /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/lib/arm64/libbreakpad-core.so (Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16)

    #01 pc 00000000000090cc /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/oat/arm64/base.odex (offset 0x9000)

    Android的NativeCrash有什么用

    3.2.2、工具解析

    google提供了一個Python的工具,將符號表和log結合起來可以直接分析出堆棧

    執行命令,就可以解析出相關堆棧,該工具可以用于服務端批量進行解析,此處不再詳細說明。

    python parse_stack.py <asm-file> <logcat-file>

    3.2.3、偏移位置簡析

    上面文章提到了一個偏移位置的概念,筆者對此了解也不多,不過大致有一個概念,C代碼有一個根位置的代碼的,每行代碼相對根代碼都有一個偏移位置。

    如上圖示例log中有一行語句(Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16),+16就是代表相對nUpdateLaunchInfo方法的位置往后偏移16。

    由上圖可以看到,nUpdateLaunchInfo方法的位置是16190,偏移16,也就是16190+10(10進制的16轉化16進制后為10)=161a0,同日志輸出的一樣。

    到此,相信大家對“Android的NativeCrash有什么用”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    任丘市| 宣城市| 新田县| 林周县| 旺苍县| 陵川县| 汶上县| 平阴县| 怀集县| 黔江区| 延吉市| 丹寨县| 饶河县| 普兰县| 三原县| 界首市| 刚察县| 拉孜县| 罗田县| 安康市| 湘西| 安陆市| 郑州市| 永靖县| 辽源市| 大新县| 涞源县| 南溪县| 肇东市| 集安市| 辽中县| 密山市| 北京市| 繁峙县| 门头沟区| 黄浦区| 玉门市| 万盛区| 辽宁省| 阿合奇县| 噶尔县|