【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android崩潰日志獲取與解析_第1頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android崩潰日志獲取與解析_第2頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android崩潰日志獲取與解析_第3頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android崩潰日志獲取與解析_第4頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android崩潰日志獲取與解析_第5頁(yè)
免費(fèi)預(yù)覽已結(jié)束,剩余2頁(yè)可下載查看

下載本文檔

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

文檔簡(jiǎn)介

【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android崩潰日志獲取與解析

今天來(lái)寫點(diǎn)Android崩潰的東西,在我們寫代碼的過(guò)程中,代碼寫的再好再嚴(yán)謹(jǐn),也總是會(huì)有一些程序的崩潰,對(duì)于release出去的APP,我們肯定是希望我們能夠拿到崩潰的日志,便于bug的發(fā)現(xiàn)以及修復(fù),在下一個(gè)版本中再修復(fù)。所以,今天我們來(lái)說(shuō)說(shuō)Android崩潰日志的抓取。在程序界面有一句話很流行,那就是不要重復(fù)造輪子。現(xiàn)在市面上有很多的崩潰日志抓取工具,比如騰訊的bugly,不管是eclipse還是AndroidStudio,集成都是非常簡(jiǎn)單,他可以抓取到JAVA的崩潰,同樣也可以抓取到NDK代碼的崩潰。Java的崩潰就沒(méi)有什么好說(shuō)的,集成的步驟以及實(shí)現(xiàn)的原理太簡(jiǎn)單,下面我們來(lái)看看如何集成NDK崩潰的抓取1、首先在c/c++代碼的任意位置添加代碼constcharSO_FILE_VERSION[]

__attribute__((section(".bugly_version")))="versionname";注意,如果是cpp文件得話必須加上extern"C",這一點(diǎn)騰訊給出來(lái)的文檔里面沒(méi)有說(shuō)明。如果不加的話,我們編譯出來(lái)的動(dòng)態(tài)庫(kù)是沒(méi)有版本號(hào)信息的,為什么是.bugly_version,這個(gè)只有騰訊知道,我們知道騰訊定義了這個(gè)一個(gè)符號(hào),用來(lái)讀取出動(dòng)態(tài)庫(kù)的版本號(hào)來(lái)。2、編譯出so后,我們可以查看版本號(hào),使用NDK工具arm-linux-androideabi-readelf.exe來(lái)讀取。命令行工具下運(yùn)行arm-linux-androideabi-readelf.exe-p.bugly_versionlibXXXX.so就可以讀取到第一步中寫入的versionname了。Windows系統(tǒng)arm-linux-androideabi-readelf.exe的路徑為android-ndk-r10e\toolchains\arm-linux-androideabi-4.x\prebuilt\windows-x86_64\bin。3、從編譯結(jié)果的obj目錄取出對(duì)應(yīng)的動(dòng)態(tài)庫(kù),利用騰訊提供的批處理文件生成map文件,并將map文件上傳給騰訊,這樣我們的apk里面發(fā)布的動(dòng)態(tài)庫(kù)是release版本的,但是通過(guò)騰訊的網(wǎng)頁(yè)看到的堆棧還是可以定位到崩潰代碼的行數(shù)以及所在的文件。集成就這么結(jié)束了,還是很簡(jiǎn)單的,如下是我測(cè)試程序產(chǎn)生的崩潰日志#00pc00000cd4libErrorReport.socrash(E:/workspace/BuglyErrorTest/app/src/main/jni/ErrorReport.cpp:14-16)[armeabi-v5te][1.0.1]#01pc00000cdblibErrorReport.soJava_com_openflight_bugly_JniTest_nativeCrash(E:/workspace/BuglyErrorTest/app/src/main/jni/ErrorReport.cpp:21)[armeabi-v5te][1.0.1]#02pc000d3051

