breakpad的简单使用

前言

Breakpad是一组实现崩溃报告系统的客户机和服务器组件。我们可以在windows、linux、mac os上使用它生成dump文件。

放一张 Breakpad 官方的架构图:

在这里插入图片描述

Google Breakpad 源码解析中写到,Breakpad 主要由三个部分组成:

  • Client,当端上发生崩溃时,会默认生成 minidump 文件。
  • Symbol Dumper,这个工具用于生成 Breakpad 专属的符号表,要作用在带有调试信息原始库才行。
  • Processor,这个工具通过读取 Client 生成的 minidump 文件,再去匹配 Symbol Dumper 生成的对应符号表,最后生成人类可读的 C/C++ 堆栈跟踪。

关于client部分,我读了下官方文档的client_design。它的一般使用过程是:1)将client库链接到需要监测崩溃的程序。2)给监测程序安装一个异常处理程序,并提供回调函数,生成minidump文件… 安装异常处理程序因操作系统而异。Linux上通过信号的传递获知异常。

知道一个大概过程后,我们做些实验,来简单了解下这个工具的使用。


Linux上breakpad使用

下面,我按照这个代码的组织结构,尝试使用breakpad。

注:下面所有目录在/mnt/data/others路径下。每个人的路径是各种各样的。

# 按照下面结构组织代码
.
├── breakpad #源码
├── breakpad_output #breakpad经过编译后生成的头文件和库
├── breakpad_test #自己的代码,用于测试breakpad的使用
├── depot_tools #拉取breakpad源码的工具
└── doc #文档

编译breakpad

如果下载breakpad的源码后,直接进行编译,会报一些错误,处理过程可参考:breakpad简单使用。或者可以尝试这个仓库代码,当它可能比较旧,也不可信:google-breakpad可编译通过的原始代码

我没有尝试上面的过程。我参考google/breakpad-README,使用depot_tools,进行代码拉取(这对网络有些要求)。

# depot_tools目录
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=/mnt/data/others/depot_tools/:$PATH

# breakpad目录
mkdir breakpad && cd breakpad
fetch breakpad
cd src
./configure && make # 先测试下能否编过

# 安装到指定的breakpad_output目录
./configure --prefix=/mnt/data/others/breakpad_output
make install

我们看下编译生成的头文件和库。

➜  breakpad_output tree -L 2                
.
├── bin
│   ├── core2md
│   ├── dump_syms
│   ├── dump_syms_mac
│   ├── microdump_stackwalk
│   ├── minidump-2-core
│   ├── minidump_dump
│   ├── minidump_stackwalk
│   ├── minidump_upload
│   ├── pid2md
│   └── sym_upload
├── include
│   └── breakpad
├── lib
│   ├── libbreakpad.a
│   ├── libbreakpad_client.a
│   └── pkgconfig
├── libexec
│   └── core_handler
└── share
    └── doc

项目中集成breakpad

breakpad编译完之后,我们看下,如何将其集成到现有的项目中。参考链接:linux_starter_guide.mdbreakpad崩溃日志收集

C++没有包管理器,使用一个外部库,需要添加头文件和库的搜索路径,对库进行链接。breakpad亦是如此。

我们使用的测试用例,是链接中提供的demo。

#include "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[]) {
  google_breakpad::MinidumpDescriptor descriptor("./");
  google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, true, -1);
  crash();
  return 0;
}

上面代码中,我们创建了一个ExceptionHandler实例。其生命周期,即是可以处理的崩溃的时期。所以,我们应该尽早的创建,尽量晚的销毁这个对象。ExceptionHandler有多个参数。第一个参数指定写入minidumps的位置;第三个参数是个回掉函数,用于接收关于写入的迷你转储的信息。

更多信息见本节的参考链接,如:ExceptionHandler更多的参数信息;通过http发送minidumps信息;

接下来,我们在CMakeLists.txt中,指定头文件和库的位置,并链接库。(如果make install在标准路径中安装了breakpad,可以使用pkg-config来找到具体安装位置和链接库。)

cmake_minimum_required(VERSION 3.11)

project("breakpad_test")

include_directories(${CMAKE_SOURCE_DIR}/../breakpad_output/include/breakpad)
link_directories(${CMAKE_SOURCE_DIR}/../breakpad_output/lib)
message(STATUS "breakpad header path: ${CMAKE_SOURCE_DIR}/../breakpad_output/include/breakpad")

add_executable(${PROJECT_NAME} main.cpp)

target_link_libraries(${PROJECT_NAME} breakpad_client pthread)

编译debug版本的程序

从二进制文件的debugging symbols中生成text-format symbol files是个比较“正常“的过程。(后面,我们也尝试下从release的二进制文件中生成text-format symbol files,看下是什么现象。不了解原理,就只好多尝试了。)

