参考文章: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
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行崩溃。
$ ./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中的哪个文件的那一行发生异常了,接下来可以使用日志追踪有问题的代码了。