崩潰優(yōu)化,Native崩潰收集

最近在重新學(xué)習(xí)極客時間Android開發(fā)高手課,張紹文老師推薦了Breakpad開源庫來采集native 的crash日志,參照老師的講解、Demo和同學(xué)做的封裝庫Android_Breakpad,寫了一個DemoBreakPadDemo
。該庫主要是在發(fā)生native crash的時候生成文件保存到sd卡,然后分析文件,定位問題。 在我們開發(fā)過程中Android JNI層Crash問題是個比較頭疼的問題。 相對Java層來說,由于c/c++造成的crash沒有輸出如同Java的Exception Strace,所以定位問題是個比較艱難的事情。 Google Breakpad是一套完整的工具集,從crash的捕獲到crash的dump,都提供了相對應(yīng)的工具。 Breakpad是一個庫和工具套件可以讓你發(fā)布的應(yīng)用程序(把編譯器提供的調(diào)試信息剝離掉的)給用戶,記錄了崩潰緊湊的“dump”文件,發(fā)送回您的服務(wù)器,并從這些minidump產(chǎn)生C和C++堆棧蹤跡。Breakpad可以根據(jù)請求使沒有崩潰的程序也可以寫出minidump Breakpad有三個主要組件:

  1. 客戶端:是一個庫,包含在您的應(yīng)用程序中。 它可以獲取當(dāng)前線程的狀態(tài)和當(dāng)前加載的可執(zhí)行文件和共享庫的ID寫轉(zhuǎn)儲文件。您可以配置客戶端發(fā)生了崩潰時寫入一個minidump時,或明確要求時。
  2. 符號卸載器:是一個程序,讀取由編譯器產(chǎn)生的調(diào)試信息,并生成一個使用Breakpad格式的符號文件 。
  3. 處理器(minidump processor):是一個程序,讀取一個minidump文件,找到相應(yīng)的版本的符號文件的(可執(zhí)行文件和共享庫的轉(zhuǎn)儲提到的),并產(chǎn)生了一個人可讀的C / C + +堆棧跟蹤。 Breakpad是一個跨平臺的開源庫,我們也可以在 Breakpad Github下載自己編譯。
編譯

接下來就是講解如何下載編譯的過程(其實基本上按照說明做就好,我是mac系統(tǒng),python、ndk之類的環(huán)境之前就已配好),

  1. 安裝depot_tools并配置環(huán)境變量,注意:這個步驟需要翻墻環(huán)境。這個倉庫中有一個關(guān)于android平臺如何引用的說明可以參照 Android_README 其中主要是要注意“Android.mk”文件依賴關(guān)系。我采用的是“CMakeLists.txt”和“Android.mk”大同小異,只是語法問題,具體可以參照NDK。 整個編譯過程相對來說算復(fù)雜的,因為中間涉及到很多知識點需要去掌握和查看,特別是對jni開發(fā)掌握不夠的程序員,不過也可以趁機了解學(xué)習(xí)一下jni相關(guān)知識。
  2. 下載breakpad
mkdir breakpad && cd breakpad
fetch breakpad
cd src
  1. 構(gòu)建
./configure && make

這一步就有可能出各種問題了,尤其是各種環(huán)境配置方面的

  1. 測試(可選)
make check
  1. 安裝(可選)
install the built libraries
  1. 注:如果要重新build,需要先執(zhí)行make distclean指令

下面解析下封裝庫



可以看出是一個普通項目和library。我們先看libary中g(shù)radle指向的CMakeLists.txt:
CMakeLists.txt指向編譯配置文件,查看該文件

cmake_minimum_required(VERSION 3.4.1)
project(breakpad-core)

set(ENABLE_INPROCESS ON)
set(ENABLE_OUTOFPROCESS ON)
set(ENABLE_LIBCORKSCREW ON)
set(ENABLE_LIBUNWIND ON)
set(ENABLE_LIBUNWINDSTACK ON)
set(ENABLE_CXXABI ON)
set(ENABLE_STACKSCAN ON)
if (${ENABLE_INPROCESS})
    add_definitions(-DENABLE_INPROCESS)
