嵌入式平台Qt项目使用backtrace函数抓取崩溃堆栈并解析的方法

该博客介绍了如何在Linux和ARM环境下,通过Qt项目利用backtrace函数捕获程序崩溃时的错误堆栈,并结合addr2line工具进行堆栈地址解析,以获取详细的文件名、函数名和行号。在交叉编译环境中,需要注意添加编译选项,并寻找能在目标平台运行的addr2line版本。文中提供了相关代码示例和步骤,适用于嵌入式开发板的错误调试。
摘要由CSDN通过智能技术生成

一般的通过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的使用

Linux中C语言执行shell脚本的方法_酸菜鱼的鱼的博客-CSDN博客_c语言执行shell脚本

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zanglengyu

敲碗要饭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值