| 更多精彩内容 |
|---|
| 👉个人内容分类汇总 👈 |
| 👉C++软件调试、异常定位 👈 |
Windows下封装的崩溃报告模块
如果找不到在哪下载可以下载我准备好的
qBreakpad 是 Qt 库,用于使用 google-breakpad 崩溃报告工具(并方便地使用它)。支持
可用下载我打包修改好的源码,包含了依赖文件
git clone https://gitee.com/mahuifa/q-breakpad.git
下载qBreakpad
git clone https://github.com/buzzySmile/qBreakpad.git
进入依赖文件夹third_party
cd qBreakpad/third_party


下载依赖仓库,注意: 是在qBreakpad/third_party文件夹下下载,或者下载后移动到qBreakpad/third_party文件夹下
# breakpad
git clone https://github.com/google/breakpad.git
# linux-syscall-support
git clone https://github.com/adelshokhy112/linux-syscall-support.git
# 然后将 breakpad 放至qBreakpad/third_party/beakpad
# 然后将 linux-syscall-support下的lss 放至qBreakpad/third_party
进入qBreakpad/handler文件夹
cd qBreakpad/handler
使用qtcreator 打开handler.pro

选择Release模式,点击锤子开始编译

编译完成后会在handler 文件夹下生成一个libqBreakpad.a文件,这就编译完成了

打qBreakpad/demo/program/program.pro工程


编译后发现报了如下3个错误信息,错误信息大概是google_breakpad::PEFile::TryGetDebugInfo未定义

打开minidump_writer.cc文件,搜索TryGetDebugInfo,找到如下位置

按着Ctrl键,鼠标点击TryGetDebugInfo,进入到pe_file.h头文件,这里是声明,那没定义大概是没有包含cc文件或者cpp文件

将鼠标放到下列位置就可以知道头文件位置了

发现pe_file.h头文件在qBreakpad/third_party/breakpad/src/client/linux/minidump_writer文件夹下,进入该文件夹后发现了pe_file.cc文件

在breakpad.pri文件夹中如下位置加入$$BREAKPAD_PATH/client/linux/minidump_writer/pe_file.cc \,然后将build-handler-Desktop_Qt_5_14_2_GCC_64bit-Release文件夹删除后重新编译handler.pro

编译完成后再去编译program.pro 就不会报错了,program.pro编译运行后会崩溃(正常情况,特意写的异常用于生成dmp文件),program.pro生成的可执行程序为test

进入可执行程序路径下,会发现生成了一个crashes 文件夹,dmp 文件就放在这个文件夹下

这个文件夹名称是在main.cpp文件中由QBreakpadInstance.setDumpPath("crashes");指定的。

dump_syms读取调试信息并生成Breakpad 符号文件;可以获取一个minidump及其相应的符号,并产生一个符号化的崩溃位置信息;
cd breakpad/ # 进入breakpad文件夹
mkdir build # 创建一个build文件夹
sudo chmod -R 777 ./ # 修改所有文件的权限
cd build/ # 进入build文件夹
../configure # 开始配置
sudo make # 使用make构建编译
使用make 编译时可能会出现下列错误,根据错误提示打开breakpad/src/third_party/lss/linux_syscall_support.h:文件,定位到2408行,将"rsp"删除
../src/third_party/lss/linux_syscall_support.h: In member function ‘bool google_breakpad::ExceptionHandler::GenerateDump(google_breakpad::ExceptionHandler::CrashContext*)’:
../src/third_party/lss/linux_syscall_support.h:2408:75: error: listing the stack pointer register ‘rsp’ in a clobber list is deprecated [-Werror=deprecated]
2408 | : "rsp", "memory", "r8", "r10", "r11", "rcx");

删除后如下所示,然后保存修改,重新使用sudo make 构建