/data/dalvik-cache/arm/data@app@com.openflight.bugly-2@base.apk@classes.dexbugly的集成就講到這里了,那么問(wèn)題來(lái)了,bugly是怎么做到的呢?以上都是基于我們的APP運(yùn)行在有網(wǎng)絡(luò)的環(huán)境下,那么如果我們開發(fā)的APP是要運(yùn)行在一個(gè)沒(méi)有網(wǎng)絡(luò)的環(huán)境中呢,怎么辦?怎么辦?怎么辦?很悲劇,本人是做車載導(dǎo)航開發(fā)的,而很多車載設(shè)備是沒(méi)有網(wǎng)絡(luò)的,那么就只能是抓取log保存在本地,然后取出對(duì)應(yīng)的log來(lái)給開發(fā)人員分析了。JAVA的崩潰很好辦,我們實(shí)現(xiàn)一下UncaughtExceptionHandler,然后將crash信息保存到本地的文件中就好了。那么NDK的崩潰呢?我們要感謝偉大的google把google-breakpad(/svn2github/google-breakpad)開源出來(lái)了。那接下來(lái)我們來(lái)看一下breakpad的集成吧。本例采用eclipse工程的方式,為啥不用AndroidStudio?因?yàn)閎reakpad給android提供的編譯方式就是使用mk文件來(lái)編譯的,而且個(gè)人感覺(jué)用eclipse來(lái)做NDK開發(fā)更方便。先看一下jni目錄的結(jié)構(gòu)google-breakpad-master中有提供一個(gè)android的例子,先不管那么多,直接進(jìn)入到例子的目錄,ndk-build一下再說(shuō),什么?編譯不過(guò),怎么可能,google一下為什么,好吧,真的是編譯不過(guò),為什么,因?yàn)楣雀璧墓こ處熢趯慴reakpad的mk文件得時(shí)候居然漏了一些東西,好吧,我們都給補(bǔ)上。修改google-breakpad-master/android/google-breakpad/Android.mk,把LOCAL_SRC_FILES修改為L(zhǎng)OCAL_SRC_FILES:=\

src/client/linux/crash_generation/crash_generation_client.cc\

src/client/linux/handler/exception_handler.cc\

src/client/linux/handler/minidump_descriptor.cc\

src/client/linux/log/log.cc\

src/client/linux/minidump_writer/linux_dumper.cc\

src/client/linux/minidump_writer/linux_ptrace_dumper.cc\

src/client/linux/minidump_writer/minidump_writer.cc\

src/client/linux/microdump_writer/microdump_writer.cc\

src/client/linux/dump_writer_common/ucontext_reader.cc\

src/client/linux/dump_writer_common/seccomp_unwinder.cc\

src/client/linux/dump_writer_common/thread_info.cc\

src/client/minidump_file_writer.cc\

src/common/android/breakpad_getcontext.S\

src/common/convert_UTF.c\

src/common/md5.cc\

src/common/string_conversion.cc\

src/common/linux/elfutils.cc\

src/common/linux/file_id.cc\

src/common/linux/guid_creator.cc\

src/common/linux/linux_libc_support.cc\

src/common/linux/memory_mapped_file.cc\

src/common/linux/safe_readlink.cc好了,再來(lái)一次ndk-build,這次沒(méi)有問(wèn)題了,可以正常編譯,把編譯的結(jié)果push到手機(jī)上,運(yùn)行一下,生成了一個(gè)dmp文件,恩,這個(gè)dmp文件就是我們要的東西了??梢蚤_始移植了,jni的代碼和mk文件也都非常簡(jiǎn)單,直接貼出來(lái)Application.mkAPP_STL:=stlport_staticAPP_ABI:=armeabiAndroid.mkLOCAL_PATH:=$(callmy-dir)include$(CLEAR_VARS)LOCAL_MODULE

:=ErrorReportLOCAL_SRC_FILES:=ErrorReport.cppLOCAL_STATIC_LIBRARIES+=breakpad_clientLOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib-llogLOCAL_STATIC_LIBRARIES+=libgccinclude$(BUILD_SHARED_LIBRARY)ifneq($(NDK_MODULE_PATH),)

$(callimport-module,google_breakpad)else