# breakpad_test目录
# 编译debug版本
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=release ..
make

# 根据二进制程序的调试信息,导出 text-format symbol files
../../breakpad_output/bin/dump_syms breakpad_test > breakpad_test.sym 

# 为了在minidump_stackwalk中使用上面生成的symbol file, 我们需要将其放在特定的目录下。
head -n1 breakpad_test.sym
MODULE Linux x86_64 2CE6C807FC66681B9855B67570D894310 breakpad_test
mkdir -p ./symbols/breakpad_test/2CE6C807FC66681B9855B67570D894310
mv breakpad_test.sym ./symbols/breakpad_test/2CE6C807FC66681B9855B67570D894310

# ----------------------------------------------#
# 上面这个过程可能有点麻烦。可以使用[symbolstore.py](https://searchfox.org/mozilla-central/source/toolkit/crashreporter/tools/symbolstore.py)。
# 这个脚本对版本有要求,3.7.3上mozbuild不满足安装条件
# pip3 install buildconfig pyyaml mozbuild
# symbolstore.py <params> <dump_syms path> <symbol store path> <debug info files or dirs>

接下来,我们运行程序,触发崩溃。在运行之前,我们去除程序的符号信息。这里使用strip命令,或者重新编译下release版本也行。

# 去除符号信息
strip breakpad_test

# 产生minidump文件
./breakpad_test                                                      
Dump path: .//4d890aed-5948-46dd-18df4bb4-4bbe01bb.dmp
[1]    13091 segmentation fault  ./breakpad_test

接下来,我们查看下这个dump文件。

方法一,将这个minidunp转换成coredump,然后使用gdb。此时,不再需要上面的symbol file。按照coredump进行调试即可。

# minidunp转换成coredump
../../breakpad_output/bin/minidump-2-core 4d890aed-5948-46dd-18df4bb4-4bbe01bb.dmp > core 

# 由于此时的breakpad_test没有符号信息,看不出更详细的内容
gdb breakpad_test core 
Core was generated by `./breakpad_test'.
Program terminated with signal SIGSEGV, Segmentation fault.

# 重新编译一份breakpad_test的debug版本,也可以看到错误信息
Core was generated by `./breakpad_test'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000402bab in crash () at /mnt/data/others/breakpad_test/main.cpp:9
9       void crash() { volatile int* a = (int*)(NULL); *a = 1; }

因为breakpad是跨平台的库,所以上面过程可行。下面我们看下通用(跨平台)的做法。

#  从minidump中生成stack trace
../../breakpad_output/bin/minidump_stackwalk 4d890aed-5948-46dd-18df4bb4-4bbe01bb.dmp ./symbols > stack_trace.txt 2>&1

# 查看下堆栈信息
.....
Crash reason:  SIGSEGV /SEGV_MAPERR
Crash address: 0x0
Process uptime: not available

Thread 0 (crashed)
0  breakpad_test!crash() [main.cpp : 9 + 0x4]
.....
1  breakpad_test!main [main.cpp : 14 + 0x5]
....

编译release版本的程序

对release版本的程序,重复上面过程。无法得到有效的信息。

cmake -DCMAKE_BUILD_TYPE=Release ..
make

../../breakpad_output/bin/dump_syms breakpad_test > breakpad_test.sym

head -n1 breakpad_test.sym
mkdir -p ./symbols/breakpad_test/D9F881C0A5EA59DF847F6AA6317737090
mv breakpad_test.sym ./symbols/breakpad_test/D9F881C0A5EA59DF847F6AA6317737090

./breakpad_test                                                      
Dump path: .//70067b88-cc34-4008-b018be8b-b5841440.dmp
[2]    30645 segmentation fault  ./breakpad_test

../../breakpad_output/bin/minidump_stackwalk 70067b88-cc34-4008-b018be8b-b5841440.dmp ./symbols > stack_trace.txt 2>&1

# 可以看到,无法得到有效的信息
.....
Crash reason:  SIGSEGV /SEGV_MAPERR
Crash address: 0x0
Process uptime: not available
....
Thread 0 (crashed)
 0  breakpad_test!main + 0xe2
...
1  libc.so.6 + 0x2409b

windows上breakpad的使用

PS: 下面过程,我没实验过。

windows上和Linux上程序的调试信息,在存储上有所区别。Linux上调试信息和二进制文件绑定在一起。

windows可以编译生成release版本的时候,将调试信息存储在pdb文件中,需要在编译的时候加上/Zi参数

使用VS进行编译,可以参考:VS2015 让Release程序生成pdb文件

使用cmake,可以参考:how to generate pdb files for Release build with cmake flags?CMAKE_BUILD_TYPE == Release should set /Zi (or /Z7) for MSVC++

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

da1234cao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值