重新编译后又出现了新的错误,错误信息提示strcmp不是std成员,strcmp位于string.h文件夹中
../src/common/dwarf/elf_reader.cc: In constructor ‘google_breakpad::ElfSectionReader<ElfArch>::ElfSectionReader(const char*, const string&, int, const typename ElfArch::Shdr&)’:
../src/common/dwarf/elf_reader.cc:200:15: error: ‘strcmp’ is not a member of ‘std’; did you mean ‘strcmp’?
200 | if ((std::strcmp(name, ".strtab") == 0 ||
| ^~~~~~
In file included from ../src/common/dwarf/elf_reader.cc:35:
/usr/include/string.h:137:12: note: ‘strcmp’ declared here
137 | extern int strcmp (const char *__s1, const char *__s2)
| ^~~~~~
../src/common/dwarf/elf_reader.cc:201:15: error: ‘strcmp’ is not a member of ‘std’; did you mean ‘strcmp’?
201 | std::strcmp(name, ".shstrtab") == 0) &&
| ^~~~~~
In file included from ../src/common/dwarf/elf_reader.cc:35:
/usr/include/string.h:137:12: note: ‘strcmp’ declared here
137 | extern int strcmp (const char *__s1, const char *__s2)

根据错误提示打开breakpad/src/common/dwarf/elf_reader.cc文件,将所有std::strcmp替换成strcmp,然后保存重新编译就可以编译通过了

执行sudo make install 安装编译后的breakpad。
进入可执行程序当前文件夹,使用dump_syms生成 符号文件,test 是program工程生成的可执行程序。
dump_syms test > test.sym

在program工程源码当前路径下创建symbols/test/B84C09F0458DE588E280087B600FDCC80三级文件夹
symbols;符号文件名称,如test.sym,则目录名为test;16进制编号,将其作为目录名。如下图所示:
将test.sym文件移动到symbols/test/B84C09F0458DE588E280087B600FDCC80文件夹下
将包含dmp 文件的crashes文件夹也移动到program工程源码路径下,如下图所示

使用minidump_stackwalk 生成堆栈跟踪信息
minidump_stackwalk crashes/470c3ed4-d8f5-470c-17786f91-4280144e.dmp symbols/ > error.log
打开error.log文件,根据可执行程序名称test 找到奔溃位置位于TestThread类crash()函数中,具体在TestThread.cpp文件的第34行。


注意:目前测试来看除零异常可用定位到文件、函数、行号,但是空指针异常很难定位到(不知道是不是我操作问题)
使用QWidget的空指针异常无法定位到,使用int空指针异常可用定位到。


windows下如果不想这么麻烦的编译qBreakPad或者不考虑跨平台,可以使用我封装的crashhandler模块。
使用到的源码和Linux下一样


进入qBreakpad/handler文件夹,双击打开handler.pro

注意编译器一定要选择MSVC,不能使用MinGW,这里演示就使用Release,如果需要Debug也是差不多的;

编译成功后会在qBreakpad\handler文件夹下生成一个qBreakpad.lib 文件

因为后续调用qBreakpad需要使用到qBreakpad.pri模块,这里使用文本编辑器打开qBreakpad.pri文件,加入下列两行代码,否则Release程序编译时不会生成.pdb符号文件;
msvc:CONFIG(release, debug|release) {
QMAKE_CFLAGS_RELEASE -= -O2 # 取消C优化
QMAKE_CFLAGS_RELEASE += -Zi # 生成调试信息,放到pdb文件中
QMAKE_CXXFLAGS_RELEASE -= -O2 # 取消C++优化
QMAKE_CXXFLAGS_RELEASE += -Zi # 生成调试信息
QMAKE_LFLAGS_RELEASE -= /INCREMENTAL:NO # 选择增量链接
QMAKE_LFLAGS_RELEASE += /DEBUG # 将调试信息放到PDB文件中
message(MSVC编译器Release关闭优化,生成调试信息使用)
}
mingw:CONFIG(release, debug|release) {
QMAKE_CFLAGS_RELEASE -= -O2 # 取消C优化
QMAKE_CFLAGS_RELEASE += -O0 # 显示指定禁止优化
QMAKE_CFLAGS_RELEASE += -g # 生成C调试信息
QMAKE_CXXFLAGS_RELEASE -= -O2 # 取消C++优化
QMAKE_CXXFLAGS_RELEASE += -O0 # 显示指定禁止优化
QMAKE_CXXFLAGS_RELEASE += -g # 生成C++调试信息
QMAKE_LFLAGS_RELEASE -= -Wl,-s # 取消Release模式删除所有符号表和重新定位信息的设置
QMAKE_LFLAGS_RELEASE += -g # 链接器生成调试信息
message(Mingw编译器Release关闭优化,生成调试信息使用)
}
# 如果不加unix,MinGW也会进入这里
unix:gcc:CONFIG(release, debug|release) {
QMAKE_CFLAGS_RELEASE -= -O2 # 取消C优化
QMAKE_CFLAGS_RELEASE += -O0 # 显示指定禁止优化
QMAKE_CFLAGS_RELEASE += -g # 生成C调试信息
QMAKE_CXXFLAGS_RELEASE -= -O2 # 取消C++优化
QMAKE_CXXFLAGS_RELEASE += -O0 # 显示指定禁止优化
QMAKE_CXXFLAGS_RELEASE += -g # 生成C++调试信息
QMAKE_LFLAGS_RELEASE -= -Wl,-O1 # 取消Release模式链接器优化
QMAKE_LFLAGS_RELEASE += -g # 链接器生成调试信息
message(GCC编译器Release关闭优化,生成调试信息使用)
}
打qBreakpad/demo/program/program.pro工程,如下图所示

直接点击左下角
开始编译运行,编译完成后程序运行就会崩溃,然后打开可执行程序路径,会发现生成

只要生成pdb、dmp两个文件就说明已经成功使用qBreakpad捕获了崩溃信息。
打开Visual Studio,将生成的dmp文件直接拖进vs中

点击1️⃣【设置符号路径】,进行如下设置;

设置完成后点击【确认】,然后点击【使用仅限本机进行调试】

如下图所示成功得到崩溃位置

新建一个qBreakpad文件夹,将qBreakpad.pri文件和handler文件夹移动到qBreakpad文件夹中,handler文件夹中包含qBreakpad.lib


新建工程TestCrashes,编译器选择和编译qBreakpad一样,使用Release

将打包的qBreakpad模块文件夹复制到新建的工程路径下

在工程的TestCrashes.pro文件中添加下列两行代码,加载qBreakpad模块
include(./qBreakpad/qBreakpad.pri)
INCLUDEPATH += ./qBreakpad

由于qBreakpad有使用到网络模块,所以在qBreakpad.pri文件中添加QT += network

在main.cpp文件中添加头文件#include "QBreakpadHandler.h"和QBreakpadInstance.setDumpPath("crashes");

在widget.cpp文件中设置一个除0异常 ,点击按键后程序就会异常崩溃

进入编译后的文件夹下,可以看见成功生成pdb符号文件和dmp文件。

通过VS成功定位崩溃位置。
