更多精彩内容 |
---|
个人内容分类汇总 |
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行。
注意:目前测试来看除零异常可用定位到文件、函数、行号,但是空指针异常很难定位到(不知道是不是我操作问题)
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️⃣【设置符号路径】,进行如下设置;
设置完成后点击【确认】,然后点击【使用仅限本机进行调试】
如下图所示成功得到崩溃位置
新建工程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成功定位崩溃位置。