1. 程式人生 > >Android崩潰日誌獲取與解析

Android崩潰日誌獲取與解析

今天來寫點Android崩潰的東西,在我們寫程式碼的過程中,程式碼寫的再好再嚴謹,也總是會有一些程式的崩潰,對於release出去的APP,我們肯定是希望我們能夠拿到崩潰的日誌,便於bug的發現以及修復,在下一個版本中再修復。所以,今天我們來說說Android崩潰日誌的抓取。
在程式介面有一句話很流行,那就是不要重複造輪子。現在市面上有很多的崩潰日誌抓取工具,比如騰訊的bugly,不管是eclipse還是Android Studio,整合都是非常簡單,他可以抓取到JAVA的崩潰,同樣也可以抓取到NDK程式碼的崩潰。
Java的崩潰就沒有什麼好說的,整合的步驟以及實現的原理太簡單,下面我們來看看如何整合NDK崩潰的抓取
1、首先在c/c++程式碼的任意位置新增程式碼
const char SO_FILE_VERSION[]  __attribute__ ((section (".bugly_version"))) = "version name";
注意,如果是cpp檔案得話必須加上extern "C",這一點騰訊給出來的文件裡面沒有說明。如果不加的話,我們編譯出來的動態庫是沒有版本號資訊的,為什麼是.bugly_version,這個只有騰訊知道,我們知道騰訊定義了這個一個符號,用來讀取出動態庫的版本號來。
2、編譯出so後,我們可以檢視版本號,使用NDK工具arm-linux-androideabi-readelf.exe來讀取。
命令列工具下執行arm-linux-androideabi-readelf.exe -p .bugly_version libXXXX.so就可以讀取到第一步中寫入的version name了。Windows系統arm-linux-androideabi-readelf.exe的路徑為android-ndk-r10e\toolchains\arm-linux-androideabi-4.x\prebuilt\windows-x86_64\bin。
3、從編譯結果的obj目錄取出對應的動態庫,利用騰訊提供的批處理檔案生成map檔案,並將map檔案上傳給騰訊,這樣我們的apk裡面釋出的動態庫是release版本的,但是通過騰訊的網頁看到的堆疊還是可以定位到崩潰程式碼的行數以及所在的檔案。
整合就這麼結束了,還是很簡單的,如下是我測試程式產生的崩潰日誌
#00 pc 00000cd4 libErrorReport.so crash (E:/workspace/BuglyErrorTest/app/src/main/jni/ErrorReport.cpp:14-16) [armeabi-v5te] [1.0.1]
#01 pc 00000cdb libErrorReport.so Java_com_openflight_bugly_JniTest_nativeCrash (E:/workspace/BuglyErrorTest/app/src/main/jni/ErrorReport.cpp:21) [armeabi-v5te] [1.0.1]
#02 pc 000d3051  /data/dalvik-cache/arm/
[email protected]
@[email protected]@classes.dex

bugly的整合就講到這裡了,那麼問題來了,bugly是怎麼做到的呢?以上都是基於我們的APP執行在有網路的環境下,那麼如果我們開發的APP是要執行在一個沒有網路的環境中呢,怎麼辦?怎麼辦?怎麼辦?很悲劇,本人是做車載導航開發的,而很多車載裝置是沒有網路的,那麼就只能是抓取log儲存在本地,然後取出對應的log來給開發人員分析了。JAVA的崩潰很好辦,我們實現一下UncaughtExceptionHandler,然後將crash資訊儲存到本地的檔案中就好了。那麼NDK的崩潰呢?我們要感謝偉大的google把google-breakpad(https://github.com/svn2github/google-breakpad)開源出來了。

那接下來我們來看一下breakpad的整合吧。本例採用eclipse工程的方式,為啥不用Android Studio?因為breakpad給android提供的編譯方式就是使用mk檔案來編譯的,而且個人感覺用eclipse來做NDK開發更方便。

先看一下jni目錄的結構

google-breakpad-master中有提供一個android的例子,先不管那麼多,直接進入到例子的目錄,ndk-build一下再說,什麼?編譯不過,怎麼可能,google一下為什麼,好吧,真的是編譯不過,為什麼,因為谷歌的工程師在寫breakpad的mk檔案得時候居然漏了一些東西,好吧,我們都給補上。修改google-breakpad-master/android/google-breakpad/Android.mk,把LOCAL_SRC_FILES修改為

LOCAL_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
好了,再來一次ndk-build,這次沒有問題了,可以正常編譯,把編譯的結果push到手機上,執行一下,生成了一個dmp檔案,恩,這個dmp檔案就是我們要的東西了。可以開始移植了,jni的程式碼和mk檔案也都非常簡單,直接貼出來
Application.mk
APP_STL := stlport_static
APP_ABI := armeabi

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := ErrorReport
LOCAL_SRC_FILES := ErrorReport.cpp
LOCAL_STATIC_LIBRARIES += breakpad_client
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
LOCAL_STATIC_LIBRARIES += libgcc

include $(BUILD_SHARED_LIBRARY)

ifneq ($(NDK_MODULE_PATH),)
  $(call import-module,google_breakpad)
else
  include $(LOCAL_PATH)/google-breakpad-master/android/google_breakpad/Android.mk
endif

ErrorReport.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"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "ErrorReport", __VA_ARGS__)

extern "C" {
	JNIEXPORT jint JNICALL Java_com_openflight_errorreport_CrashHandler_nativeCrash(
			JNIEnv* env, jobject thiz);
	JNIEXPORT jint JNICALL Java_com_openflight_errorreport_CrashHandler_setNativeCrashDir(
			JNIEnv* env, jobject thiz, jstring path);
}

static google_breakpad::ExceptionHandler *handler = NULL;
bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
                  void* context,
                  bool succeeded) {
	LOGD("Dump path: %s", descriptor.path());
	return succeeded;
}

