Qt使用qBreakpad定位崩溃位置(2)

软件调试

Qt使用qBreakpad定位崩溃位置(2)

更多精彩内容
👉个人内容分类汇总 👈
👉C++软件调试、异常定位 👈

Windows下封装的崩溃报告模块

前言

1、Google - Breakpad

  • Breakpad 是 Google 公司开发的开源 跨平台C++崩溃检测库。
  • Breakpad可以捕获发布给用户的应用程序的崩溃,并记录软件崩溃的调试信息到 minidump 文件中。
  • 调试信息包括错误行号,报错详情,堆栈错误(stack traces)。

2、qBreakpad

  • qBreakpad 是 Qt 库,用于使用 google-breakpad 崩溃报告工具(并方便地使用它)。支持

    • Windows(但故障转储解码不适用于 MinGW 编译器)
    • Linux
    • macOS X
  • 地址

3、crashpad

  • Crashpad 是 Google 构建的最新开源崩溃报告工具,是流行的 Breakpad 崩溃报告器的继任者。 Crashpad 允许您在产品发生崩溃后将小型转储提交到配置的 URL。
  • 这里没有使用crashpad,以后再慢慢了解。

4、注意

  • dmp文件、pdb文件、可执行程序、源码 版本一定要一致,否则可能无法调试。

Linux下

个人观点:

  • Linux下Breakpad使用麻烦,而且Qt中有些异常无法定位到,不如GDB + Core好用;
  • 但是这个和Core使用不冲突,也可以一起用。

1、环境

  • 系统:ubuntu20.04.1
  • Qt版本:Qt5.14.2

2、qBreakpad源码准备

  • 可用下载我打包修改好的源码,包含了依赖文件

    git clone https://gitee.com/mahuifa/q-breakpad.git
    
  • 下载qBreakpad

    git clone https://github.com/buzzySmile/qBreakpad.git
    
  • 进入依赖文件夹third_party

    cd qBreakpad/third_party
    

    • 打开README文件,可以看见我们还需要下载breakpadlinux-syscall-support两个仓库,由于这两个仓库地址不方便访问,但是在github上也有,所以直接在Github下载就可以

  • 下载依赖仓库,注意:是在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
    

3、qBreakpad编译

  • 进入qBreakpad/handler文件夹

    cd qBreakpad/handler
    
  • 使用qtcreator 打开handler.pro

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

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

4、测试qBreakpad

  • 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");指定的。

5、dump文件调试

  • 由于dmp文件不能直接查看,定义崩溃位置,这里需要使用到2个工具
    • dump_syms:由beakpad编译生成,使用 dump_syms读取调试信息并生成Breakpad 符号文件;
    • minidump_stackwalk:由beakpad编译生成,minidump_stackwalk可以获取一个minidump及其相应的符号,并产生一个符号化的崩溃位置信息;

5.1 编译breakpad

  • 想要生成dump_syms、minidump_stackwalk 两个文件就需要编译breakpad,由于之前编译qBreakpad已经下载breakpad源码,这里就不需要再下载了;
  • lss文件夹移动到 breakpad/src/third_party/ 路径下

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。

5.2 开始分析dmp文件

  • 进入可执行程序当前文件夹,使用dump_syms生成 符号文件,test 是program工程生成的可执行程序。

    dump_syms test > test.sym
    

  • program工程源码当前路径下创建symbols/test/B84C09F0458DE588E280087B600FDCC80三级文件夹

    • 第一级目录,固定为symbols
    • 第二级目录,为即将放入的符号文件名称,如test.sym,则目录名为test
    • 第三级目录,在sym文件中第一行内容,有一串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下

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

1、环境

  • Windows10

  • Qt版本:Qt5.12.5

  • 编译器版本:msvc2017_64

2、源码准备

  • 使用到的源码和Linux下一样

    • qBreakpad
    • breakpad 放至qBreakpad/third_party/beakpad
    • linux-syscall-support下的lss 放至qBreakpad/third_party

3、qBreakpad编译

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

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

    • 打开handler.pro后选择Release,然后点击左下角的锤子,等待编译。

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

  • 因为后续调用qBreakpad需要使用到qBreakpad.pri模块,这里使用文本编辑器打开qBreakpad.pri文件,加入下列代码,否则Release程序编译时不会生成.pdb符号文件;

    # Windows下在Release生成用于调试dump的信息,包括【禁用release编译优化】、【生成PDB符号表】,但是这些设置会降低程序性能,和debug差不多
    CONFIG(release, debug|release) {
        QMAKE_CXXFLAGS_RELEASE -= -O2
        QMAKE_CXXFLAGS_RELEASE += -O0
        QMAKE_CXXFLAGS_RELEASE += /Zi
        QMAKE_LFLAGS_RELEASE += /DEBUG /OPT:REF /OPT:ICF                # 生成 PDB符号文件,功能和下一行一样,但是最好用这一行,显示指定编译选项
    #    QMAKE_LFLAGS_RELEASE += $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO
    
        # 打印变量参数值
        message(QMAKE_CXXFLAGS_RELEASE变量值:$$QMAKE_CXXFLAGS_RELEASE)
        message(QMAKE_LFLAGS_RELEASE变量值:$$QMAKE_LFLAGS_RELEASE)
    }
    

4、测试qBreakpad

  • qBreakpad/demo/program/program.pro工程,如下图所示

  • 直接点击左下角

    开始编译运行,编译完成后程序运行就会崩溃,然后打开可执行程序路径,会发现生成

    • crashes文件夹:存放dump文件
    • test.pdb:符号文件

  • 只要生成pdb、dmp两个文件就说明已经成功使用qBreakpad捕获了崩溃信息。

5、dump文件调试

  • 打开Visual Studio,将生成的dmp文件直接拖进vs中

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

    1. 如果是第一次调试需要勾选Microsoft符号服务器2️⃣:会从网络下载调试使用的符号文件,然后将符号文件下载到5️⃣位置,以后就不需要勾选符号服务器了;
    2. 点击“+”号3️⃣添加【test.pdb】6️⃣文件所在路径4️⃣

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

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

6、打包qBreakpad模块

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

7、测试打包的qBreakpad模块

  • 新建工程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成功定位崩溃位置。

热门相关:从现代飞升以后   童养媳之桃李满天下   重生娘子在种田   女配她逆袭了   间谍的战争