跨平台的Qt程序崩溃生成Dump文件Breakpad

跨平台的Qt程序崩溃生成Dump文件Breakpad

简介:

1、Window平台:可以使用“SetUnhandledExceptionFilter”来设置应用程序奔溃捕捉,这在崩溃的时候,这个函数指定的回掉函数就会被调用并返回崩溃详情。返回的崩溃详情为MiniDump格式。这个是windows提供的函数,因此必须包含“#include <Windows.h>”,具体的使用方法参考 Window端Qt Create dmp的生成与解析

2、 如果需要同时满足 Windows、macOS、Linux三个平台呢?

查阅相关资料,发现Qt并没有提供类似的功能。但是Google breakpad 提供了一整套的崩溃解析方案,并且breadpad 是为了解决c\c++异常尔存在的支持跨平台。

Google breakpad简单介绍

1、Breakpad可以捕获发布给用户的应用程序的崩溃,并记录软件崩溃的调试信息到“minidump”文件中。调试信息包括错误行号,报错详情,堆栈错误(stack traces)。软件崩溃时候把生成的“minidump”上传到自己的服务器上就可已方便的获取足够细致崩溃详情。Breakpad提供了简单的上传“minidump”到服务器的代码实现
2、BreakPad是开源协议下发布的项目由C++开发,目的在于捕捉各个系统平台下的C\C++开发的程序的崩溃详情。从而辅助修改bug。BreakPad支持的系统平台有:windows(Qt MinGW64 无法使用)、linux、mac、ios、solaris、android ndk

Google breakpad实现原理简单了解

1、不通平台实现方式
Windows:通过SetUnhandledExceptionFilter()设置崩溃回掉函数
Max OS:监听 Mach Exception Port 获取崩溃事件
Linux:监听 SIGILL SIGSEGV 等异常信号 获取崩溃事件

2、Dump格式

minidump是由微软开发的崩溃记录文件格式。minidump为二进制文件,体积小。为了保持统一,breakpad在其他系统下也选择生成minidump文件。

Google breakpad 的具体使用方法

1、使用源码

breakpad提供的是源码,源码提供了configure和Makefile来编译。编译之后获取到一个静态库文件和对应的头文件。在平台的应用软件工程中添加静态库即可使用。在breakpad的源码中有各个平台的使用说明文档,其中Linux平台下的使用方法如下:

#include "client/linux/handler/exception_handler.h"
int main(int argc, char* argv[]) {
    google_breakpad::MinidumpDescriptor descriptor("/tmp"); 
    google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, true, -1);
    crash();
    return 0;
}
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) {
    printf("Dump path: %s\n", descriptor.path());
    return succeeded;
}
void crash() {
    volatile int* a = (int*)(NULL);
    *a = 1;
}

1.1、descriptor指定当捕获到崩溃的时候生成minidump文件的储存路径

1.2、dumpCallback这个回掉函数是当保存完minidump文件之后通过这个回掉函数返回minidump文件的保存路径与文件文件名字

跨平台具体使用细则

当使用qt开发跨平台软件使用breakpad的时候,可以对各个平台下使用breakpad方法做一个封装。从而给qt应用程序提供统一的调用接口。(注意:不同平台调用breakpad的函数和头文件是不同的)

由于不同平台下不仅调用函数不同,编译方法也不尽相同。所以这个封装不是特别简单。因此使用使用gitHub上封装好的开源代码了。

这个封装库支持在windows、linux、macos平台下使用breakpad。并且有demo帮助你快速使用qBreakpad。

1 、编译qBreakpad

1.1 下载qBreakpad 源码

git clone --recursive https://github.com/buzzySmile/qBreakpad.git

1.2 下载Google breakpad源码

在源码 third_party目录下 下载最新的breakpad

cd third_party
git clone https://github.com/google/breakpad

1.3、在third_party目录下 下载最新的linux-syscall-support,下载好后把文件夹名字改为 “lss”:

git clone https://github.com/ithaibo/linux-syscall-support
mv linux-syscall-support lss

1.4、编译:

Mac、Linux:

