Google Breakpad Android

平羽
2023-12-01

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


由于目前使用Mac系统开发,Google Breadpad处理Android崩溃日志时需要Linux环境,借助vagrant可以非常方便地在Mac使用Ubuntu环境。

$ vagrant up
$ vagrant ssh
$ cd /vagrant
$ cd /ubuntu
$ 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 

集成 google-breakpad,比如集成 google-breakpad到cocos2d-x中

方法一:

拷贝 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)


接下来:

加入对 breakpad_client 的引用

LOCAL_WHOLE_STATIC_LIBRARIES += breakpad_client

包含搜索路径

$GOOGLE_BREAKPAD_PATH/src
$GOOGLE_BREAKPAD_PATH/src/common/android/include

在 Application.mk 文件添加 -llog  链接标志。

使用 google breakpad 的 c++代码:

#ifndef __ANDROIDCRASHDUMP_H__
#define __ANDROIDCRASHDUMP_H__

#include <android/log.h>
#include <string>
#include "client/linux/handler/exception_handler.h"
#include "common/linux/google_crashdump_uploader.h"
#include "common/using_std_string.h"

#define  LOG_TAG    "AndroidCrashDump"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)

using namespace cocos2d;

#define  DEFINE_PARAM(_NAME, _VALUE, _DIS) \
static std::string _NAME(_VALUE)

// 定义上传dump数据模块需要的参数, 当然,也可以动态获取。
DEFINE_PARAM(STR_crash_server, "http://192.35.4.29:8888/crashed/file/uploadFile",
             "The crash server to upload minidumps to.");
DEFINE_PARAM(STR_product_name, "MyCppGame",
             "The product name that the minidump corresponds to.");
DEFINE_PARAM(STR_product_version, "1.0.0",
             "The version of the product that produced the minidump.");
DEFINE_PARAM(STR_client_id, "0000000000000000000000000000000",
             "The client GUID");
DEFINE_PARAM(STR_minidump_path, "minidump.dmp",
             "The path of the minidump file.");
DEFINE_PARAM(STR_ptime, "100000",
             "The process uptime in milliseconds.");
DEFINE_PARAM(STR_ctime, "100000",
             "The cumulative process uptime in milliseconds.");
DEFINE_PARAM(STR_email, "libo@snail.com",
             "The user's email address.");
DEFINE_PARAM(STR_comments, "nothing",
             "Extra user comments");
DEFINE_PARAM(STR_proxy_host, "",
             "Proxy host");
DEFINE_PARAM(STR_proxy_userpasswd, "",
             "Proxy username/password in user:pass format.");
DEFINE_PARAM(STR_PackageName, "org.cocos2dx.hellocpp",
             "Application package name, for LibcurlWrapper dlopen libcurl.so.");



// 发生崩溃时的回调函数。
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
                         void* context,
                         bool succeeded)
{
    if (succeeded) 
	{
        LOGD(" ============== >>>> Dump success ");
    }
    // 初始化长传dump数据模块
    google_breakpad::LibcurlWrapper* http_layer = new google_breakpad::LibcurlWrapper(STR_PackageName.c_str());
    
    google_breakpad::GoogleCrashdumpUploader g(STR_product_name,
                                               STR_product_version,
                                               STR_client_id,
                                               STR_ptime,
                                               STR_ctime,
                                               STR_email,
                                               STR_comments,
                                               descriptor.path(),
                                               STR_crash_server,
                                               STR_proxy_host,
                                               STR_proxy_userpasswd,
                                               http_layer);
    // 上传数据
    succeeded = g.Upload();
    return succeeded;
}

static bool initAndroidCrashDump()
{
   // 参数为保存dmp文件的目录路径,路径必须存在。
    static google_breakpad::MinidumpDescriptor descriptor("/mnt/sdcard/temp");
    static google_breakpad::ExceptionHandler exceptionhandler(descriptor, NULL, dumpCallback, NULL, true, -1);
}

#endif //
在程序启动后调用  initAndroidCrashDump()开始崩溃捕获。

android下dump信息的解析:

回到ubuntu环境,建立一个 Dump目录,将/mnt/sdcard/temp 目录下.dmp文件拷贝到 Dump目录,把生成的 minidump_stackwalk 

和 dump_syms 也拷贝到这个目录,还有android项目下的obj/local/armeabi/libxxx.so库也拷贝到这个目录,

执行命令

$ cd /Dump
$ dump_syms libxxx.so > libxxx.so.sym

查看libxxx.so.sym文件内容,头部会有,BB0351B14DDA42A6D36FA6EA358B49D50 这样的字符串

MODULE Linux arm BB0351B14DDA42A6D36FA6EA358B49D50 libxxx.so

继续执行命令

$ mkdir -p symbols/libxxx.so/BB0351B14DDA42A6D36FA6EA358B49D50/
$ mv libxxx.so.sym symbols/libxxx.so/BB0351B14DDA42A6D36FA6EA358B49D50/
这样就导出了符号文件,接下来导出崩溃堆栈

$ ./minidump_stackwalk xxxx-xxxx-xxxxx-xxxx.dmp symbols > crashed.log
确保.dmp文件和 symbols目录是同级的。

打开 crashed.log

Thread 12 (crashed)
 0  libcocos2dcpp.so!AppDelegate::applicationDidFinishLaunching() [AppDelegate.cpp : 44 + 0x4]
     r0 = 0x00000000    r1 = 0x00000001    r2 = 0x5a18a8e8    r3 = 0x5a18a6b8
     r4 = 0x56ea70f0    r5 = 0x5c5c47b8    r6 = 0x00000000    r7 = 0x59f59f24
     r8 = 0x5e5b6c58    r9 = 0x59f59f1c   r10 = 0x5c5c47c8   r12 = 0x00000000
     fp = 0x5e5b6b60    sp = 0x5e5b6b18    lr = 0x5dc2d3e4    pc = 0x5db3e37c
定位到是 AppDelegate.cpp的44行崩溃。
如果没有符号文件,比如这样导出crash log

$ ./minidump_stackwalk xxxx-xxxx-xxxxx-xxxx.dmp > crashed.log
crashed.log 中的信息就是这样了

Thread 12 (crashed)
 0  libcocos2dcpp.so + 0x2ee37c
     r0 = 0x00000000    r1 = 0x00000001    r2 = 0x5a18a8e8    r3 = 0x5a18a6b8
     r4 = 0x56ea70f0    r5 = 0x5c5c47b8    r6 = 0x00000000    r7 = 0x59f59f24
     r8 = 0x5e5b6c58    r9 = 0x59f59f1c   r10 = 0x5c5c47c8   r12 = 0x00000000
     fp = 0x5e5b6b60    sp = 0x5e5b6b18    lr = 0x5dc2d3e4    pc = 0x5db3e37c
这个时候我们就要使用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/aa.so 0003deb4

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

 类似资料:

相关阅读

相关文章

相关问答