Qt环境生成dump文件解决程序异常崩溃以及生成日志文件

一、背景

1、在测试自己的项目代码过程

① 程序的编译的时候没有报错,程序在Qt release模式下运行程序,程序运行五个小时后异常退出,The process was ended forcefully;② 以及在项目程序通过windeployqt 工具打包发布的程序(排出网上所说缺乏动态依赖库)仍然运行五个小时后异常退出The process was ended forcefully,没有提示的问题。
Qt环境生成dump文件解决程序异常崩溃以及生成日志文件_第1张图片

2、调试项目代码排出bug过程

由于项目主要是具有4个功能,测试第1个功能,将剩下的3个功能注释完进行测试,发现长时间运行没有出现程序异常退出的情况;接着对第1个和第2个功能进行测试,剩下2个功能注释,就发现 ① Qt 对sqlite数据库操作存在内存泄漏问题,以及对同一份数据库进行同时读写的时候没有加锁造成的问题,② 如果是多个子线程操控数据库的时候,记得加锁,防止造成资源争夺

(1)解决对sqlite数据库操作内存泄漏问题

Qt 对sqlite数据每一次对增删改查操作是不是会占用内存资源,然后这种情况下没有释放数据库资源,解决办法如下。
参考:Qt sqlite 查询内存泄漏

query.clear();

(2)同一份数据库进行同时读写时加互斥锁

参考:QT-多线程与MySQL数据库

线程互斥(Mutex):是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有很多个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源才能被其他线程使用。也是代码中的上锁(mutex.lock)和解锁(mutex.unlock)。

互斥对象:互斥对象和临界区很像,采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程同时访问。当前拥有互斥对象的线程处理完任务后必须将线程交出,以便其他线程访问该资源。

方法:由于项目还没有完成,所以后期对同一份数据库文件进行操作的时候加上互斥锁,后续补充相关代码。

二、具体操作

1、生成dump文件

参考1:Qt环境生成dump解决异常崩溃
参考2:Qt-生成dump文件

2、.pro文件配置

QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO,
QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO /DEBUG

# test crash
QMAKE_CFLAGS_RELEASE += -g
QMAKE_CXXFLAGS_RELEASE += -g
QMAKE_CFLAGS_RELEASE -= -O2
QMAKE_CXXFLAGS_RELEASE -= -O2
QMAKE_LFLAGS_RELEASE = -mthreads -W

# 方便生成DUMP调试
LIBS += -lDbgHelp
QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO /DEBUG

QMAKE_CXXFLAGS += -g
QMAKE_CFLAGS += -g

# 调试信息以及pdb文件
QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO
QMAKE_CFLAGS_RELEASE = $$QMAKE_CLAGS_RELEASE_WITH_DEBUGINFO
QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO

3、工程文件中添加ccrashstack类

资源

4、封装生成.dmp和日志文件代码

outputDump.h文件
参考:Qt环境生成dump解决异常崩溃

#ifndef OUTPUTDUMP_H   //避免同一个文件只会被包含一次
#define OUTPUTDUMP_H

//#pragma once         //避免同一个文件只会被包含一次
#include"ccrashstack.h"
#include 
#include 
#include 
#include 
#pragma comment(lib, "dbghelp.lib")  //使用注释方式引入库dbghelp.lib或编译目录。

/*------------------生成dump文件------------------------*/
LONG crashHandler(EXCEPTION_POINTERS *pException)
{
    QString curDataTime = QDateTime::currentDateTime().toString("yyyyMMddhhmmss");
    QString dumpName = curDataTime + ".dmp";

    HANDLE dumpFile = CreateFile((LPCWSTR)QString("./" + dumpName).utf16(),GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if(dumpFile != INVALID_HANDLE_VALUE)
    {
        MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
        dumpInfo.ExceptionPointers = pException;
        dumpInfo.ThreadId = GetCurrentThreadId();
        dumpInfo.ClientPointers = TRUE;

        MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),dumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
        CloseHandle(dumpFile);
    }
    else
    {
        qDebug() << "dumpFile not vaild";
    }

    return EXCEPTION_EXECUTE_HANDLER;
}

