嵌入式Linux开发中,程序崩溃问题如何高效定位?Google Breakpad 提供了一套跨平台的异常捕获与栈回溯机制。本篇将手把手带你完成 Breakpad 的编译安装、API 用法,并通过一个空指针崩溃的实例,演示从 dump 文件生成到符号解析、再转 core 文件用 GDB 调试的全流程。
1 Breakpad 的安装
在 Linux 下编译 Breakpad:
git clone https://github.com/google/breakpad.git
cd breakpad
./configure
make -j8
sudo make install
最新主线要求 C++20 支持,如果你的编译器版本较低会直接报错。

如果不打算升级 C++ 工具链,可以下载一个旧版本的 Breakpad 源码(例如 2021 年的版本)。
不过旧版本在 make 后仍可能出现缺少第三方库的提示:

此时需要手动下载 linux_syscall_support 源码,放到 src/third_party 目录下:
git clone https://github.com/cpp-pm/linux-syscall-support.git
注意调整文件夹名称使其与报错路径匹配,之后再执行 make 就可以顺利通过。

编译后需要关注以下几个关键产物:
src/client/linux/libbreakpad_client.a(链接到自己的程序)
src/tools/linux/dump_syms/dump_syms(提取符号)
src/processor/minidump_stackwalk(栈回溯)

make install 后,这些文件会被拷贝到系统路径,方便全局调用。

2 Breakpad 核心 API 简介
2.1 MinidumpDescriptor
用来描述 dump 输出位置(目录或文件描述符)。主要构造形式:
namespace google_breakpad {
class MinidumpDescriptor {
public:
// 构造1:传入目录路径
explicit MinidumpDescriptor(const char* directory);
explicit MinidumpDescriptor(const std::string& directory);
// 构造2:传入已打开的文件 fd
explicit MinidumpDescriptor(int fd);
// 启用 microdump 极简崩溃日志
void set_microdump(bool microdump);
// 获取接口
std::string path() const;
int fd() const;
bool IsFD() const;
bool microdump() const;
};
} // namespace google_breakpad
简化记忆:
google_breakpad::MinidumpDescriptor(const char* dir);
google_breakpad::MinidumpDescriptor(const std::string& dir);
google_breakpad::MinidumpDescriptor(int fd);
path(字符串):minidump 文件写入的目录路径
fd(int):直接写入已打开的文件描述符(如 socket、管道、文件)
2.2 ExceptionHandler
异常处理器,接管崩溃信号。完整构造函数如下:
namespace google_breakpad {
class ExceptionHandler {
public:
// 完整标准构造
ExceptionHandler(const MinidumpDescriptor& descriptor,
FilterCallback filter,
MinidumpCallback callback,
void* callback_context,
bool install_handlers,
int handler_types);
~ExceptionHandler();
};
} // namespace google_breakpad
简化:
google_breakpad::ExceptionHandler(
const MinidumpDescriptor& descriptor,
FilterCallback filter,
MinidumpCallback callback,
void* callback_context,
bool install_handlers,
int handler_types
);
各参数含义:
const MinidumpDescriptor& descriptor:minidump 输出描述符,统一管理 dump 文件生成策略。
FilterCallback filter:崩溃前置过滤,决定本次崩溃是否要产生 minidump。
- 返回
true:允许生成 dump
- 返回
false:忽略,不抓栈
- 若传
nullptr,则默认放行。
MinidumpCallback callback:后置回调,dump 写入完成后触发。可获得文件路径和写入结果,常用于上报或日志记录,不需要则传 nullptr。
void* callback_context:自定义上下文,会原样透传给 filter 和 callback 回调。
bool install_handlers:是否立即安装全局信号处理器。
true:构造时直接接管崩溃信号
false:后续手动安装。
int handler_types:需要捕获的信号类型,按位组合:
enum HandlerType {
// 不安装任何信号处理器
HANDLER_NONE = 0,
// 基础崩溃信号:SIGSEGV / SIGABRT / SIGFPE / SIGILL / SIGBUS
HANDLER_CRASH_EXCEPTIONS = 1 << 0,
// 捕获 SIGTERM
HANDLER_TERMINATE = 1 << 1,
// 捕获 SIGPIPE
HANDLER_PIPE = 1 << 2,
// 全部信号集合
HANDLER_ALL = -1
};
3 实战示例
3.1 示例代码——DumpCallback 演示
编写测试程序 test1.cpp:
#include <cstdio>
#include "client/linux/handler/exception_handler.h"
// 崩溃回调:告知 .dmp 路径
static bool DumpCallback(const google_breakpad::MinidumpDescriptor& desc,
void* context, bool succeeded)
{
printf("Breakpad dump saved: %s\n", desc.path());
return succeeded;
}
// 触发崩溃(空指针写)
void TestCrash()
{
volatile int* p = nullptr;
*p = 1;
}
int main()
{
// 初始化 Breakpad:dump 存 ./log/test1
google_breakpad::MinidumpDescriptor md("./log/test1");
google_breakpad::ExceptionHandler eh(md, nullptr, DumpCallback,
nullptr, true, -1);
printf("TestCrash...\n");
TestCrash(); // 触发崩溃
printf("TestCrash done\n");
return 0;
}
3.2 编译、运行,生成 .dmp 文件
g++ -g test1.cpp -o test1 -I/usr/local/include/breakpad -lbreakpad_client -lpthread
./test1