void crash(){
	volatile int* a = reinterpret_cast<volatile int*>(NULL);
	*a = 1;
}

JNIEXPORT jint Java_com_openflight_errorreport_CrashHandler_nativeCrash(
			JNIEnv* env, jobject thiz){
	crash();
}

// 設定dmp檔案儲存路徑
JNIEXPORT jint JNICALL Java_com_openflight_errorreport_CrashHandler_setNativeCrashDir(
		JNIEnv* env, jobject thiz, jstring path){
	const char *filePath = env->GetStringUTFChars(path, 0);
	google_breakpad::MinidumpDescriptor descriptor(filePath);
	handler = new google_breakpad::ExceptionHandler(descriptor, NULL, DumpCallback, NULL, true, -1);
}

其中有幾個需要注意的地方,大家可能看到Android.mk中加了一句LOCAL_STATIC_LIBRARIES += libgcc,這一句在例子中是沒有的,為啥呢,因為本人手上兩臺手機,N5以及Note2,如果如加這一句的話Note2一執行就崩潰。還有一點,為啥要用static google_breakpad::ExceptionHandler *handler呢,例子裡面是直接在main函式裡面宣告並初始化的,好吧,我最開始也是這麼認為的,直接放在Jni_OnLoad裡面,結果dmp檔案無法生成。那為什麼例子中可以正常生成呢?好吧,因為例子中crash是在main函式裡面的,handler的作用域是整個main函式,所以他可以生成。如果你把這段程式碼放到Jni_OnLoad中,那麼他的作用域也是Jni_OnLoad,Jni_OnLoad返回之後就沒有效果了,這顯然不是我們想要的,我們希望它的作用域跟APP的生命週期是一樣的,所以把他定義為static。

工作都做完了,拿臺手機來跑吧,呼叫crash方法會崩潰,崩潰之後我們可以看到在我們設定的目錄下面已經有一個dmp檔案生成了,那dmp檔案這麼解析呢?windows實在是太不方便了, 下面還是給出ubuntu系統的解析方法吧。

1、下載google-breakpad原始碼,編譯linux版本,找到以下兩個檔案

google-breakpad-read-only/src/tools/linux/dump_syms/dump_syms
google-breakpad-read-only/src/processor/minidump_stackwalk

2、任意位置建立一個資料夾,資料夾中包含dump_syms、minidump_stackwalk、*.dmp、所有的動態庫檔案(多個動態庫需要重複3、4步驟)

3、執行命令./dump_syms libXXX.so > libXXX.so.sym

4、建立資料夾 symbols/libgame.so/6D7D7B4FAAE9D2686CF45FA12A9E3AD30,並將生成的libXXX.so.sym拷貝到該資料夾中,6D7D7B4FAAE9D2686CF45FA12A9E3AD30為生成的libXXX.so.sym的第一行的內容

5、執行命令./minidump_stackwalk XXXXXXXXXXX.dmp symbols/ > result.txt

好了,大功告成,我們來看一下解析出來的result.txt裡面崩潰的堆疊