endif ()
if (${ENABLE_OUTOFPROCESS})
    add_definitions(-DENABLE_OUTOFPROCESS)
endif ()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=implicit-function-declaration")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ")

# breakpad
include_directories(external/libbreakpad/src external/libbreakpad/src/common/android/include)
add_subdirectory(external/libbreakpad)
list(APPEND LINK_LIBRARIES breakpad)
add_library(breakpad-core SHARED
        breakpad.cpp)
target_link_libraries(breakpad-core ${LINK_LIBRARIES}
        log)

關(guān)于cmake相關(guān)的語法可以網(wǎng)上查得。對于只想使用該項目的來說指導(dǎo)這里就基本上可以了解到整個項目的流程了,其他的不做深入講解。
使用在項目中應(yīng)用該libary或者引用該libary打包出來的aar即可,進(jìn)行初始化就行。如下:

apply plugin: 'com.android.application'
android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.android.zone"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
            }
        }
        ndk {
            abiFilters "armeabi-v7a", "arm64-v8a", "x86"
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation project(":breakpad-build")
}

在application中初始化就行

BreakpadInit.initBreakpad(externalReportPath.getAbsolutePath());

測試的代碼具體可以查看github上的項目BreakpadDemo。
生成crash之后導(dǎo)出對應(yīng)的dump文件,采用上文編譯出的工具minidump_stackwalk來解析該文件。 也就是本文tools下的工具,不同平臺需要自行編譯。

  1. 分析dump文件
./breakpad/src/src/processor/minidump_stackwalk ***.dmp >***/crashLog.txt 

得出日志結(jié)果過長,大體如下:

Operating system: Android
                  0.0.0 Linux 3.10.73-gd7193540482 #1 SMP PREEMPT Wed Dec 6 22:19:12 UTC 2017 aarch64
CPU: arm64
     8 CPUs
GPU: UNKNOWN
Crash reason:  SIGSEGV /SEGV_MAPERR
Crash address: 0x0
Process uptime: not available
Thread 0 (crashed)
 0  libnative-lib.so + 0x6fe0
     x0 = 0x0000007e4d8cb1c0    x1 = 0x0000007fd17ceb14
     x2 = 0x0000007fd17ceba0    x3 = 0x0000007e4d43ea74
     x4 = 0x0000007fd17ceda0    x5 = 0x0000007ecef7aa58
     x6 = 0x0000007fd17ce990    x7 = 0x0000007e4dcbe34c
     x8 = 0x0000000000000001    x9 = 0x0000000000000000
    x10 = 0x0000000000430000   x11 = 0x0000007e4d7d97a8
    x12 = 0x0000007ed1f3f790   x13 = 0x697871c6dea5b553
    x14 = 0x0000007ed2036000   
······

根據(jù)文章Android 平臺 Native 代碼的崩潰捕獲機制及實現(xiàn) 的介紹,我們可知“Crash reason: SIGSEGV /SEGV_MAPERR ”代表哪種類型的錯誤:

SIGSEGV 是當(dāng)一個進(jìn)程執(zhí)行了一個無效的內(nèi)存引用,或發(fā)生段錯誤時發(fā)送給它的信號。
Thread 0 (crashed) //crash 發(fā)生時候的線程
0  libnative-lib.so + 0x6fe0 //!!!發(fā)生 crash 的位置和寄存器信息

有了具體的寄存器信息,我們進(jìn)行符號解析: 符號解析,可以使用 ndk 中提供的addr2line來根據(jù)地址進(jìn)行一個符號反解的過程,該工具在 $NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line 輸出結(jié)果如下:

arm-linux-androideabi-addr2line -f -C -e sample/build/intermediates/transforms/mergeJniLibs/debug/0/lib/armeabi-v7a/libcrash-lib.so 0x77e                           
//輸出結(jié)果如下
Crash()

得出該崩潰為Crash方法所致,接下來就需要去修改該方法是后話了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容