首页 » 技术文章 » Google Breakpad 学习

Google Breakpad 学习

 

一. 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

五. 异常处理机制

提供两种不同的异常处理机制:

  1. 同进程(in-process)
  2. 跨进程(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回调函数中做尽量少的工作,因为你的程序处于一个不安全的状态,它需要无法安全的去分配内存,或调用其他共享库中的函数。安全的方式是 forkexec 一个新进程去做想要做的事情。如果你需要在回调中做一些工作,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来构建,需要如下几个步骤:

  1. 在你的项目中的 Android.mk 中 include google-breakpad的 Android.mk
include $(LOCAL_PATH)/breakpad/android/google_breakpad/Android.mk
  1. 链接break-client未你的静态库模块
LOCAL_STATIC_LIBRARIES += breakpad_client
  1. 因为需要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平台上是基本类似的。

需要如下几个步骤:

  1. 在cpp文件中include exception_handler头文件
#include "client/linux/handler/exception_handler.h"
  1. 如果使用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
  1. 时刻记住在Android系统上没有 /tmp 目录,需要指定一个其他目录,可以是 application 专有目录(/data/data/packageName/files),或者存储到SDCard上面去(需要写入权限)

7.3 生成stack trace

这个操作和其他平台一样。

  1. 从设备上获取minidumps文件
  2. 使用dump_syms工具生成so的symbol files
dump_symc $PROJECT_PATH/obj/local/$ABI/libfoo.so > libfoo.so.sym
  1. 创建symbol files专有目录结构
// MODULE Linux x86_64 6EDC6ACDB282125843FD59DA9C81BD830 test
$PROJECT_PATH/symbols/libfoo.so/6EDC6ACDB282125843FD59DA9C81BD830/libfoo.sym
  1. 使用 minidump_stackwalk 来生成stack trace:
minidump_stackwalk $MINIDUMP_FILE $PROJECT_PATH/symbols

参见:breakpad for android 官方文档

八. 参考资料

 

本文作者:鼎三

原文链接:Google Breakpad 学习,转载请注明来源!

0