一前情回顾
最近把公司的一个视频处理程序更新了一个版本, 准备提交测试的发现了崩溃的情况这个程序采用 Qt 和 ffmpeg 技术栈开发, 主要用于对视频进行渲染拼接处理, 在 Windows 和 mac 两个平台同时进行发布在 windows 上测试完一切正常, 然而就在我以为一切大功告成的时候, 测试的同事直接给我来了个当头棒喝, 程序崩溃了! 没有道理啊, 同一套代码在 Windows 上安然无恙, 在 Mac 上为何直接崩溃? 好消息是程序在崩溃的时候保存了 dump 文件
这得感谢前段时间集成的 Google Breakpad 了 Google Breakpad 是 Google 开发的一个跨平台异常捕获和 dump 文件 (准确的说是 mini dump) 生成的开发库利用这个库可以在 Windows, Mac, Linux, iOS, Android 平台上对程序异常崩溃进行捕获, 并生成 dump 文件供后期调试据说 Google Chrome, Chromium, Firefox 都使用了这套机制, 因此其可用性是经得起考验的, 并且这个库现在依然更新的很频繁
如此强大的东西, 怎么使用呢? 好在网上关于 breakpad 的资料是还是挺多的, 只不过都不是很完整很简洁要么就只介绍了实现原理或者只介绍了怎么编译或者就只介绍了怎么集成, 对于新手使用非常不友善这里就根据我在 Windows 和 Mac 两个平台的使用经验来总结下吧
二 breakpad 的使用
breakpad 以源代码的形式发布, 所以首先要从仓库中把代码下下来:
git clone https://chromium.googlesource.com/breakpad/breakpad
这个是 Google 的代码仓库, 基于国内的环境需要把 VPN 打开下载下来的代码包含了 windows, mac, linux 三个平台所有的文件了, 也包含了各个平台的工具源码没错, breakpad 的工具需要自己编译
假设源代码下载到了 E:/breakpad, 那么进入到这个目录运行 make 命令:
./configure make
在 Windows 上需要用 gyp 工具来编译, 所以还得下载 gyp 非常麻烦在 mac 上就非常简单了, 直接运行上述命令即可生成静态库文件但是工具的话需要进入到 tools 目录, 里面有个已经配置好的 xcode 工程, 直接打开即可编译
不过要注意的是, 最新的 breakpad 源码在编译工具的时候会报错:
- Undefined symbols for architecture x86_64:
- "google_breakpad::BaseName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&)", referenced from:
- google_breakpad::DumpSymbols::CreateEmptyModule(google_breakpad::scoped_ptr<google_breakpad::Module>&) in dump_syms.o
- ld: symbol(s) not found for architecture x86_64
- clang: error: linker command failed with exit code 1 (use -v to see invocation)
解决办法是:
在这一步中我们编译源码主要是为了得到两个工具: minidump_stackwalk 和 dump_syms 这两个分别有什么用呢? dump_syms 用于从可执行程序中抽取出调试符号保存到 syms 符号文件中, 而 minidump_stackwalk 则根据 syms 文件来分析 mini dump 文件, 得到一个可读性强的崩溃调用堆栈由于我的工程是基于 Qt 的, 所以我直接利用了 Github 上面的一个开源项目进行编译这个项目针对 Qt 剔除了一些无用的头文件, 并对源代码做了稍微的调整
基于 QMake 的工程, 可以直接用 Qt Creator 打开编译在 Windows 上和 Mac 上无缝支持编译即可得到我们需要的 lib 文件了这个在我们后面集成工程中链接需要用到当然也可以直接将源代码集成到工程去
接下来就讲讲如何集成吧集成步骤其实非常简单, 直接上代码:
- #ifdef _WINDOWS
- #include <client/windows/handler/exception_handler.h>
- #else
- #include <client/mac/handler/exception_handler.h>
- #endif
- #ifdef _WINDOWS
- bool minidumpCB(const wchar_t *dump_path, const wchar_t *id, void *context, EXCEPTION_POINTERS *exinfo, MDRawAssertionInfo *assertion, bool succeeded) {
- #else
- bool minidumpCB(const char* dump_path, const char* id, void* context, bool succeeded) {
- #endif
- if (succeeded) {
- std::wcout <<"Mini Dump file:" << id << ".dump Path:" << dump_path << std::endl;
- }
- return succeeded;
- }
- int main() {
- #ifdef NDEBUG // 只在 Release 模式下启用 Breakpad
- #ifdef _WINDOWS
- google_breakpad::ExceptionHandler eh(dumpLocation.toStdWString(), NULL, minidumpCB, NULL, google_breakpad::ExceptionHandler::HANDLER_ALL);
- #else
- google_breakpad::ExceptionHandler eh(dumpLocation.toStdString(), NULL, minidumpCB, NULL, true, NULL);
- #endif
- #endif
- }
接口非常简单, 只要定义一个回调函数 minidumpCB()当程序崩溃被捕捉到的时候就会调用这个函数, 这里只是输出了 mini dump 文件保存的位置如果第一张截图中的红框所示
三 dump 文件如何利用
生成的 dump 文件如何利用? 如何转换成我们能看得懂的调用堆栈信息? 其实有上面编译出来的两个工具, 接下来的工作分三个步骤:
使用 dump_syms 生成符号表:
./dump_syms ~/Test/Caputre> Capture.syms
创建有层次的调试符号文件夹:
head -n1 Capture.syms // 查看文件层次
mkdir -p ./symbols/PanoramaCapture/3EXXXXXX$/ 这一步根据上面的输出来
mv Capture.syms ./symbols/PanoramaCapture/3EXXXXX$/ 将符号文件移动进去
利用 minidump_stackwalk 分析 dump 文件:
./minidump_stackwalk minidump.dmp ./symbols
最后一步将输出详细的堆栈信息:
相信有了这些信息, 找出代码中潜伏的 bug 不是什么难事了而我也正是根据这些信息, 成功解决了这次的崩溃问题再提一句, 不管在 Windows 上还是 Mac 上, 编译 Release 的时候最好把调试符号文件保存好这样利用 breakpad 来分析的时候才能事半功倍, breakpad 方才能展现其强大的一面
四参考链接
- https://www.jianshu.com/p/295ebf42b05b
- https://github.com/google/breakpad
- https://groups.google.com/forum/#!topic/google-breakpad-discuss/fierVnIAv1M
- https://github.com/gyunaev/google-breakpad-qt
来源: https://www.cnblogs.com/csuftzzk/p/mac_breakpad_qt_dump_debug.html