开发过程中,最担心的问题就是程序崩溃,而且还不知道崩溃的原因,现在使用Google Breakpad来跟踪崩溃的位置,非常方便;由于目前使用Mac系统开发,Google Breadpad处理Android崩溃日志时需要Linux环境,借助 vagrant 可以非常方便地在Mac使用Ubuntu环境。

有了vagrant以后就方便了~

$ varant ssh$ cd /vagrant$ sudo apt-get update$ sudo apt-get install build-essential$ svn checkout http://google-breakpad.googlecode.com/svn/trunk/ google-breakpad-read-only$ cd google-breakpad-read-only$ ./configure$ make

执行完上面的命令将会在

  • google-breakpad-read-only/src/processor 目录下生成minidump_stackwalk 工具,用于导出crash log.
  • google-breakpad-read-only/src/tools/linux/dump_syms 目录下生成 dump_syms工具,用于导出符号文件
  • google-breakpad-read-only/src/client/linux 目录下生成 libbreakpad_client.a。
  1. 如果编译遇到 “cc1plus: warnings being treated as errors “错误,请将google-breakpad-read-only/Makefile文件中”* -Werror* “删除,重新编译。
  2. 如果make的时候遇到 “/lib64/libc.so.6: version `GLIBC_2.14’ not found “错误,说明 glibc 没有安装,安装方法请移步:http://my.oschina.net/zhangxu0512/blog/262275

集成 google-breakpad到Android项目中

1. 添加google-beakpad代码:
  • 方法一:

    • 拷贝 libbreakpad_client.a 到 jni 目录下。
    • 打开android项目的 Android.mk文件,在 LOCAL_PATH := $(call my-dir) 下加入如下代码:
include $(CLEAR_VARS)  LOCAL_MODULE := breakpad_client  LOCAL_MODULE_FILENAME := breakpad_client  LOCAL_SRC_FILES := libbreakpad_client.a  include $(PREBUILT_STATIC_LIBRARY)
  • 方法二:
$(call import-module,google-breakpad-read-only/android/google_breakpad) 
  • 方法三:
    • 将 google-breakpad 源代码里面的src文件夹拷贝到项目的jni目录下;
    • 修改Android.mk文件:
    LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := breakpad_clientLOCAL_CPP_EXTENSION := .cc .cppLOCAL_ARM_MODE := armLOCAL_SRC_FILES := \    main.cpp \    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.ccLOCAL_C_INCLUDES        := $(LOCAL_PATH)/src/common/android/include \                           $(LOCAL_PATH)/src                        LOCAL_C_INCLUDES += $(NDK_ROOT)/sources/cxx-stl/stlport/stlportLOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)LOCAL_EXPORT_LDLIBS     := -llogLOCAL_LDLIBS    := -llogLOCAL_LDLIBS += $(NDK_ROOT)/sources/cxx-stl/stlport/libs/armeabi/libstlport_static.ainclude $(BUILD_SHARED_LIBRARY)
2. 加入对googole-breakpad的引用:
LOCAL_WHOLE_STATIC_LIBRARIES += breakpad_client
3. 包含搜索路径:
$GOOGLE_BREAKPAD_PATH/src  $GOOGLE_BREAKPAD_PATH/src/common/android/include 

本文使用的是第三种集成方法。

4. 添加google breakpad 的 c++代码:
#include <jni.h>#include <time.h>   #include <stdlib.h> #include "com_test_crash_TestCrash.h"#include "client/linux/handler/exception_handler.h"#include "client/linux/handler/minidump_descriptor.h"#include "log.h"google_breakpad::ExceptionHandler* exceptionHandler;JavaVM *jvm = NULL;void onNativeCrash(const char* info) {    JNIEnv *env = 0;    int result = jvm->GetEnv((void **) &env, JNI_VERSION_1_6);    if (result != JNI_OK) {        LOGE("%s", "jvm->GetEnv null");        return;    }    jclass crashPinClass = env->FindClass(            "com/test/crash/TestCrash");    if (crashPinClass == NULL) {        LOGE("%s", "FindClass com/test/crash/TestCrash null");        return;    }    jmethodID crashReportMethod = env->GetStaticMethodID(crashPinClass,            "onNativeCrash", "(Ljava/lang/String;)V");    if (crashReportMethod == NULL) {        LOGE("%s", "GetMethod onNativeCrash null");        return;    }    jstring crashInfo = env->NewStringUTF(info);    env->CallStaticVoidMethod(crashPinClass, crashReportMethod, crashInfo);}// 崩溃回调bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,        void* context, bool succeeded) {    LOGE("Dump path: %s\n", descriptor.path());    onNativeCrash("");    return succeeded;}extern "C" jint JNI_OnLoad(JavaVM *vm, void* /*reserved*/) {    LOGE("JNI_OnLoad");    jvm = vm;    return JNI_VERSION_1_6;}#ifdef __cplusplusextern "C" {#endif// 通过java代码调用initial()方法传人dmp文件保存的路径JNIEXPORT void JNICALL Java_com_test_crash_TestCrash_initial(JNIEnv* env, jobject obj, jstring filepath) {    LOGE("The Momo native crash initial.");    const char *path = env->GetStringUTFChars(filepath, 0);    google_breakpad::MinidumpDescriptor descriptor(path);    exceptionHandler = new google_breakpad::ExceptionHandler(descriptor, NULL, DumpCallback, NULL,            true, -1);}// native crash 测试代码JNIEXPORT void JNICALL Java_com_test_crash_TestCrash_crash(JNIEnv *,        jobject) {    LOGE("The test native crash.");    volatile int* a = (int*)(NULL);    *a = 1;}#ifdef __cplusplus}#endif
5. 编译C++代码成.so文件(步骤略),通过加载libbreakpad_client.so,调用initial()初始化之后,就开始崩溃捕获。
public class TestCrash {    // 加载动态链接库(加载.so最好放在application中)    static {        try {            System.loadLibrary("breakpad_client");        } catch (Error e) {            e.printStackTrace();        }    }    // 初始化    public native void initial(String filePath);    // native crash测试代码方法    public native void crash();}
6. 解析生成的dmp文件:
  • 回到ubuntu环境,建立一个Dump目录:
$ mkdir Dump 
  • 分别将生成的.dmp文件、minidump_stackwalk、dump_syms、android项目下的obj/local/armeabi/libxxx.so库拷贝到Dump目录。( 特别说明:libxxx.so文件必须是obj/local/armeabi/ 文件夹下的,这个里面so文件包含了debug信息,相对lib下的so文件来说体积很大,有可能上百兆。)

    创建好的Dump文件夹路径在“个人 —> ubuntu —> Dump”;

  • 执行生成符号文件命令:

$ cd /Dump  $ dump_syms libxxx.so > libxxx.so.sym 
  • 查看libxxx.so.sym文件内容,头部会有,BB0351B14DDA42A6D36FA6EA358B49D50 这样的字符串:
$ head -n1 libxxx.so.symMODULE Linux arm BB0351B14DDA42A6D36FA6EA358B49D50 libxxx.so
  • 继续执行命令:
$ mkdir -p symbols/libxxx.so/BB0351B14DDA42A6D36FA6EA358B49D50/  $ mv libxxx.so.sym symbols/libxxx.so/BB0351B14DDA42A6D36FA6EA358B49D50/ 
  • 导出崩溃堆栈信息文件( 确保.dmp文件和 symbols目录是同级的 ):
$ ./minidump_stackwalk filename.dmp symbols > crashed.log
7. 打开 crashed.log 文件:
Thread 0 (crashed) 0  libxxx.so!Java_com_test_crash_TestCrash [TestCrash.cpp : 68 + 0x8]     r0 = 0x00000027    r1 = 0xc3dab5eb    r2 = 0x00000001    r3 = 0x00000000     r4 = 0x0000026a    r5 = 0x12f99f20    r6 = 0x12d63e00    r7 = 0x75c4a1b0     r8 = 0x1302b9e0    r9 = 0xb4f07800   r10 = 0x0000003a   r12 = 0xb6e98929     fp = 0x0000003a    sp = 0xbebb8f58    lr = 0xa289aafc    pc = 0xa289ab04
  • [TestCrash.cpp : 68 + 0x8]:定位到TestCrash.cpp的 第68行,从左到右第8个字符 ,产生的崩溃。
8. 如果没有生成符号文件,则 crashed.log 文件中的信息如下:

 
Thread 0 (crashed)
0 libxxx.so + 0xa289ab04
r0 = 0x00000027 r1 = 0xc3dab5eb r2 = 0x00000001 r3 = 0x00000000
r4 = 0x0000026a r5 = 0x12f99f20 r6 = 0x12d63e00 r7 = 0x75c4a1b0
r8 = 0x1302b9e0 r9 = 0xb4f07800 r10 = 0x0000003a r12 = 0xb6e98929
fp = 0x0000003a sp = 0xbebb8f58 lr = 0xa289aafc pc = 0xa289ab04

  • 通过上面的信息,我们没办法直观的看出哪里导致的崩溃,所以需要使用NDK中的arm-linux-androideabi-addr2line 工具定位崩溃;
  • arm-linux-androideabi-addr2line 路径为:$NDK_ROOT/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86/bin/arm-linux-androideabi-addr2line

  • usage: arm-linux-androideabi-addr2line -C -f -e <库路径> <内存地址>

arm-linux-androideabi-addr2line -C -f -e project/test/obj/local/armeabi/libxxx.so 0xa289ab04

这时候就可以在终端中看到是 libxxx.so 中的哪个文件的那一行发生异常了,接下来可以使用日志追踪有问题的代码了。



参考:http://blog.linguofeng.com/archive/2014/04/02/google-breakpad-android.html


更多相关文章

  1. Android(安卓)MVP 模式:简单易懂的介绍方式
  2. Chrome V8 引擎移植到 Android
  3. android TextView显示文字和图片
  4. 阿里路由框架ARouter简介
  5. Android(安卓)三言两语
  6. Android中ScrollView布局初始化显示的时候使页面置顶
  7. 【Android开发】构建第一个APP
  8. Binder框架 -- android AIDL 的使用
  9. Android(安卓)NDK学习笔记4-Android.mk篇

随机推荐

  1. Android(安卓)当前线程是否为主线程(UI线
  2. android状态栏右上角增加图标的方法
  3. 【Android(安卓)开发教程】预创建数据库
  4. Android事件处理分析
  5. SharedPreferenced
  6. Android视频分享-初学者你必须知道的那些
  7. Android内容提供器-读取通讯录
  8. 滚动选择packview风格的三级城市底部弹出
  9. android Canvas.drawBitmap 方法的理解
  10. Android(安卓)的Activity.getWindowManag