在命令行切换到qBreakpad目录下,执行一下命令:“qmake”就会生成”Makefile”,然后执行“make”,等待编译完成,即可编译出“libqBreakpad.a”库文件。或者用Qt Creater 开“third_party/qBreakpad/qBreakpad.pro”这个文件,并进行编译即可,只不过编译得到的“.a”文件在qBreakpad并行以“build开头”的目录里面。需要手工放置“libqBreakpad.a”到“third_party/qBreakpad/handler”,主程序就能找到这个库:

Windows–MinGW:

(只能编译32、64的可以编译通过但是 无法正常使用):使用mingw在windows下编译可以可以选择用命令行编译(需要配置qmake和mingw32-make的环境变量)。也可以使用QtCreater 按照编译MacOS的下使用QtCreater的步骤编译。注意用mingw在windows下生成的静态库也是以“.a”结尾。但是这个“.a”库和MacOs或Linux下的库是不同的,无法混用。

Windows–MSVC:

用Qt Creater 首先打开“third_party/qBreakpad/qBreakpad.pro” 工程,切换到release下编译,在qBreakpad目录并行以“build开头”的目录里面的Handler目录下可以找到编译好的“libqBreakpad.lib”文件。把这个文件复制到“third_party/qBreakpad/handler”即可。然后用Qt create 编译你调用这个库的工程。调用库的方法参考上面的“Qt多个平台下使用breakpad的方法”。注意需要把工程在Qt creater 的左下角设置为release。然后可以成功编译。这里多次提到用release,是因为在debug下,无论是“libqBreakpad.lib”还是调用他的工程都没法正常编译。报错“MD_DynamicRelease”不匹配值“MDd_DynamicDebug”,没有找到解决办法。还好release 下可用,不影响发布应用程序。

2、qBreakpad 的使用

2.1、pro中的设置

#config for qBreakpad
#CONFIG -= app_bundle #配置上这个参数以后 你的图形界面程序就会以命令行方式运行,
CONFIG +=  warn_on # warn_off 则不能再次打开使用 会将exe 移动位置实现不能再次运行
CONFIG += thread exceptions rtti stl
macx: LIBS += -framework AppKit


#加入调试信息
QMAKE_CFLAGS_RELEASE += -g
QMAKE_CXXFLAGS_RELEASE += -g
#禁止优化
QMAKE_CFLAGS_RELEASE -= -O2
QMAKE_CXXFLAGS_RELEASE -= -O2
#release在最后link时默认有"-s”参数,表示"Omit all symbol information from the output file",因此要去掉该参数
QMAKE_LFLAGS_RELEASE = -mthreads -Wl   #此行经过测试可用可不用
#link qBreakpad library
include($$PWD/3rdparty/qBreakpad/qBreakpad.pri)
#end of config for qBreakpad

2.2 main.cpp中的设置

#include "mainwindow.h"
#include <QApplication>
#include "QBreakpadHandler.h"
void crash() { volatile int* a = (int*)(NULL); *a = 1; }
int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	QBreakpadInstance.setDumpPath(QLatin1String("crashes"));
	MainWindow w;
	w.show();
	crash();
	return a.exec();
}
3、解析生成的Dump

软件崩溃生成minidump文件之后,怎样从这个文件通过breakpad提供的工具生成Stack Trace的方法

要想获得stack track需要图示的几个步骤。其中用到的几个工具是:
symbol dumper: 对应到实际命令行工具是:dump_syms
minidump processor:对应到实际命令行工具是:minidump_stackwalk

这两个命令行工具是由breakpad以源码形式提供的,需要使用者在各个平台下编译生成可执行文件,然后才能使用

3.1、编译 dump_syms

Linux、macOS 进入到 breakpad目录,执行下名命令

./configure  
make

即可在对平平台 生成dump_syms

breakpad/src/tools/linux/dump_syms/目录生成“dump_syms”

breakpad/src/tools/mac/dump_syms/目录生成“dump_syms“

Window:

breakpad/src/tools/mac/dump_syms/目录 直接使用VS打开工程编译即可获得dump_syms

3.2、编译minidump_stackwalk

