作者:lds([email protected])
日期:2017-03-24
一. BreakPad简介
Google breakpad是一个跨平台的崩溃转储和分析框架和工具集合。
Breakpad由三个主要组件:
- client,以library的形式内置在你的应用中,当崩溃发生时写 minidump文件
- symbol dumper, 读取由编译器生成的调试信息(debugging information),并生成 symbol file
- processor, 读取 minidump文件 和 symbol file ,生成可读的c/c++ Stack trace.
简单来说就是一个生成 minidump,一个生成symbol file,然后将其合并处理成可读的Stack trace。
二. MiniDump文件格式
minidump文件格式是由微软开发的用于崩溃上传,它包括:
- 当dump生成时进程中一系列executable和shared libraries, 包括这些文件的文件名和版本号。
- 进程中的线程列表,对于每个线程,minidump包含它在寄存器中的状态,线程的stack memory内容。这些数据都是未解析的字节流,Breakpad client通常没有调试信息(debugging information)能生成函数名,行号,甚至无法确定stack frame的边界。
- 其他收集关于系统的信息,如:处理器,操作系统高版本,dump的原因等等。
breakpad在所有平台上(windows/linux等)都统一使用minidump文件格式,而不使用core files,原因是因为:
- core files可能很大,而minidump比较小。
- core files文档不全
- 很难说服windows机器去生成core files,但可以说服其他机器来生成minidump文件。
- breakpad只支持一种统一的格式会比较简单,而不是同时支持多种格式。
什么是core files?core files是unit系统上程序崩溃时生成的文件。
参见:http://www.jianshu.com/p/8e1352a9638f
三. Symbols文件格式
symbols文件是基于纯文本的,每一行一条记录,每条记录中的字段以一个空格作为分隔符,每条记录的第一个字段表示这一行是什么类型的记录。
记录类型:
- 模块记录:
MODULE
operatingsystem architecture id name - 文件记录:
FILE
number name - 函数记录:
FUNC
address size parameter_size name - 行号记录:address size line filenum
- PUBLIC记录:
PUBLIC
address parameter_size name STACK WIN
STACK CFI
参见:https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
四. 不同平台的实现原理
默认情况下,当崩溃时breakpad会生成一个minidump文件,在不同平台上的实现机制不一样:
- 在windows平台上,使用微软提供的
SetUnhandledExceptionFilter()
方法来实现。 - 在OS X平台上,通过创建一个线程来监听
Mach Exception port
来实现。 - 在Linux平台上,通过设置一个信号处理器来监听
SIGILL
SIGSEGV
等异常信号。
当minidump被生成后,在不同平台上也使用不同的机制来上传crash dump文件。
参见:Windows SetUnhandledExceptionFilter
参见:Mac OS X Exception handling
参见:Catching Exceptions and Printing Stack Traces for C on Windows, Linux, & Mac
五. 异常处理机制
提供两种不同的异常处理机制:
- 同进程(in-process)
- 跨进程(out-precess)
因为在崩溃的进程写minidump文件是不安全的,所以三个平台(windows、linux、mac os)都提供跨进程的异常处理机制。
六. 在Linux平台使用breakpad
这里因为要研究Android上使用breakpad,所有主要研究linux平台,windows和mac平台的使用方法请自行参考文档。
6.1 构建Breakpad库
breakpad提供自动构建工具来构建linux client库和processor库。
在breakpad源码目录下通过运行命令:
./configure
make
将自动生成 src/client/linux/libbreakpad_client.a
文件, 其包含了在你的应用中生成minidumps文件的必要代码。
6.2 在你的应用中使用Breakpad
首先配置build precess来link刚生成的 libbreakpad_client.a
文件。
然后设置 include paths 来 包含 google-breakpad 目录下的 src 目录。
接下来include头文件:
#include "client/linux/handler/exception_handler.h"
现在你就可以初始化 ExceptionHandler
对象,初始化的时候需要提供一个用于写minidump文件的目录,以及一个回调函数用于在minidump文件写完以后调用。
int main(int argc, char* argv[]) {
// 初始化ExceptionHandler
google_breakpad::MinidumpDescriptor descriptor("/tmp"); // minidump文件写入到的目录
google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, true, -1);
crash();
return 0;
}
// 写完minidump后的回调函数
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
void* context, bool succeeded) {
printf("Dump path: %s\n", descriptor.path());
return succeeded;
}
// 触发crash来测试
void crash() {
volatile int* a = (int*)(NULL);
*a = 1;
}
编译并运行这个实例应该会在 /tmp 目录下生成 minidump 文件,并且终端下会输入minidump文件的路径。
关于初始化ExceptionHandler可以接受的参数需要参见源码
ExceptionHandler(const MinidumpDescriptor& descriptor,
FilterCallback filter,
MinidumpCallback callback,
void* callback_context,
bool install_handler,
const int server_fd);
具体参数:
- descripter,minidump文件写入的目录
- filter,可选,在写minidump文件之前,会先调用filter回调。根据它返回true/false来决定是否需要写minidump文件。
- callback, 可选,在写minidump文件之后调用的回调函数
- callback_context,
- install_handler, 如果为ture,不管怎样当未捕捉异常被抛出时都会写入minidump文件,如果为false则必须明确调用了
WriteMinidump
才会写入minidump 文件 - server_fd, 如果为-1,则使用同线程模式(in-precess),如果有一个有效的值,则使用跨线程模式(out-of-process)
参见:exception_handler.h源码
需要注意的是,你必须在callback回调函数中做尽量少的工作,因为你的程序处于一个不安全的状态,它需要无法安全的去分配内存,或调用其他共享库中的函数。安全的方式是 fork
和 exec
一个新进程去做想要做的事情。如果你需要在回调中做一些工作,breakpad源码提供一些简单的重新实现的libc库里的方法,来避免直接调用libc, 并提供一个a header file for making linux system calls,来避免直接调用其他共享库的方法。
6.3 发送minidump文件
在真实环境中,你通常需要以某种方式来处理minidump文件,例如把它发送给服务器来进行分析,Breakpad源码提供了一些HTTP上传的代码,并提供一个minidump上传工具( 详见minidump_upload.cc)。
6.4 生成symbols文件
为了生成可读的stack trace, breakpad需要你将binaries里的调试符号(debugging symbols)转换成基于文本格式的symbol files。
首先确保你在编译代码的时候加上 -g
参数来生成带调试符号的。
然后使用 configure && make
breakpad源码来生成 dump_syms
工具。
接着运行 dump_syms
命令来生成 symbol files,如下:
$ google-breakpad/src/tools/linux/dump_syms/dump_syms ./test > test.sym
为了可以使用 ``minidump_stackwalk` 工具来生成stack trace,你需要将文件放置在一定的目录结构,symbol file的第一行说明了需要放置的目录结构,可以使用命令,或使用 Mozilla 提供的 symbolstore.py 工具来新建这样的目录结构。
$ head -n1 test.sym
// MODULE Linux x86_64 6EDC6ACDB282125843FD59DA9C81BD830 test
$ mkdir -p ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830
$ mv test.sym ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830
6.5 生成Stack Trace
breakpad包含一个叫做 minidump_stackwalk
的工具来将 minidump 文件,外加symbol files来生成一个人可读的stack trace。在编译breakpad后,这个工具一般在 google-breakpad/src/processor
目录下, 通过将 minidump 和 symbol files 传入给它即可:
$ google-breakpad/src/processor/minidump_stackwalk minidump.dmp ./symbols
它会生成verbose output到stderr, stacktrace到stdout。
参见:How To Add Breakpad To Your Linux Application
七. 在Android平台使用Breakpad
Breakpad支持 ARM x86 和 MIPS 架构的Android系统。需要Android NDK r11c及以上版本。
7.1 构建client库
Android版本的Client库设计为一个静态库(static library)来使得你可以链接到你的Android native代码里。
一共有两种方法来build。
7.1.1 方法1:使用ndk-build构建(推荐)
使用ndk-build来构建,需要如下几个步骤:
- 在你的项目中的 Android.mk 中 include google-breakpad的 Android.mk
include $(LOCAL_PATH)/breakpad/android/google_breakpad/Android.mk
- 链接break-client未你的静态库模块
LOCAL_STATIC_LIBRARIES += breakpad_client
- 因为需要c++ STL,所以还需要在
Application.mk
文件中加入 STL 支持:
APP_STL := stlport_static
7.1.2 方法2:使用Android toolchain构建
$GOOGLE_BREAKPAD_PATH/configure --host=arm-linux-androideabi \
--disable-processor \
--disable-tools
make -j4
将会build到 src/client/linux/libbreakpad_client.a 文件。
可使用 make check
来在Android设备上运行测试。
7.2 在Android上使用Breakpad
在Android平台上使用breakpad,基本和在linux平台上是基本类似的。
需要如下几个步骤:
- 在cpp文件中include exception_handler头文件
#include "client/linux/handler/exception_handler.h"
- 如果使用ndk-build,需要配置include path(在Android.mk文件中)
LOCAL_C_INCLUDES := $(GOOGLE_BREAKPAD_PATH)/src/common/android/include \
$(GOOGLE_BREAKPAD_PATH)/src
并且加上 -llog flag
LOCAL_LDLIBS := -llog
- 时刻记住在Android系统上没有
/tmp
目录,需要指定一个其他目录,可以是 application 专有目录(/data/data/packageName/files),或者存储到SDCard上面去(需要写入权限)
7.3 生成stack trace
这个操作和其他平台一样。
- 从设备上获取minidumps文件
- 使用dump_syms工具生成so的symbol files
dump_symc $PROJECT_PATH/obj/local/$ABI/libfoo.so > libfoo.so.sym
- 创建symbol files专有目录结构
// MODULE Linux x86_64 6EDC6ACDB282125843FD59DA9C81BD830 test
$PROJECT_PATH/symbols/libfoo.so/6EDC6ACDB282125843FD59DA9C81BD830/libfoo.sym
- 使用
minidump_stackwalk
来生成stack trace:
minidump_stackwalk $MINIDUMP_FILE $PROJECT_PATH/symbols
参见:breakpad for android 官方文档
八. 参考资料
- breakpad项目主页
- breakpad官方文档
- breakpad for android 官方文档
- http://www.aichengxu.com/android/1527158.htm
- http://blog.linguofeng.com/archive/2014/04/02/google-breakpad-android.html
- http://www.jianshu.com/p/d91c14a67072
- http://www.jianshu.com/p/8e1352a9638f