分享

C++ signal介绍及发布版中捕获崩溃信息方法(文件)

 wuxinit_ 2024-04-24 发布于湖北

常用signal介绍

类型动作解释
SIGHUP1A终端挂起或者控制进程终止
SIGINT2A终止信号(关联ctrl+c,只能结束前台进程)
SIGABRT6C程序异常终止触发
SIGFPE8C错误的算术运算触发,对于那些处理复杂数学运算的程序,一般会建议你捕获该信号。
SIGKILL9AEF一旦该信号被交付给一个进程,那么这个进程就会终止。如果发生“非中断操作”会造成进程死锁,只有重启才会终止该进程。
SIGSEGV11C当程序没有权利访问一个受保护的内存地址时,或者访问无效的虚拟内存地址时(野指针),会产生这个信号
SIGPIPE13A管道破裂(无效的管道/不成立的管道)
SIGTERM15A终止请求,并且在终止之前做一些清理活动。

*处理动作解释:

动作意义
A缺省的动作是终止进程
B缺省的动作是忽略此信号
C缺省的动作是终止进程并进行内核映像转储(dump core)
D缺省的动作是停止进程
E信号不能被捕获
F信号不能被忽略

设置信号signal(registered signal, signal handler)

使用:

signal(SIGINT, sigfunc);
void sigfunc(int signo)
{
  ... //处理信号相关的操作
}

生成信号int raise (signal sig);

使用:

raise( SIGINT);

异常捕获示例:

1、代码中文件部分使用的QT
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <string>
#ifdef __linux
#include <execinfo.h>   // 该文件只有Linux下 gcc 和 clang 中存在
#endif // __linux

const int MAX_STACK_FRAMES = 128;
void sig_crash(int sig)
{
	time_t t = time(NULL);
    tm* now = localtime(&t);
    std::string strCurDay = QDate::currentDate().toString("yyyy_MM_dd");

    QString qstrFileName = QString("%1/Error_Log_").arg(qstrFilePath) + strCurDay + QString(".txt");

    FILE* fd = fopen(qstrFileName.toStdString().c_str(), "at");
    
    if (NULL == fd)
    {
        exit(0);
    }
    try
    {
        char szLine[512] = {0, };
        time_t t = time(NULL);
        tm* now = localtime(&t);
        sprintf(szLine,
                "#########################################################\n"
                "[%04d-%02d-%02d %02d:%02d:%02d][crash signal number:%d]\n",
                now->tm_year + 1900,
                now->tm_mon + 1,
                now->tm_mday,
                now->tm_hour,
                now->tm_min,
                now->tm_sec,
                sig);
                
        fwrite(szLine, 1, strlen(szLine), fd);
#ifdef __linux
        void* array[MAX_STACK_FRAMES];
        char** strings = NULL;
        signal(sig, SIG_DFL);
        int size = backtrace(array, MAX_STACK_FRAMES);
        strings = (char**)backtrace_symbols(array, size);
        for (int i = 0; i < size; ++i)
        {
            char szLine[512] = {0, };
            sprintf(szLine, "%d %s\n", i, strings[i]);
            fwrite(szLine, 1, strlen(szLine), fd);
            
            std::string symbol(strings[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);
            char cmd[128] = {0, };
            sprintf(cmd, "addr2line -e test %s", address.c_str()); // test为应用程序名称,需要改为用户自己的应用程序名
            FILE *fPipe = popen(cmd, "r");
            if(fPipe != NULL){
                char buff[1024];
                memset(buff, 0, sizeof(buff));
                char* ret = fgets(buff, sizeof(buff), fPipe);
                pclose(fPipe);
                fwrite(ret, 1, strlen(ret), fd);
            }
        }
       free(strings);
#endif // __linux
   }
   catch (...)
   {
       //
   }
   fflush(fd);
   fclose(fd);
   fd = NULL;
   exit(0);
}

int main()
{
	// 捕捉崩溃日志
	signal(SIGSEGV, sig_crash);
	signal(SIGABRT, sig_crash);
	
	// 测试部分(利用 stod 空字符串转double类型奔溃问题)
	std::string strTest;
	stod(strTest); 
	return 0;
}

2、抛去数据解析其他部分都用QT

#include <stdio.h>
#include <signal.h>
#include <QDateTime>

#ifdef __linux
#include <execinfo.h>   // 该文件只有Linux下 gcc 和 clang 中存在
#endif // __linux

const int MAX_STACK_FRAMES = 128;
void sig_crash(int sig)
{
    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);
    }
    try
    {
        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(sig) << "\r\n";

#ifdef __linux
        void* array[MAX_STACK_FRAMES];
        char** strings = NULL;
        signal(sig, SIG_DFL);
        int size = backtrace(array, MAX_STACK_FRAMES);
        strings = (char**)backtrace_symbols(array, size);

        for (int i = 0; i < size; ++i)
        {
            text_stream << QString("[原始数据] %1").arg(strings[i]) << "\r\n";

            std::string symbol(strings[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 qstrCMD = QString("addr2line -e saimo %1").arg(QString::fromStdString(address));
            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);
                pclose(fPipe);

                text_stream << QString("[分析错误] %1").arg(ret) << "\r\n";
            }
        }

        free(strings);
#endif // __linux
    }
    catch (...)
    {
        //
    }

    file.flush();
    file.close();
    exit(0);
}

int main()
{
	// 捕捉崩溃日志
	signal(SIGSEGV, sig_crash);
	signal(SIGABRT, sig_crash);
	
	// 测试部分(利用 stod 空字符串转double类型奔溃问题)
	std::string strTest;
	stod(strTest); 
	return 0;
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多