include$(LOCAL_PATH)/google-breakpad-master/android/google_breakpad/Android.mkendifErrorReport.cpp#include<jni.h>#include<stdio.h>#include<android/log.h>#include"google-breakpad-master/src/client/linux/handler/exception_handler.h"#include"google-breakpad-master/src/client/linux/handler/minidump_descriptor.h"#defineLOGD(...)__android_log_print(ANDROID_LOG_DEBUG,"ErrorReport",__VA_ARGS__)extern"C"{ JNIEXPORTjintJNICALLJava_com_openflight_errorreport_CrashHandler_nativeCrash( JNIEnv*env,jobjectthiz); JNIEXPORTjintJNICALLJava_com_openflight_errorreport_CrashHandler_setNativeCrashDir( JNIEnv*env,jobjectthiz,jstringpath);}staticgoogle_breakpad::ExceptionHandler*handler=NULL;boolDumpCallback(constgoogle_breakpad::MinidumpDescriptor&descriptor,

void*context,

boolsucceeded){ LOGD("Dumppath:%s",descriptor.path()); returnsucceeded;}voidcrash(){ volatileint*a=reinterpret_cast<volatileint*>(NULL); *a=1;}JNIEXPORTjintJava_com_openflight_errorreport_CrashHandler_nativeCrash( JNIEnv*env,jobjectthiz){ crash();}//設(shè)置dmp文件保存路徑JNIEXPORTjintJNICALLJava_com_openflight_errorreport_CrashHandler_setNativeCrashDir( JNIEnv*env,jobjectthiz,jstringpath){ constchar*filePath=env->GetStringUTFChars(path,0); google_breakpad::MinidumpDescriptordescriptor(filePath); handler=newgoogle_breakpad::ExceptionHandler(descriptor,NULL,DumpCallback,NULL,true,-1);}其中有幾個(gè)需要注意的地方,大家可能看到Android.mk中加了一句LOCAL_STATIC_LIBRARIES+=libgcc,這一句在例子中是沒(méi)有的,為啥呢,因?yàn)楸救耸稚蟽膳_(tái)手機(jī),N5以及Note2,如果如加這一句的話Note2一運(yùn)行就崩潰。還有一點(diǎn),為啥要用staticgoogle_breakpad::ExceptionHandler*handler呢,例子里面是直接在main函數(shù)里面聲明并初始化的,好吧,我最開始也是這么認(rèn)為的,直接放在Jni_OnLoad里面,結(jié)果dmp文件無(wú)法生成。那為什么例子中可以正常生成呢?好吧,因?yàn)槔又衏rash是在main函數(shù)里面的,handler的作用域是整個(gè)main函數(shù),所以他可以生成。如果你把這段代碼放到Jni_OnLoad中,那么他的作用域也是Jni_OnLoad,Jni_OnLoad返回之后就沒(méi)有效果了,這顯然不是我們想要的,我們希望它的作用域跟APP的生命周期是一樣的,所以把他定義為static。工作都做完了,拿臺(tái)手機(jī)來(lái)跑吧,調(diào)用crash方法會(huì)崩潰,崩潰之后我們可以看到在我們?cè)O(shè)置的目錄下面已經(jīng)有一個(gè)dmp文件生成了,那dmp文件這么解析呢?windows實(shí)在是太不方便了,下面還是給出ubuntu系統(tǒng)的解析方法吧。1、下載google-breakpad源代碼,編譯linux版本,找到以下兩個(gè)文件google-breakpad-read-only/src/tools/linux/dump_syms/dump_symsgoogle-breakpad-read-only/src/processor/minidump_stackwalk2、任意位置建立一個(gè)文件夾,文件夾中包含dump_syms、minidump_stackwalk、*.dmp、所有的動(dòng)態(tài)庫(kù)文件(多個(gè)動(dòng)態(tài)庫(kù)需要重復(fù)3、4步驟)3、執(zhí)行命令./dump_symslibXXX.so>libXXX.so.sym4、創(chuàng)建文件夾symbols/libgame.so/6D7D7B4FAAE9D2686CF45FA12A9E3AD30,并將生成的libXXX.so.sym拷貝到該文件夾中,6D7D7B4FAAE9D2686CF45FA12A9E3AD30為生成的libXXX.so.sym的第一行的內(nèi)容5、執(zhí)行命令./minidump_stackwalkXXXXXXXXXXX.dmpsymbols/>result.txt好了,大功告成,我們來(lái)看一下解析出來(lái)的result.txt里面崩潰的堆棧Thread0(crashed)

