如果对linux下如何生成dump,以及如何利用dump文件定位崩溃点,不清楚,可以先参考此文《Linux下Qt生成dump文件并定位bug(基于qBreakpad)》。
在上文中,我们以单文件程序为例,当异常发生于单文件程序内部时,定位崩溃点过程进行了说明。
Linux下多so文件情形的dump文件异常定位(基于qBreakpad)
本文中,将着重讲解App+多个So动态库文件的开发形式下,异常发生于so文件中,该如何来定位问题。
一、生成dump
按照一样的方式,在主程序App中,调用qBreakpad,以异常发生时,自动生成dump。代码如下:
main.cpp:
#include <QCoreApplication>
#include "my1.h"
#include "my2.h"
#include "QBreakpadHandler.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QBreakpadInstance.setDumpPath("crashes"); // 设置生成dump文件路径
My1 xx;
xx.add(1, 2);
My2 yy;
yy.sub(2, 1);
return a.exec();
}
并增加了对2个动态库my1、my2的调用,该2个库导出类如下:
my1.h:
#ifndef MY1_H
#define MY1_H
#include "my1_global.h"
class MY1SHARED_EXPORT My1
{
public:
My1();
int add(int a, int b);
};
#endif // MY1_H
my1.cpp:
#include "my1.h"
My1::My1()
{
}
int My1::add(int a, int b)
{
*((int*)0) = 10; // 此处人为造成异常发生
return (a + b);
}
my2.h:
#ifndef MY2_H
#define MY2_H
#include "my2_global.h"
class MY2SHARED_EXPORT My2
{
public:
My2();
int sub(int a, int b);
};
#endif // MY2_H
my2.cpp:
#include "my2.h"
My2::My2()
{
}
int My2::sub(int a, int b)
{
return (a - b);
}
运行程序时,可直接在Qt Creator中,点击运行,这样,不用设置环境变量亦可找到依赖的my1、my2的so库。如下:
非常简单的测试,当程序在main函数中,执行到xx.add(1, 2)行时,会发生异常,并在crashes目录下生成dump文件。如下:
二、生成带调试信息的程序和so库
在App.pro、My1.pro、My2.pro中,分别添加如下内容,以实现在release模式下,亦可生成带调试信息的程序和so库。
QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO
生成的调试信息,就在程序和so文件内部,相比无调试信息时,文件变大了。
到此我们得到了如下4个东西。
dump文件:
带调试信息的App:
带调试信息的my1动态库:
带调试信息的my2动态库:
后面3者,主要用来导出符号文件,以配合dump文件,进行定位。
三、使用dump_syms和minidump_stackwalk定位异常
1、生成符号文件
进入App所在目录。
导出App符号文件“App.sym”
dump_syms App > App.sym
导出my1库的符号文件“libMy1.so.sym”
dump_syms ../My1/libMy1.so > libMy1.so.sym
此处符号文件名需要,参考该文件内容第一行末尾字符,进行修改,如下:
故,应将文件名“libMy1.so.sym”,改为“libMy1.so.1.sym”。
导出my2库的符号文件“libMy2.so.sym”
dump_syms ../My2/libMy2.so > libMy2.so.sym
以my1库符号文件相同方式,进行改名,将文件名“libMy2.so.sym”,改为“libMy2.so.1.sym”。
特别注意:
需要将sym文件放置到特定路径下,方可在后续生成的堆栈信息中,查看到崩溃发生的文件名和行号。否则,堆栈信息中,只能看到内存地址。下一步,将介绍,如何放置到特定路径。
2、将符号文件移动到特定路径
我们在App程序所在目录下,创建symbols目录,并在该目录下继续创建,如下类似的目录结构:
解释:
- 第一级目录,固定为symbols;
- 第二级目录,为即将放入的符号文件名称,如qBreakpadTest.sym,则目录名为qBreakpadTest;
- 第三级目录,在sym文件中第一行内容,有一串16进制编号,将其作为目录名;
建立好以上路径后,将相应的*.sym移动到此路径下。
最后,所有符号文件,放置完毕,目录效果如下:
可以看到App.sym、libMy1.so.1.sym、libMy2.so.1.sym三个符号文件均放置妥当。
3、生成崩溃处调用堆栈信息
执行如下命令,生成调用堆栈信息:
minidump_stackwalk ./crashes/275976b1-dc56-42b7-db518985-a5150714.dmp ./symbols > error.log
- 第一个参数,是dump文件名;
- 第二个参数,固定为./symbols,应该是指定符号文件位于当前symbols目录下默认路径位置;
- 第三个参数,将命令执行结果,写入到error.log文件中。
生成的堆栈调用信息文件error.log,内容如下:
一般找到“crashed”字样,与它最近的一行,就是发生崩溃时,程序的调用堆栈,可以很清楚的看到,崩溃发生在my1.cpp文件,第9行。
到此,我们顺利通过dump文件、带调试信息的程序文件、带调试信息的so文件,成功定位到了bug所在。
四、总结
对于这种App+多so的形式,生成dump也是比较容易的,和单App时没有区别。
但是,在通过dump和符号文件,进行异常定位时,符号文件的生成,以及放置到特定路径下,这个过程较繁琐。
若一个App调用了50个so,那么因为不知晓具体异常发生于哪个so上,故需要将App、50个so进行导出符号,并按特定路径进行组织,放置好,方可使用minidump_stackwalk工具,对异常位置进行定位。
因此,由App和多个so文件,生成,完整的symbols符号目录,这个过程建议由脚本,自动化完成。
可以使用python开源工具symbolstore.py,其github地址:https://github.com/MozillaReality/symbolgenerator,有兴趣可以去研究一下。
特别注意:
- 欲定位异常,至少需要dump文件、带调试信息的主程序文件、带调试信息的所有So文件。
本文涉及工程代码:
https://gitee.com/bailiyang/cdemo/tree/master/Qt/57qBreakpadTest/AppAndMultiSo
若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!
同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。