//防止CRT(C runtime)函数报错可能捕捉不到
void DisableSetUnhandledExceptionFilter()
{
    void* addr = (void*)GetProcAddress(LoadLibrary(L"kernel32.dll"), "SetUnhandledExceptionFilter");
    if(addr)
    {
        unsigned char code[16];
        int size = 0;

        code[size++] = 0x33;
        code[size++] = 0xC0;
        code[size++] = 0xC2;
        code[size++] = 0x04;
        code[size++] = 0x00;

        DWORD dwOldFlag, dwTempFlag;
        VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
        WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
        VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
    }
}

#endif // OUTPUTDUMP_H

outputLog.h

#ifndef OUTPUTLOG_H
#define OUTPUTLOG_H  //避免同一个文件不会被包含多次 条件编译 满足一定条件下才会被编译

//#pragma once       //避免同一个文件不会被包含多次
#include 
#include 
#include 
#include 
#include 

#include"ccrashstack.h"

/*---------------打日志文件----------------------*/
void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    static QMutex mutex;
    mutex.lock();
    QString text;
    switch(type)
    {
    case QtDebugMsg:
        text = QString("Debug:");
        break;
    case QtWarningMsg:
        text = QString("Warning:");
        break;
    case QtCriticalMsg:
        text = QString("Critical:");
        break;
    case QtFatalMsg:
        text = QString("Fatal:");
    }
    QString context_info = QString("File:(%1) Line:(%2)").arg(QString(context.file)).arg(context.line);
    QString current_date_time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd");
    QString current_date = QString("(%1)").arg(current_date_time);
    QString message = QString("%1 \r\n%2 %3 \r\n%4").arg(current_date).arg(text).arg(context_info).arg(msg);

    //判断文件夹是否存在,不存在新建
    QString aFile = QDir::currentPath() + "/LogFile";
    QDir dir(aFile);
    if(!dir.exists())
    {
        dir.mkdir(aFile);//只创建一级子目录,即必须保证上级目录存在
    }

    QString current_time = QDateTime::currentDateTime().toString("yyyyMMdd");
    QFile file(aFile+"/log"+current_time+".txt");
    file.open(QIODevice::WriteOnly | QIODevice::Append);
    QTextStream text_stream(&file);
    text_stream << message << "\r\n \r\n";

    file.flush();
    file.close();
    mutex.unlock();
}
#endif // OUTPUTLOG_H

main.c 中添加完#include"outputDump.h"和#include"outputLog.h" 头文件,直接调用以下函数,注意放置的位置。如下所示:
1、注冊异常捕获函数,生成.dmp文件。

SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)crashHandler);
DisableSetUnhandledExceptionFilter();

2、注册MessageHandler,生成日志文件。

 qInstallMessageHandler(outputMessage);
#include "mainwindow.h"
#include 
#include"outputDump.h" //生成dump头文件
#include"outputLog.h"  //生成日志头文件

int main(int argc, char *argv[])
{
    /*-------1、注冊异常捕获函数 生成.dmp文件--------*/
    SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)crashHandler);
    DisableSetUnhandledExceptionFilter();

    QApplication a(argc, argv);

    /*-------2、注册MessageHandler 生成日志文件--------*/
    qInstallMessageHandler(outputMessage);

    MainWindow w;
    w.show();
    return a.exec();
}

三、测试效果

1、程序异常退出测试代码

void MainWindow::on_crashBtn_clicked()
{
    QString *p = NULL;
    p->clear();
}

2、Qt-release编译生成.dmp文件和日志文件

Qt环境生成dump文件解决程序异常崩溃以及生成日志文件_第2张图片
Qt环境生成dump文件解决程序异常崩溃以及生成日志文件_第3张图片

3、使用VS2017定位代码出错位置

直接把.dmp文件拖入VS2017软件下。
Qt环境生成dump文件解决程序异常崩溃以及生成日志文件_第4张图片

Qt环境生成dump文件解决程序异常崩溃以及生成日志文件_第5张图片

四、小结

1、感谢技术交流群的大佬、师兄、代工在调试和测试代码过程中对我的指导和帮助,帮助我顺利解决问题。
2、由于此次项目的出现的问题是sqlite内存泄漏以及数据库文件操作没有加锁两个问题,生成.dmp文件在VS2017中调试不了,且所占字节为0Kb,目前自己也没有搞懂这个问题,所有的时候调试代码还得需要经验。如果哪位大佬知道这个问题麻烦指导一下,感谢。

你可能感兴趣的:(qt连接MySQL,QSqlQuery,sqlite,日志文件,dump文件,qt程序调试测试)