0

libErrorReport.so!_Z5crashv+0x3

r0=0x41803818

r1=0x59600019

r2=0x00000001

r3=0x00000000

r4=0x00000000

r5=0x57ac67b8

r6=0x00000000

r7=0x57765dac

r8=0xbea0a510

r9=0x57765da4

r10=0x57765da0

r12=0x5e0b6555

fp=0xbea0a524

sp=0xbea0a508

lr=0x5e0b655b

pc=0x5e0b6550

Foundby:givenasinstructionpointerincontext這是什么鬼,_Z5crashv是什么東西?應(yīng)該是armv5架構(gòu)的CPU,crash函數(shù)中崩潰,而且該函數(shù)的返回值是void,就這些信息,如果NDK代碼多的話,還是很難定位到崩潰的地方,意義不是很大吶,好吧,這個(gè)是從libs中取出來(lái)的so庫(kù),那么我們?cè)囋噊bj中取出來(lái)的so去解析結(jié)果是怎么樣的。Thread0(crashed)

0

libErrorReport.so!crash[ErrorReport.cpp:27+0x4]

r0=0x41803818

r1=0x59600019

r2=0x00000001

r3=0x00000000

r4=0x00000000

r5=0x57ac67b8

r6=0x00000000

r7=0x57765dac

r8=0xbea0a510

r9=0x57765da4

r10=0x57765da0

r12=0x5e0b6555

fp=0xbea0a524

sp=0xbea0a508

lr=0x5e0b655b

pc=0x5e0b6550

Foundby:givenasinstructionpointerincontext

1

libErrorReport.so!Java_com_openflight_errorreport_CrashHandler_nativeCrash[ErrorReport.cpp:32+0x3]

r4=0x00000000

r5=0x57ac67b8

r6=0x00000000

r7=0x57765dac

r8=0xbea0a510

r9=0x57765da4

r10=0x57765da0

fp=0xbea0a524

sp=0xbea0a508

pc=0x5e0b655b

Foundby:callframeinfo哇,這個(gè)結(jié)果就比較完美了,c++中崩潰的堆棧都有了,哪個(gè)文件哪一行都有,但是呢,跟bugly比起來(lái)還是差了一些,因?yàn)閎ugly中可以看到j(luò)ava層調(diào)用的堆棧。結(jié)果是比較完美了,可是可是可是,操作還是麻煩了一點(diǎn),如果我的應(yīng)用有很多個(gè)動(dòng)態(tài)庫(kù)的話,那解析一個(gè)dmp文件就要好久了,很不幸,我曾經(jīng)開發(fā)的應(yīng)用里面包含了將近10個(gè)庫(kù),天啊,這就算再熟練解析一個(gè)文件也要好幾分鐘啊,而且中途還不能出錯(cuò),殺了我吧,我不干了。我的理念是,凡是重復(fù)的工作都讓電腦去做,腳本是個(gè)很好的東西,下面就把shell腳本放出來(lái)吧#!/bin/bash#[usage]#將本腳本、dump_sys、minidump_stackwalk放在同級(jí)目錄下,并創(chuàng)建libs文件夾,所有動(dòng)態(tài)庫(kù)放到libs文件夾內(nèi)#./dump2txt.sh[dmp文件路徑][生成的txt文件路徑]LIBRARY_DIRECTORY="libs"LIBRARY_EXTENDNAME=".sym"LIBRARY_KEYPOS=3Check(){ if[$#-ne2];then echopleaseinputtwoparam,thefirstparamisthedmpfilepath,thesecondparamistxtfilepath exit fi if[!-f"$1"];then echo$1isnotexsit exit fi}DealLibrary(){ if[!-f"$LIBRARY_DIRECTORY/$1"]

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝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ù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 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)論