作者:lds(lds2012@gmail.com )
日期: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源码目录下通过运行命令:
将自动生成 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 ; }
static bool dumpCallback ( const google_breakpad:: MinidumpDescriptor& descriptor, void * context, bool succeeded) { printf ( “Dump path: %s\n” , descriptor. path ( ) ) ; return succeeded; }
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
时刻记住在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 官方文档
八. 参考资料