3.3 解析 .dmp 文件
3.3.1 使用 dump_syms 生成符号表
dump_syms test1 > log/test1/test1.sym

查看符号文件的前几行,提取模块 ID,并按照 Breakpad 的符号目录规范存放:
head test1.sym
# 根据首行 MODULE 信息建立 symbols 目录结构
mkdir -p symbols/test1/BAED630B5595363664CC4251DE58FAFA0
mv test1.sym symbols/test1/BAED630B5595363664CC4251DE58FAFA0/

3.3.2 使用 minidump_stackwalk 解析 dump
minidump_stackwalk fd49a534-53da-4c18-6479f988-5acc949e.dmp symbols/
3.3.3 解析结果分析

崩溃原因
Crash reason: SIGSEGV /SEGV_MAPERR
Crash address: 0x0
Process uptime: not available
- SIGSEGV:段错误(最常见的崩溃信号)
- SEGV_MAPERR:访问了未映射的非法地址
- Crash address: 0x0:访问地址
0x0 — 明显的空指针解引用!
崩溃栈回溯
Thread 0 (crashed)
0 test1!TestCrash() [test1.cpp : 16 + 0x4]
rax = 0x0000000000000000 rdx = 0x00007fb10cd838c0
...
rip = 0x00005609e111f7e0
Found by: given as instruction pointer in context
1 test1!main [test1.cpp : 26 + 0x5]
rbx = 0x0000000000000000 rbp = 0x00007fff1fcc2120
...
rip = 0x00005609e111f8b0
Found by: call frame info
解读:
- 第一行指出崩溃直接位置:
test1!TestCrash() 第 16 行(即 *p = 1 所在行),解引用空指针导致崩溃。
- 第二行指出调用者:
main 函数第 26 行调用了 TestCrash(),随后 TestCrash() 内部崩溃。
对照源码可以清楚看到对应行关系:

3.4 将 dmp 转为 core 文件并用 GDB 调试
有时我们想用 GDB 复现崩溃现场,可通过 minidump-2-core 工具将 .dmp 转为 core 文件。

minidump-2-core fd49a534-53da-4c18-6479f988-5acc949e.dmp > fd49a534-53da-4c18-6479f988-5acc949e.core
gdb ../test1 fd49a534-53da-4c18-6479f988-5acc949e.core
进入 GDB 后,常用的调试命令:
bt:查看崩溃调用栈
bt full:显示完整堆栈及局部变量

如果希望在程序内自动完成 dmp 到 core 的转换,可以在 DumpCallback 中调用系统命令:
static bool DumpCallback(const google_breakpad::MinidumpDescriptor& desc,
void* context, bool succeeded)
{
printf("Breakpad dump saved: %s\n", desc.path());
std::string corePath = std::string(desc.path(), strlen(desc.path()) - 4) + ".core";
std::string cmd = "minidump-2-core " + desc.path() + " > " + corePath;
printf("do cmd: %s\n", cmd.c_str());
system(cmd.c_str());
return succeeded;
}
4 总结
本文从 Google Breakpad 的编译安装开始,梳理了 MinidumpDescriptor 和 ExceptionHandler 的核心 API,并通过一个经典的 空指针 崩溃案例,完整演示了 dump 生成、符号提取及栈回溯的流程;最后还展示了如何将 dump 转为 core 文件,借助 GDB 进行深度调试。掌握了这套方法,再遇到嵌入式 Linux 程序的崩溃问题,你就能快速定位根源,而不再“盲猜”。更多技术分享可访问云栈社区。