参考链接:
- windows下捕获dump之Google breakpad_client的理解
- Google Breakpad:基本介绍和操作方法
- Breakpad 入门
- linux下用QT捕获程序异常
简介
github 地址
三大组件
- client:读取当前线程的状态、加载的可执行文件、共享库等信息,写入到 minidump 中。可以放到应用中,应用崩溃时自动使用或显式调用;
- symbol dumper:读取编译器生成的调试信息,产生基于 breakpad 格式的 symbol file;
- processor:读取 minidump 寻找适合的 symbol file,生成可读的 C/C++ 调用栈。
为什么是 minidump?
Breakpad 在所有平台都使用 Windows 的 minidump 文件,而不是传统的 core 文件,原因是:
- .core 文件可能非常大,无法通过网络发送到收集器进行处理。Minidump 更小;
- .core 文件格式的文档记录很差。比如 Linux Standards Base 没有描述在
PT_NOTE
段中寄存器是如何存储的; - Windows 机器生成 .core dump 文件比让其他机器生成 minidump 文件更难;
- 只有一个文件格式,简化了 breakpad 处理器。
minidump 的文件格式
包含:
- 该进程已加载的可执行文件和共享库列表,包括名称和版本;
- 进程中所有现场的列表,包括每个线程的寄存器状态、栈内容(纯字节流);
- 其他的系统信息,包括处理器、操作系统、崩溃原因等等。
minidump 的生成
默认情况下,breakpad 初始化时注册一个异常或信号处理函数,该函数能在异常发生时生成 minidump。注册方法有平台决定:
- Windows 中使用
SetUnhandledExceptionFilter()
; - OS X 中创建一个线程来等待 Mach 例外端口;
- Linux 安装一个能处理像 SIGSEGV、SIGKILL 之类的信号处理函数;
一旦生成了 minudump,每个平台都有略微不同的方式来上传崩溃转储。在 Windows 和 Linux 上,提供了一个单独函数库,可以调用它来执行上传。在 OS X上,会生成一个单独的进程,提示用户授予权限(如果配置为这样做)并发送文件。
进程内、外异常处理
通常认为,在崩溃的进程内写入 minidump 是不安全的,因为关键的进程数据结构可能已经被破坏,或者异常处理器所运行的栈可能已经被覆写等。所以 3 个平台都支持所谓的“进程外”异常处理。
编译
根据提示文档进行常规的 ./configure; make; make install
即可。
整合到程序中
可以根据平台,直接参考 docs 目录下的 xxx_starter_guide.md 文档。
步骤:
- 注意:项目编译时需要连接好
libbreakpad_client.a
这个库文件;比如 Qt 项目中的 .pro 文件:LIBS += -lbreakpad_client
;同时,避免部分文件找不到对应的头文件,在 .pro 中加入INCLUDEPATH += /usr/local/include/breakpad
; - 在
main.cpp
中添加测试代码,如下:
#include <QCoreApplication>
#include "breakpad/client/linux/handler/exception_handler.h"
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; }
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
google_breakpad::MinidumpDescriptor descriptor("/tmp");
google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, true, -1);
crash();
return a.exec();
}
- 编译并运行程序后,可知,程序会在调用
crash()
函数后崩溃。
堆栈信息分析
程序运行后,会出现如下:
Dump path: /tmp/7ca76e22-4044-46fc-e06318b6-28aea319.dmp
按 <RETURN> 来关闭窗口...
程序崩溃时,生成了 .dmp 文件。
分析 .dmp 文件
x86 平台编译完 breakpad 库后,可以得到一系列工具和库。
以下步骤均在 x86 平台执行:
- 确保程序使用了 -g 编译,使用 dump_syms 对程序生成一份符号文件 .sym;如
dump\_syms /home/twdz/PersonalTest/build-breakpadDemo-Desktop\_Qt\_5\_12\_8\_GCC\_64bit-Debug/breakpadDemo > ./breakpad.sym
; - 然后依次执行以下命令:
$head -n1 breakpadDemo.sym
MODULE Linux x86_64 7DDABFE2CC59CFD3ADF67085B2D231E60 breakpadDemo
$mkdir -p ./symbols/test/7DDABFE2CC59CFD3ADF67085B2D231E60
$mv breakpadDemo.sym ./symbols/test/7DDABFE2CC59CFD3ADF67085B2D231E60/
$minidump_stackwalk 7ca76e22-4044-46fc-e06318b6-28aea319.dmp ./symbols/ > stackTrace.log
- 将堆栈调用信息生成到 stackTrace.log 文件中,根据文件进行分析崩溃的位置,如:
3.1 从位置 1 可以看到崩溃的原因以及地址;发现地址为0x0
,依次往下寻找,最新的堆栈调用信息是在位置 2,而位置 3 说明了其具体的地址; - 使用命令
addr2line 地址 -e 可执行程序名字 -f
可以定位具体的崩溃位置,如:addr2line 0x402451 -e ./breakpadDemo -f
(这里采用 0x402451 这个地址是因为使用 0x0 和 0x2451 都无法获取到有效信息)。运行后,打印如下:
_Z5crashv
/home/twdz/PersonalTest/build-breakpadDemo-Desktop_Qt_5_12_8_GCC_64bit-Debug/../breakpadDemo/main.cpp:10
可知,main.cpp 文件第 10 行发生了崩溃。