一般的通过backtrace函数在程序崩溃时打印错误堆栈,可以采用多种方法。本文只支持linux与arm环境。
backtrace的使用方法请自行百度。
本方法是搜索了网上大部分的讲解文章,根据实际的Qt项目。采用的backtrace + addr2line的方式解析的。
由于是在交叉编译的环境下使用的,所以有几个点需要注意。
首先是Qt项目在编译时候,需要在.pro中加入以下三个选项。否则交叉编译的环境下,崩溃堆栈抛出不全,甚至不抛出。
QMAKE_CXXFLAGS += -rdynamic -funwind-tables -ffunction-sections
最后想把堆栈打印出来,并保存到本地文件。结合Qt项目。采用popen函数,将打印的数据保存成文件。
最终通过addr2line解析堆栈的地址,就可以得到具体的文件名,函数名,以及行数。
由于我是需要在嵌入式开发板子上面跑程序,所以addr2line的这个工具,需要是可以在交叉编译平台运行的。
addr2line是gcc下的一个命令软件,一般的运行在非交叉编译平台的交叉工具名字叫做arm-linux-addr2line,或者其他arm-none-linux-addr2line等等。但这个其实只是在linux编译出来交叉编译工具,也就是arm-linux-gcc的工具,只能运行在linux下,不能在开发板中运行。
我是在开发板厂商提供的交叉编译工具的目录下,找到了一个native的目录,这里边有一个addr2line恰好可以在交叉编译环境下运行,直接拿来解析堆栈地址了。
如果你只在linux下面运行程序,直接装gcc就行了。
但是,我需要在arm环境下执行 "./addr2line -C -f -e TestQtGui 0x12321323 -s"; 所以需要一个可以在交叉环境下可以运行的 addr2line。将addr2line文件复制到程序运行目录,代码中按照上述写法就可以了。
这个工具由于在开发板的交叉工具链中找到了一份可以在开发板的交叉环境中使用的工具。所以直接拿来用了。
如果各位想用此方法,需要找开发板的厂商要一份可以在交叉环境下运行的addr2line工具。
以下附上代码:
以下代码可以在linux运行,如果找不到addr2line命令,请检查gcc路径下是否有此命令,一般可以编译就会有这个命令。
在交叉环境下运行,需要注意找一个可以在交叉环境下运行的addr2line。如果找不到,这里提供其他方法解析堆栈地址,需要开发着使用交叉编译工具,在linux的虚拟机下,自行编译gdb代替addr2line解析堆栈地址。
或者自行交叉编译一份gcc。如果是编译gcc的话,我猜测是要用交叉编译工具本身,编译其自身源码,才能得到可以在交叉环境下运行的addr2line。
#include "mainwindow.h"
#include <QApplication>
#include<signal.h>
#include<string.h>
#include <QDateTime>
#include <QDir>
#include <QTextStream>
#include <QDebug>
#include <execinfo.h>
#include <stdio.h>
void SystemErrorHandler(int signum)
{
QString qstrFilePath = QString("%1/Log").arg(QCoreApplication::applicationDirPath());
QDir dir(qstrFilePath);
if(!dir.exists())
{
dir.mkpath(qstrFilePath);
}
QString strCurDay = QDate::currentDate().toString("yyyy_MM_dd");
QString qstrFileName = QString("%1/Error_Log_").arg(qstrFilePath) + strCurDay + QString(".txt");
QFile file(qstrFileName);
bool bIsOpen = file.open(QIODevice::Append | QIODevice::WriteOnly);
QTextStream text_stream(&file);
if(!bIsOpen)
{
exit(0);
}
QDateTime datetime = QDateTime::currentDateTime().toLocalTime();
text_stream << "#########################################################" << "\r\n";
text_stream << QString("[%1]").arg(datetime.toString("yyyy-MM-dd HH:mm:ss")) << "\r\n";
text_stream << QString("[crash signal number: %1]").arg(signum) << "\r\n";
const int len=1024;
void *func[len];
size_t size;
int i;
char **funs;
signal(signum,SIG_DFL);
size=backtrace(func,len);
funs=(char**)backtrace_symbols(func,size);
fprintf(stderr,"System error, Stack trace:\n");
for(i=0;i<size;++i)
{
fprintf(stderr,"%d %s \n",i,funs[i]);
QString strCmdLIne = "./addr2line -C -f -e TestQtGui %1 -s";
std::string symbol(funs[i]);
size_t pos1 = symbol.find_first_of("[");
size_t pos2 = symbol.find_last_of("]");
std::string address = symbol.substr(pos1 + 1, pos2 - pos1 -1);
QString strAddress = QString::fromStdString(address);
QString qstrCMD = QString(strCmdLIne).arg(strAddress);
system(qstrCMD.toStdString().c_str());
FILE *fPipe = popen(qstrCMD.toStdString().c_str(), "r");
if(fPipe != NULL)
{
char buff[1024];
memset(buff, 0, sizeof(buff));
char* ret = fgets(buff, sizeof(buff), fPipe);
while(ret != NULL)
{
qDebug()<<"write data = "<<(ret);
text_stream << QString("data %1").arg(ret) << "\r\n";
ret = fgets(buff, sizeof(buff), fPipe);
}
pclose(fPipe);
}
}
free(funs);
file.flush();
file.close();
exit(1);
}
void Fun1()
{
char *p=NULL;
*p = 'A';
}
void Fun()
{
Fun1();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
// 捕捉崩溃日志
signal(SIGSEGV,SystemErrorHandler); //Invaild memory address
signal(SIGABRT,SystemErrorHandler); // Abort signal
Fun();
return a.exec();
}
参考链接:调试方法 - 崩溃问题定位 - backtrace_qazw9600的博客-CSDN博客
交叉编译backtrace的编译选项
ARM Linux BackTrace_geniuszm2的博客-CSDN博客_backtrace返回值一直是0
popen的使用