Linux,MacOS: 切换到breakpad所在的目录,然后依次执行以下两个命令:

./configure
make

然后在breakpad/src/processor/目录找到“minidump_stackwalk”可执行文件.

Widow: 后续补充 未找到相关文档

3.3 使用dump_syms 生成 symbols文件

Linux,MacOS:

把测试程序test和生成的可执行文件“dump_syms”放到同一目录下,然后执行以下命令生成symbols文件,生成的文件名字指定为:test.sym

./dump_syms ./test > test.sym

3.4 使用“minidump_stackwalk”生成Stack Trace文件

nux,MacOS:
把程序test崩溃生成的xxx.dmp 和test程序对应的test.sym
放到工具“minidump_stackwalk”所在的目录,然后执行以下程序。

head -n1  test.sym

得到test.sym这个文本文件的第一行,如下
MODULE mac x86_64 887D1A2C356F3401ABCCA76B666B3A810 test
然后执行命令:

mkdir -p ./symbols/test/887D1A2C356F3401ABCCA76B666B3A810
mv test.sym ./symbols/LedStripEditor/887D1A2C356F3401ABCCA76B666B3A810
./minidump_stackwalk  xxx.dmp  ./symbols > result.txt 2> process.txt

必须严格按照上方命令执行,比如你的应用名称叫做“test”则命令中用到“test”地方名字必须都是这个名字。否则也会生成Stack Trace但是里面只是内存地址,直接看不出出错的地方。只有按照上面的步骤才能正确加载test.sym文件,从而直观的显示出崩溃详情,包括出错的函数或变量名字以及行号。以上命令把“Stack Trace”记录在result.txt文件中。把处理过程记录在文件process.txt中。
如果不设置分成">"和“2>”分别输入到两个文件,则输入部分都会输出到一个文件中,即标准输出。而实际需要看的只有result.txt的部分。为了stack trace文件清晰明了。建议这两个文件单独输出。或标准错误输出到null:“2> /dev/null”

3.5 命令脚本

为了简便上线繁琐的步骤,可以使用脚本来实现解析,脚本如下:

#!/bin/bash

if [ $# != 2 ] ; then 
echo "USAGE: $0 EXE_NAME DMP_NAME" 
echo " e.g.: $0 test 3872B2CF-983B-4963-AFA9-C8534DFD4C44.dmp" 
exit 1; 
fi 

#get input param
exe_file_name=$1
dmp_file_name=$2

getSymbol() {
    echo "@getSymbol: start get symbol"
    ./dump\_syms ./$exe_file_name > $exe_file_name'.sym'
}

getStackTrace() {
    echo "@getStackTrace: start get StackTrace"
    sym_file_name=$exe_file_name'.sym'

    #get first line of $sym_file_name
    line1=`head -n1 $sym_file_name`
    #echo $line1

    #get version number from string of first line
    OIFS=$IFS; IFS=" "; set -- $line1; aa=$1;bb=$2;cc=$3;dd=$4; IFS=$OIFS 
    #echo $dd
    version_number=$dd

    #make standard dir and move *.sym in it
    mkdir -p ./symbols/$exe_file_name/$version_number
    mv $sym_file_name ./symbols/$exe_file_name/$version_number

    #print stack trace at std output
    ./minidump_stackwalk $dmp_file_name ./symbols 2> /dev/null 

    #print stack trace at a file
    #./minidump_stackwalk $dmp_file_name ./symbols 2>/dev/null >result.txt
}

main() {
    getSymbol 
    if [ $? == 0 ] 
    then 
        getStackTrace
    fi
}
# run main
main

把以上脚本保存成文件“dump_tool.sh” 并通过命令"chmod +x ./dump_tool.sh"增加可执行权限。

需要解析的时候执行如下命令

./dump_tool.sh  test 3872B2CF-983B-4963-AFA9-C8534DFD4C44.dmp

之后 就会在 symbols文件夹下生成test>3872B2CF-983B-4963-AFA9-C8534DFD4C44>text.sys

当然文件下生成3872B2CF-983B-4963-AFA9-C8534DFD4C44.dmp.text 里面就是具体的崩溃信息

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值