Thread 0 (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
    Found by: given as instruction pointer in context

這是什麼鬼,_Z5crashv是什麼東西?應該是armv5架構的CPU,crash函式中崩潰,而且該函式的返回值是void,就這些資訊,如果NDK程式碼多的話, 還是很難定位到崩潰的地方 ,意義不是很大吶,好吧,這個是從libs中取出來的so庫,那麼我們試試obj中取出來的so去解析結果是怎麼樣的。

Thread 0 (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
    Found by: given as instruction pointer in context
 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
    Found by: call frame info

哇,這個結果就比較完美了,c++中崩潰的堆疊都有了,哪個檔案哪一行都有,但是呢,跟bugly比起來還是差了一些,因為bugly中可以看到java層呼叫的堆疊。結果是比較完美了,可是可是可是,操作還是麻煩了一點,如果我的應用有很多個動態庫的話,那解析一個dmp檔案就要好久了,很不幸,我曾經開發的應用裡面包含了將近10個庫,天啊,這就算再熟練解析一個檔案也要好幾分鐘啊,而且中途還不能出錯,殺了我吧,我不幹了。我的理念是,凡是重複的工作都讓電腦去做,指令碼是個很好的東西,下面就把shell指令碼放出來吧

#!/bin/bash

#[usage]
#將本指令碼、dump_sys、minidump_stackwalk放在同級目錄下,並建立libs資料夾,所有動態庫放到libs資料夾內
#./dump2txt.sh [dmp檔案路徑] [生成的txt檔案路徑]

LIBRARY_DIRECTORY="libs"
LIBRARY_EXTENDNAME=".sym"
LIBRARY_KEYPOS=3

Check()
{
	if [ $# -ne 2 ];then
		echo please input two param,the first param is the dmp file path,the second param is txt file path
		exit
	fi

	if [ ! -f "$1" ]; then
		echo $1 is not exsit
		exit
	fi
}

DealLibrary()
{
	if [ ! -f "$LIBRARY_DIRECTORY/$1" ]; then
		echo $LIBRARY_DIRECTORY/$1 is not exsit
		return
	fi

	SYM_NAME=$1$LIBRARY_EXTENDNAME
	./dump_syms libs/$1 > $SYM_NAME
	
	cat $SYM_NAME | while read line
	do
		LIBRARY_CODE=$line
		ARR=($LIBRARY_CODE)
		LIBRARY_CODE="${ARR[$LIBRARY_KEYPOS]}"
		mkdir -p symbols/$1/$LIBRARY_CODE
		mv $SYM_NAME symbols/$1/$LIBRARY_CODE
		break
	done
}

Main()
{
	#檢查引數 $1:dmp檔案路徑;$2:生成的txt檔案的路徑
	Check $1 $2
	echo "start convert "$1" to "$2"...."

	#建立解析dmp檔案相關的目錄以及檔案
	rm -rf symbols
	for file in $LIBRARY_DIRECTORY/*
	do
		DealLibrary ${file:5}
	done

	#生成txt檔案
	./minidump_stackwalk $1 symbols/ > $2
	echo $2 is generated!!!!
}
Main $1 $2

好了,寫到這裡本文也就接近尾聲了,什麼?不知道怎麼用shell指令碼?好吧,好人做到底,送佛送到西。新建檔案xxx.sh,雙擊開啟,將指令碼內容複製進去,控制檯執行命令chmod a+x xxx.sh,然後照著腳本里面註釋的內容做吧,Good Luck!沒有Ubuntu系統怎麼辦?我是裝了個虛擬機器跑Ubuntu系統,如果你是做NDK開發的,建議你也這麼做,比Windows方便太多了,那如果你不想裝,那就用你的手機試試吧,你的手機也是linux系統的,shell指令碼也是可以跑,但是本文中提出的dump_sys和minidump_stackwalk兩個檔案就得編譯arm版本的了,這個由於我沒有需求,有這個需求的人可以研究一下。

------End------

相關推薦

Android崩潰日誌獲取解析

今天來寫點Android崩潰的東西,在我們寫程式碼的過程中,程式碼寫的再好再嚴謹,也總是會有一些程式的崩潰,對於release出去的APP,我們肯定是希望我們能夠拿到崩潰的日誌,便於bug的發現以及修復,在下一個版本中再修復。所以,今天我們來說說Android崩潰日誌的抓取

Xamarin.Android獲取解析JSON

一、新建專案 1.新建一個Android專案,並命名為為NetJsonList 2.右擊引用,選擇新增引用,引用System.Json.dll 二、同步請求 既然是跨平臺,我們自然不能按照java下的方式進行編寫,否則如何跨平臺呢,所以我們需要使用Syste.Net名稱空間下的兩個類:

android 訪問web端解析json,模擬用戶登錄

android用戶登錄 與解析json數據 之前寫過一個java web端的登錄驗證,最後返回一個json字符串。字符串格式如下:{"appmsg":"賬號或密碼錯誤","appcode":0,"_default_boolean_a":false}今天就結合著Android來寫一個簡單的登錄。註意:

Android原生生成JSON解析JSON

JSON資料是一種輕量級的資料交換格式,在Android中通常應用於客戶端與伺服器互動之間的資料傳輸。像現在在網上有很多解析JSON資料的jar包,但是歸根到底用的都是Android原生解析JSON資料的方式,所以掌握Android原生解析JSON資料的方法相當重要。 下面

iOS開發之Crash日誌獲取分析

當在非除錯狀態下,我們用真機測試app,crash或者說閃退是一件很常見的事,最讓我們開發人員頭疼的是,自己在開發過程中總是不會遇到crash,安裝到別人的裝置,就出現了閃退崩潰現象。這種偶現的、概率比較低的閃退是最令人頭疼。 這時iOS crash log 

iOS 崩潰日誌 收集傳送伺服器

iOS開發中我們會遇到程式丟擲異常退出的情況,如果是在除錯的過程中,異常的資訊是一目瞭然,我們可以很快的定位異常的位置並解決問題。那麼當應用已經打包,iPhone裝置通過ipa的包

Android崩潰日誌收集

crash日誌收集這個已經有許多成熟的第三方庫實現了這個功能了,大多是以服務的方式提供,這裡有個上傳日誌的需求,在使用者本地的資訊需要採集和資料處理。 如果是創業公司,國內建議使用網易雲捕或者騰訊Bugly 國外建議使用Crashlytics 如果要自己搭建伺服器

iOS Crash日誌收集解析

Crash日誌收集與解析 一、本地crash日誌收集 1、 把發生crash的裝置連線到你的電腦上,用 iTunes 或 itools Mac OS X:~/Library/Logs/CrashReporter/MobileDevice Windo

未處理異常處理器 UncaughtExceptionHandler 實現 崩潰日誌儲存 重啟應用

前言 當我們編寫程式的時候 , 遇到會丟擲異常的方法的時候 , 我們一般會採取 try … catch 的方式: try { bitmap = BitmapFactory.decodeStream(getContentResolver()

iOS 原生的崩潰日誌收集傳送一

崩潰日誌工具類的建立 MyCrashExceptionHandler.h #import <Foundation/Foundation.h> @interface MyCras

Android學習】第三章 · 儲存容量的獲取&xml格式文字的建立解析

相對佈局:結合RelativeLayout九宮格     表格佈局和絕對佈局不常用,瞭解就好   谷歌替代system.out.println()用Log.v(d<i<w<e)(tag,”文字資訊”)   設定

獲取Android崩潰crash資訊並寫入日誌

Android崩潰是開發中不可避免的一件事,考慮不夠周全的程式碼、糟糕的網路環境、讓人頭疼的碎片化問題都可能導致crash,線上版本crash嚴重影響使用者體驗,所以crash的捕獲和收集對我們開發人員很重要。 〇、Exception的分類及捕獲 Java的異常

Android獲取崩潰日誌並上傳到郵箱

好吧,老闆要獲取崩潰日誌並上傳伺服器,已經實現了,這個比較簡單,主要說說上傳到郵箱的一個主意的地方:上傳到伺服器和郵箱需要4個jar包(android-crash-1.0.jar,activation.jar,additionnal.jar,mail.jar),我是在別人的部

Android開發中日誌錯誤資訊的獲取上報

1、背景介紹         在做Android開發過程中,開發階段,我們可以通過DDMS看到輸出的日誌資訊,或者是異常報錯,這個時候一般都是執行時一場,比如空指標,記憶體溢位等等問題,我們在開始做開發的時候就可以得到這些資訊。但是當我們的應用釋出之後呢,對於不同的一些機型

Android中XML文件的序列化生成解析

eval test director 南海 attribute trac cli found dir 首先,我把Person的實體類 package net.loonggg.test; public class Person { privat

Android網絡請求數據解析,使用Gson和GsonFormat解析復雜Json數據

byte 自動 content json對象 .sh cimage 超文本 getjson puts 版權聲明:未經博主允許不得轉載 一:簡介 【達叔有道】軟件技術人員,時代作者,從 Android 到全棧之路,我相信你也可以!閱讀他的文章,會上癮!You and m

Android 建立解析XML(一)—— 概述

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

iOS 獲取崩潰日誌

新建一個類CatchCrash @interface CatchCrash : NSObject void uncaughtExceptionHandler(NSException *exception); @end @implementation CatchCrash void un

android中常用的xml生成解析

總結了一下解析XML的三種方式。下圖為要解析的XML的格式。 解析了兩種方式的XML,一種是檔案,另一種是流。 程式碼中用到的許多的類是我虛構出來的,例如在第二種解析中用到的User類。使用者應該先寫這個類,寫出他們的屬性,並寫get和set方法才可以的。 <?xml

Android 建立解析XML(五)—— Dom4j方式

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!