1、简介
1.1、常见崩溃
笔者抓取应用程序的崩溃,当时是在windows上用mingw编译器进行编译的程序,可以直接用微软提供的 SetUnhandledExceptionFilter() 方法来实现(msvc编译器略有不同)。如果在mac上用clang编译器,就不能用这一套了。
好奇读小伙伴会想,有没有一种在各个平台上都能用的崩溃处理方案呢?答案是肯定的。谷歌提供了一套跨平台的崩溃转储和分析框架和工具集合Google breakpad。
在c++开发中,很容易出现程序Crash而不出现任何提示的情况,尤其是在使用原生c++开发的情况,如果用了QT之类的框架还可能会输出一点点线索(也可能没有),在引入dump之前,我们都是用断点的方式debug,但是一旦发布出去,那就只能打印整个流程的所有log,来“云”调试。所以为了解决收集和调试发布之后的程序,需要收集程序崩溃之后的信息,微软在其Windows平台上提供了minidump,需要引用WinDbg的库,注册handler也可以生成,但是这一套方案不是特别好用,而且也不能跨平台,所以Breakpad是比较好的第二方案。
1.2、BreakPad
1.2.1、源码
Github的源码路径
https://github.com/google/breakpad
Googlesource
https://chromium.googlesource.com/breakpad/breakpad/
Wiki
https://wiki.rdkcentral.com/display/RDK/Google+Breakpad
1.2.2、原理
Breakpad is a library and tool suite that allows to distribute an application to users with compiler-provided debugging information removed. Breakpad library is linked with application which is executed on platform. When application crashes , it produces compact “minidump” files. These minidumps are send back to server and produce C and C++ stack traces.
wiki提供的部分原理图
BreakPad 主要包括三个部分:
- dumpSyms 负责读取用户开发应用中的debug信息,并生成特定的符号文件 https://github.com/google/breakpad/blob/master/docs/symbol_files.md
- client 在崩溃系统中也就是指的崩溃上报的sdk,负责抓取当前线程和当前载入的库生成minidump文件
- processor 是崩溃系统中的minidump_stackwalk,读取minidump文件 找到合适的符号文件 产生一个人类可读的c/c++调用栈
什么是minidump呢?
minidump文件格式一开始是微软为了崩溃处理自行开发的。minidump文件包含当前进程所载入的库包括库名和版本当前进程包含的线程,线程寄存器状态,栈内存内容这些数据都是字节流没有函数名称和代码行号其他的一些操作系统的版本之类的。
1.3、用BreakPad处理崩溃具体流程
我们的程序加入breakpad模块或库 ➡️ breakpad崩溃捕获 ➡️ 如果我们程序崩溃,则产生dmp文件 ➡️ 我们通过不同平台对dmp文件进行不同解读,分析出崩溃原因。
2、Qt中加入BreakPad
终于到很多Qter关心的话题了,我这里是直接加入的BreakPad源码,你也可以使用BreakPad的动态库和静态库。
2.1、在项目中引入BreakPad模块
include(google-breakpad/google-breakpad.pri)
2.2、google-breakpad.pri文件配置
根据具体的电脑情况去配置breakpad,我的配置仅作参考,关键点:mac上面记得引入系统库CoreFoundation。
# Hank.sha 2021.3
# ***************************************************************************
# Implemented using http://blog.inventic.eu/2012/08/qt-and-google-breakpad/
# as a the reference.
#
# Get Google Breakpad here: https://code.google.com/p/google-breakpad/
#
# The required breakpad sources have been copied into /src in order to make
# integration with the application smooth and easy.
#
# To use source from Google Breakpad SVN checkout instead, change $$PWD/src
# to path where it was checked out.
#
# ***************************************************************************
HEADERS += $$PWD/crash_handler.h
SOURCES += $$PWD/crash_handler.cpp
INCLUDEPATH += $$PWD
INCLUDEPATH += $$PWD/src
# Windows
win32:HEADERS += $$PWD/src/common/windows/string_utils-inl.h
win32:HEADERS += $$PWD/src/common/windows/guid_string.h
win32:HEADERS += $$PWD/src/common/scoped_ptr.h
win32:HEADERS += $$PWD/src/client/windows/handler/exception_handler.h
win32:HEADERS += $$PWD/src/client/windows/common/ipc_protocol.h
win32:HEADERS += $$PWD/src/client/windows/crash_generation/crash_generation_client.h
win32:HEADERS += $$PWD/src/google_breakpad/common/minidump_format.h
win32:HEADERS += $$PWD/src/google_breakpad/common/breakpad_types.h
win32:SOURCES += $$PWD/src/client/windows/handler/exception_handler.cc
win32:SOURCES += $$PWD/src/client/windows/crash_generation/crash_generation_client.cc
win32:SOURCES += $$PWD/src/common/windows/string_utils.cc
win32:SOURCES += $$PWD/src/common/windows/guid_string.cc
## Linux
#unix:HEADERS += $$PWD/src/client/linux/minidump_writer/cpu_set.h
#unix:HEADERS += $$PWD/src/client/linux/minidump_writer/proc_cpuinfo_reader.h
#unix:HEADERS += $$PWD/src/client/linux/handler/exception_handler.h
#unix:HEADERS += $$PWD/src/client/linux/crash_generation/crash_generation_client.h
#unix:HEADERS += $$PWD/src/client/linux/handler/minidump_descriptor.h
#unix:HEADERS += $$PWD/src/client/linux/minidump_writer/minidump_writer.h
#unix:HEADERS += $$PWD/src/client/linux/minidump_writer/line_reader.h
#unix:HEADERS += $$PWD/src/client/linux/minidump_writer/linux_dumper.h
#unix:HEADERS += $$PWD/src/client/linux/minidump_writer/linux_ptrace_dumper.h
#unix:HEADERS += $$PWD/src/client/linux/minidump_writer/directory_reader.h
#unix:HEADERS += $$PWD/src/client/linux/log/log.h
#unix:HEADERS += $$PWD/src/client/minidump_file_writer-inl.h
#unix:HEADERS += $$PWD/src/client/minidump_file_writer.h
#unix:HEADERS += $$PWD/src/common/linux/linux_libc_support.h
#unix:HEADERS += $$PWD/src/common/linux/eintr_wrapper.h
#unix:HEADERS += $$PWD/src/common/linux/ignore_ret.h
#unix:HEADERS += $$PWD/src/common/linux/file_id.h
#unix:HEADERS += $$PWD/src/common/linux/memory_mapped_file.h
#unix:HEADERS += $$PWD/src/common/linux/safe_readlink.h
#unix:HEADERS += $$PWD/src/common/linux/guid_creator.h
#unix:HEADERS += $$PWD/src/common/linux/elfutils.h
#unix:HEADERS += $$PWD/src/common/linux/elfutils-inl.h
#unix:HEADERS += $$PWD/src/common/linux/elf_gnu_compat.h
#unix:HEADERS += $$PWD/src/common/using_std_string.h
#unix:HEADERS += $$PWD/src/common/memory.h
#unix:HEADERS += $$PWD/src/common/basictypes.h
#unix:HEADERS += $$PWD/src/common/memory_range.h
#unix:HEADERS += $$PWD/src/common/string_conversion.h
#unix:HEADERS += $$PWD/src/common/convert_UTF.h
#unix:HEADERS += $$PWD/src/google_breakpad/common/minidump_format.h
#unix:HEADERS += $$PWD/src/google_breakpad/common/minidump_size.h
#unix:HEADERS += $$PWD/src/google_breakpad/common/breakpad_types.h
#unix:HEADERS += $$PWD/src/common/scoped_ptr.h
#unix:HEADERS += $$PWD/src/third_party/lss/linux_syscall_support.h
#unix:SOURCES += $$PWD/src/client/linux/crash_generation/crash_generation_client.cc
#unix:SOURCES += $$PWD/src/client/linux/handler/exception_handler.cc
#unix:SOURCES += $$PWD/src/client/linux/handler/minidump_descriptor.cc
#unix:SOURCES += $$PWD/src/client/linux/minidump_writer/minidump_writer.cc
#unix:SOURCES += $$PWD/src/client/linux/minidump_writer/linux_dumper.cc
#unix:SOURCES += $$PWD/src/client/linux/minidump_writer/linux_ptrace_dumper.cc
#unix:SOURCES += $$PWD/src/client/linux/log/log.cc
#unix:SOURCES += $$PWD/src/client/minidump_file_writer.cc
#unix:SOURCES += $$PWD/src/common/linux/linux_libc_support.cc
#unix:SOURCES += $$PWD/src/common/linux/file_id.cc
#unix:SOURCES += $$PWD/src/common/linux/memory_mapped_file.cc
#unix:SOURCES += $$PWD/src/common/linux/safe_readlink.cc
#unix:SOURCES += $$PWD/src/common/linux/guid_creator.cc
#unix:SOURCES += $$PWD/src/common/linux/elfutils.cc
#unix:SOURCES += $$PWD/src/common/string_conversion.cc
#unix:SOURCES += $$PWD/src/common/convert_UTF.c
##breakpad app need debug info inside binaries
#unix:QMAKE_CXXFLAGS+=-g
#mac
mac:{
BREAKPAD_PATH=$$PWD
HEADERS += $$BREAKPAD_PATH/src/client/mac/handler/exception_handler.h
HEADERS += $$BREAKPAD_PATH/src/client/mac/crash_generation/crash_generation_client.h
HEADERS += $$BREAKPAD_PATH/src/client/mac/crash_generation/crash_generation_server.h
HEADERS += $$BREAKPAD_PATH/src/client/mac/crash_generation/client_info.h
HEADERS += $$BREAKPAD_PATH/src/client/mac/handler/minidump_generator.h
HEADERS += $$BREAKPAD_PATH/src/client/mac/handler/dynamic_images.h
HEADERS += $$BREAKPAD_PATH/src/client/mac/handler/breakpad_nlist_64.h
HEADERS += $$BREAKPAD_PATH/src/client/mac/handler/mach_vm_compat.h
HEADERS += $$BREAKPAD_PATH/src/client/minidump_file_writer.h
HEADERS += $$BREAKPAD_PATH/src/client/minidump_file_writer-inl.h
HEADERS += $$BREAKPAD_PATH/src/common/mac/macho_utilities.h
HEADERS += $$BREAKPAD_PATH/src/common/mac/byteswap.h
HEADERS += $$BREAKPAD_PATH/src/common/mac/MachIPC.h
HEADERS += $$BREAKPAD_PATH/src/common/mac/scoped_task_suspend-inl.h
HEADERS += $$BREAKPAD_PATH/src/common/mac/file_id.h
HEADERS += $$BREAKPAD_PATH/src/common/mac/macho_id.h
HEADERS += $$BREAKPAD_PATH/src/common/mac/macho_walker.h
HEADERS += $$BREAKPAD_PATH/src/common/mac/macho_utilities.h
HEADERS += $$BREAKPAD_PATH/src/common/mac/bootstrap_compat.h
HEADERS += $$BREAKPAD_PATH/src/common/mac/string_utilities.h
HEADERS += $$BREAKPAD_PATH/src/common/linux/linux_libc_support.h
HEADERS += $$BREAKPAD_PATH/src/common/string_conversion.h
HEADERS += $$BREAKPAD_PATH/src/common/md5.h
HEADERS += $$BREAKPAD_PATH/src/common/using_std_string.h
HEADERS += $$BREAKPAD_PATH/src/common/convert_UTF.h
HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/minidump_exception_mac.h
HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/breakpad_types.h
HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/minidump_format.h
HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/minidump_size.h
SOURCES += $$BREAKPAD_PATH/src/client/mac/handler/exception_handler.cc
SOURCES += $$BREAKPAD_PATH/src/client/mac/crash_generation/crash_generation_client.cc
SOURCES += $$BREAKPAD_PATH/src/client/mac/crash_generation/crash_generation_server.cc
SOURCES += $$BREAKPAD_PATH/src/client/mac/handler/minidump_generator.cc
SOURCES += $$BREAKPAD_PATH/src/client/mac/handler/dynamic_images.cc
SOURCES += $$BREAKPAD_PATH/src/client/mac/handler/breakpad_nlist_64.cc
SOURCES += $$BREAKPAD_PATH/src/client/minidump_file_writer.cc
SOURCES += $$BREAKPAD_PATH/src/common/mac/macho_id.cc
SOURCES += $$BREAKPAD_PATH/src/common/mac/macho_walker.cc
SOURCES += $$BREAKPAD_PATH/src/common/mac/macho_utilities.cc
SOURCES += $$BREAKPAD_PATH/src/common/mac/string_utilities.cc
SOURCES += $$BREAKPAD_PATH/src/common/mac/file_id.cc
SOURCES += $$BREAKPAD_PATH/src/common/mac/MachIPC.mm
SOURCES += $$BREAKPAD_PATH/src/common/mac/bootstrap_compat.cc
SOURCES += $$BREAKPAD_PATH/src/common/md5.cc
SOURCES += $$BREAKPAD_PATH/src/common/string_conversion.cc
SOURCES += $$BREAKPAD_PATH/src/common/linux/linux_libc_support.cc
SOURCES += $$BREAKPAD_PATH/src/common/convert_UTF.cc
#LIBS += /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#LIBS += /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices
LIBS += /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation.tbd
#LIBS += /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices
QMAKE_CXXFLAGS+=-g
}
2.3、处理崩溃核心类
引入BreakPad核心处理
#if defined(Q_OS_LINUX)
#include "client/linux/handler/exception_handler.h"
#elif defined(Q_OS_WIN32)
#include "client/windows/handler/exception_handler.h"
#elif defined(Q_OS_MACX)
#include "client/mac/handler/exception_handler.h"
#endif
不同平台按照BreakPad要求设置不同崩溃回调
#if defined(Q_OS_WIN32)
bool DumpCallback(const wchar_t *_dump_dir, const wchar_t *_minidump_id, void *context, EXCEPTION_POINTERS *exinfo, MDRawAssertionInfo *assertion, bool success)
#elif defined(Q_OS_LINUX)
bool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context, bool success)
#elif defined(Q_OS_MACX)
bool DumpCallback(const char* dump_dir, const char* minidump_id, void* context, bool success)
不同平台初始化不同CrashHandler
#if defined(Q_OS_WIN32)
std::wstring pathAsStr = (const wchar_t *)dumpPath.utf16();
pHandler = new google_breakpad::ExceptionHandler(
pathAsStr,
/*FilterCallback*/ 0,
DumpCallback,
/*context*/
0,
true);
#elif defined(Q_OS_LINUX)
std::string pathAsStr = dumpPath.toStdString();
google_breakpad::MinidumpDescriptor md(pathAsStr);
pHandler = new google_breakpad::ExceptionHandler(
md,
/*FilterCallback*/ 0,
DumpCallback,
/*context*/ 0,
true,
-1);
#elif defined(Q_OS_MACX)
// std::wstring pathAsStr = (const wchar_t *)dumpPath.utf16();
pHandler = new google_breakpad::ExceptionHandler(
dumpPath.toStdString(),
NULL,
DumpCallback,
NULL,
true,
NULL);
#endif
2.4、如何使用这个核心类
在main.cpp中:
#include "google-breakpad/crash_handler.h"
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString path = qApp->applicationDirPath();
Breakpad::CrashHandler::instance()->Init(path);
MainWindow w;
w.show();
return a.exec();
}
2.5、Release模式下也生成
以上是Debug模式的调试是产生的信息,Release模式下如何产生呢?
pro模式下增加
DEFINES += QT_DEPRECATED_WARNINGS
QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO /DEBUG
QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO
3、产生崩溃,以及根据DMP文件调试具体代码
3.1、Mac上崩溃处理
3.1.1、产生dmp文件
崩溃后产生:
3.1.2、生成dsym文件
注意在调试minidump的时候需要用到程序的symbol(符号文件),QT中需要选择profile模式编译,则会在build目录生成一个.dSYM文件
生成后效果
注意:这里的dsym文件,是mac调试时要使用的哦!
3.1.3、编译dump_syms的breakpad工具
编译breakpad提供的工具的通用方法!
xcodebuild -project XXX.xcodeproj -configuration Release
编译好后,生成可执行程序
3.1.4、将.dsym文件转换成文本格式符号文件.sym
.dSYM是一个二进制文件,所以还不能直接用需要使用dump_syms将二进制符号文件转换为text-format symbol files(文本格式符号文件)
dump_syms XXXX.dsym > XXXX.sym
例如我的就是,这样,生成.sym文件的
3.1.5、将dmp和sym放到统一文件夹做处理
3.1.6、储存符号文件
为了让stack trace能让正常人看懂,还需要把文本格式的符号文件.sym放到指定的目录下面
新建一个目录为symbols/Demo_Crash_Breakpad/A7C790338F4D388EB5776A77D0292F430,再把sym文件放进去。
3.1.7、编译minidump_stackwalk的breakpad工具
编译方法和上面的不一样,直接拉取官网源码
编译
./configure && make
找到编译好的minidump_stackwalk文件
3.1.8、调试dmp文件
3.1.9、定位崩溃具体行数
我们已经生成了一个crash.log文件,并定位崩溃具体行数及原因
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210402170635596.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1OTcxOTg=,size_16,color_FFFFFF,t_70
3.1.10、源码及breakpad编译的库
mac下Qt使用breakpad的源码:https://download.csdn.net/download/u014597198/16349264
breakpad在mac上的编译好的库:
https://download.csdn.net/download/u014597198/16349346