Qt生成日志与以及捕获崩溃文件(mingw64位,winDbg)————附带详细解说

文章目录

  • Qt生成日志与以及报错文件(mingw64位,winDbg)
    • 0 背景与结果
    • 0.1 背景
    • 0.2 结果
    • 1 WinDbg
      • 1.1 安装
      • 1.2 使用
    • 2 编写代码
      • 2.1 ccrashstack类
      • 2.2 编写输出捕获异常的dmp文件
      • 2.2 编写输出日志文件
      • 2.3 调用生成日志和dmp文件
    • 参考

Qt生成日志与以及报错文件(mingw64位,winDbg)

0 背景与结果

0.1 背景

因为编写的Qt软件,编译成运用程序发布后,会遇到莫名的崩溃的问题。为了找到问题所在,解决的方法就是记录日志和捕获崩溃错误的信息。

因为作者使用mingw64位的编译器,所以这里使用ccrashstack类(专为Qt-Mingw32环境设计的工具类),生成.dmp 文件,来帮助开发者调试程序中的bug。

除了使用ccrashstack类,也可以使用Google breakpadqBreakpad(适用MSVC编译器(而mingw编译器,不支持生成Microsoft PDB格式)。需要编译源代码,会稍微麻烦一些,点击可以查看操作方法)。

0.2 结果

使用项目生成dmp文件,
在这里插入图片描述
使用winDbg打开文件,调试获得的结果如下:
在这里插入图片描述
在这里插入图片描述

可以看出结果非常nice,可以知道错误类型 NULL_POINTER_READ_NULL_INSTRUCTION_PTR_INVALID_POINTER_READ,错误的行数在哪里。非常适合测试发布版本,出现的异常闪退的问题。

生成的日志信息也非常方便,程序运行的状况。

在这里插入图片描述

1 WinDbg

1.1 安装

捕获生成的dmp异常文件,需要使用WinDbg来进行解析。因此需要先下载和安装WinDbg。

官方推荐的安装方式:

若要使用 Windows 包管理器安装 WinDbg,请从命令行/PowerShell运行以下命令:

winget install Microsoft.WinDbg

安装完成后,可以在搜索中找到安装好的调试软件。

1.2 使用

1,设置源代码路径(如果源代码修改太多,定位的时候可以会出错);
在这里插入图片描述
如果存在符号路径,也可以填写,不填写的时候,会联网下载;

在这里插入图片描述
2,打开dmp文件;
在这里插入图片描述

如果是发布的程序,则需要把exe、所有库都放在一起,如下图所示:
在这里插入图片描述

3,按照图片上的先点击.ecxr,再点击!analyze -v,就可以得到最开头的结果;
在这里插入图片描述
4,如果熟悉WinDbg的,可以在这个框中,输入调试指令;

在这里插入图片描述

2 编写代码

2.1 ccrashstack类

因为编译的环境为mingw64位,因此需要对32位的CCrashStack类进行修改,修改的地方如下:
请添加图片描述
1,按照提示把32位的Eax、Ebx、Exc、Edx、Esi、Edi、Esp、Ebp、Eip的首字母改成R开头的64位的接口;
2, error: cast from 'PVOID' {aka 'void*'} to 'int' loses precision [-fpermissive] sprintf(buffer, "Exception Addr: %08X ", (int)E.ExceptionAddress);。根据报错信息得知,void*类型转int会遗失精度(在64位系统中,内存使用64位地址,所以指针的大小也是64位。 void* 指针是64位的,而 int 通常是32位的,因此转换时可能会丢失高位信息。),使用reinterpret_castvoid*指针转换为 uintptr_t 类型,这是一个无符号整数类型(在 64 位机器上,uintptr_t 被定义为 unsigned long int),其大小与指针相同,然后使用static_cast再将 uintptr_t类型的整数转换为int类型;
3, error: cast from 'PBYTE' {aka 'unsigned char*'} to 'unsigned int' loses precision。根据报错信息得知,unsigned char*unsigned int会损失精度。错误原因同上,64位的指针地址,转为32位的无符号整型,会遗失精度报错。解决方法为把unsigned char*Ebp->Ret_Addr转换为unsigned long long类型,并使用 %016llX 来确保能完整输出64位十六进制值;

这里只展示头文件,具体文件见附录:

#ifndef CCRASHSTACK_H
#define CCRASHSTACK_H#include <windows.h>
#include <QString>class CCrashStack
{
private:PEXCEPTION_POINTERS m_pException;private:QString GetModuleByRetAddr(PBYTE Ret_Addr, PBYTE & Module_Addr);QString GetCallStack(PEXCEPTION_POINTERS pException);QString GetVersionStr();bool GetHardwareInaformation(QString &graphics_card, QString &sound_deivce);public:CCrashStack(PEXCEPTION_POINTERS pException);QString GetExceptionInfo();
};#endif // CCRASHSTACK_H

2.2 编写输出捕获异常的dmp文件

SetErrorMode函数的用法如下:

在这里插入图片描述

#ifndef OUTPUTDUMP_H
#define OUTPUTDUMP_H
//outputDump.h//#pragma once         //避免同一个文件只会被包含一次
#include "ccrashstack.h"
#include <shlobj.h>
#include <QDebug>
#include <DbgHelp.h>
#include <QDateTime>
//#pragma comment(lib, "dbghelp.lib")  //使用注释方式引入库dbghelp.lib或编译目录。/*------------------生成dump文件------------------------*/
LONG crashHandler(EXCEPTION_POINTERS *pException)
{qDebug()<<"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)函数报错可能捕捉不到(64位会报错)
void DisableSetUnhandledExceptionFilter()
{//原理:https://learn.microsoft.com/zh-cn/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormodeSetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
/*
在64位系统中运行该函数闪退,可能是由于代码尝试修改系统函数 SetUnhandledExceptionFilter 的内存,而64位系统有更严格的内存保护机制,导致内存写入失败。*/// 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

2.2 编写输出日志文件

把Qt5个类型的日志消息,附加上产生时间、文件名、行数、函数名称的信息,写入到txt文件中。控制日志的大小,每当写入5000次时,检查日志大小是否超过15M,如果超过,则清空重新写入。

对于日志内容,放入到QQueue中,进行异步上锁操作。

#ifndef OUTPUT_LOG_H
#define OUTPUT_LOG_H
//outputLog.h//#pragma once       //避免同一个文件不会被包含多次
#include <QDir>
#include <QFile>
#include <QMutex>
#include <QTextStream>
#include <QTime>// #include"ccrashstack.h"
#include <QQueue>#include <QFileInfo>
#include <fstream>
std::ofstream g_OutputDebug;
// 使用异步日志队列
QQueue<QString> logQueue;
QMutex queueMutex;void enqueueLogMessage(const QString &message) {QMutexLocker locker(&queueMutex);logQueue.enqueue(message);// 可以在此处触发异步写入操作
}/*---------------打日志文件----------------------*/
void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{// static QMutex mutex;// mutex.lock();enqueueLogMessage(msg); // 将消息加入队列,而非直接写入static int count = 0;QString text;switch(type){case QtDebugMsg:text = QString("[Debug]");break;case QtWarningMsg:text = QString("[Warning]");break;case QtCriticalMsg:text = QString("[Critical]");break;case QtInfoMsg:text = QString("[Information]");break;break;case QtFatalMsg:text = QString("[Fatal]");}// 获取纯文件名QFileInfo fileInfo(context.file);QString filename = fileInfo.baseName();// QString context_info = QString("File:(%1) Line:(%2) Function:(%3)").arg(filename).arg(context.line).arg(context.function);QString context_info = QString("( %1:%2, %3 )").arg(filename).arg(context.line).arg(context.function);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 %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();count ++;//每打印5000次判断一次文件大小,超过5M就清空重新写入QFileInfo info(aFile+"/log"+current_time+".txt");if(count > 5000){count = 0;if(info.size() > 1024*1024*15)//1024*1024*5{g_OutputDebug.open(qPrintable(aFile+"/log"+current_time+".txt"), std::ios::out | std::ios::trunc);g_OutputDebug.close();}}}#endif // OUTPUT_LOG_H

2.3 调用生成日志和dmp文件

调用Windows的系统SetUnhandledExceptionFilter函数(该函数有个设置回调函数,软件崩溃时会回调该系统函数,并传回崩溃地址信息等,),给该函数传递编写的crashHandler函数中,此函数的MiniDumpWriteDump方法(MiniDumpWriteDump 是一个 Windows API 函数,用于将用户模式小型转储信息写入指定的文件。这个函数在调试和分析程序崩溃时非常有用,可以帮助开发人员收集异常信息并进行故障排除)来把产生的错误内容写入到.dmp文件中。

pro文件中加入如下配置:

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_MESSAGELOGCONTEXT
DEFINES += QT_DEPRECATED_WARNINGS# # 添加DUMP文件
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 /DEBUGQMAKE_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_DEBUGINFOLIBS += -lpsapi

调用方法如下:

#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);MainInterface w;w.show();return a.exec();
}

参考

Qt环境生成dump解决异常崩溃

Qt-生成dump文件

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

【Qt 应用开发 日志处理 】学习自定义消息处理的艺术

Get started with WinDbg (user mode)

Qt Windows系统使用QBreakpad实战

SetErrorMode 函数

QT运行日志保存和对日志大小进行监控

Qt开发 之 抓取崩溃信息(读这一篇就够了)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/86028.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nginx + Tomcat负载均衡群集

目录 一、案例环境 二、部署 Tomcat&#xff08;102/103&#xff09; 1、准备环境 &#xff08;1&#xff09;关闭firewalld 防火墙 &#xff08;2&#xff09;安装JDK 2、安装配置 Tomcat &#xff08;1&#xff09;Tomcat 的安装和配置 &#xff08;2&#xff09;移动…

三、元器件的选型

前言&#xff1a;我们确立了题目的功能后&#xff0c;就可以开始元器件的选型&#xff0c;元器件的选型关乎到我们后面代码编写的一个难易。 一、主控的选择 主控的选择很大程度上决定我们后续使用的代码编译器&#xff0c;比如ESP32使用的是VScode&#xff0c;或者Arduino&a…

API是什么意思?如何实现开放API?

目录 一、API 是什么 &#xff08;一&#xff09;API 的定义 &#xff08;二&#xff09;API 的作用 二、API 的类型 &#xff08;一&#xff09;Web API 1. RESTful API 2. SOAP API &#xff08;二&#xff09;操作系统 API &#xff08;三&#xff09;数据库 API …

AI生成的基于html+marked.js实现的Markdown转html工具,离线使用,可实时预览 [

有一个markdown格式的文档&#xff0c;手头只有notepad的MarkdownPanel插件可以预览&#xff0c;但是只能预览&#xff0c;不能直接转换为html文件下载&#xff0c;直接复制预览的内效果又不太好&#xff0c;度娘也能找到很多工具&#xff0c;但是都需要在线使用。所以考虑用AI…

Java-前置基础

前言 基础基础 package org.example;public class Main {int a 10;String s1 "你好";public static void main(String[] args) {System.out.println(a);System.out.println(s1);} } 发现报错位置 public class Main {static int a 10;static String s1 "你好…

python字符串方法

1. capitalize&#xff1a; 是第一个字符大写&#xff0c;其余小写 2. encode&#xff1a; 将字符串转换为字节串&#xff08;bytes&#xff09;&#xff0c;默认使用 UTF-8 编码。 3. format&#xff1a; format是 Python 中字符串对象的内置方法&#xff0c;语法为S.form…

Java详解LeetCode 热题 100(24):LeetCode 234. 回文链表(Palindrome Linked List)详解

文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 回文链表的特征2.2 核心难点 3. 解法一&#xff1a;转换为数组法3.1 算法思路3.2 详细图解3.3 Java代码实现3.4 详细执行过程演示3.5 执行结果示例3.6 使用数组而非ArrayList的优化版本3.7 复杂度分析3.8 优缺点分析 4. 解…

平板电脑如何通过EN 18031认证

平板电脑若需通过 EN 18031 认证&#xff08;欧盟无线电设备网络安全标准&#xff0c;属于 CE RED 指令的一部分&#xff09;&#xff0c;需满足其针对互联网连接设备和数据处理设备的安全要求。以下是详细的认证流程、技术要求和操作指南&#xff1a; 一、认证背景与法规基础…

KaiwuDB在边缘计算领域的应用与优势

KaiwuDB 在边缘计算场景中主要应用于 工业物联网&#xff08;IIoT&#xff09;、智能电网、车联网 等领域&#xff0c;通过其分布式多模架构和轻量化设计&#xff0c;在边缘侧承担 数据实时处理、本地存储与协同分析 的核心作用。以下是具体案例和功能解析&#xff1a; 1. 典型…

MP4文件声音与视频分离

最近学习PR剪辑 要添加视频文件和音频文件 但是直接给MP4文件 得到的是一个整体 不管怎么切分 都是无法得到单独的整体 这就需要将视频文件和音频文件分离 我推荐使用ffmpeg工具进行分离 夸克链接&#xff1a;https://pan.quark.cn/s/8dbc3bfbc5d4 百度链接: https://pan.ba…

山洪径流过程及洪水淹没数值模拟

气候变化背景下&#xff0c;极端天气导致的洪水事件将更加频发。快速城市化对流域下垫面的改变&#xff0c;及人类活动向洪泛区的扩张。二者共同使得全世界多数人类活动高度聚集区的洪水风险增加。洪水淹没危险性&#xff08;各种年遇型洪水淹没&#xff09;是洪水损失评估、风…

Rust 通用代码生成器:莲花,红莲尝鲜版三十六,图片初始化功能介绍

Rust 通用代码生成器&#xff1a;莲花&#xff0c;红莲尝鲜版三十六&#xff0c;图片初始化功能介绍 Rust 通用代码生成器莲花&#xff0c;红莲尝鲜版三十六。支持全线支持图片预览&#xff0c;可以直接输出带图片的哑数据模式快速原型。哑数据模式和枚举支持图片。哑数据和枚…

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…

Acrobat DC v25.001 最新专业版已破,像word一样编辑PDF!

在数字化时代&#xff0c;PDF文件以其稳定性和通用性成为了文档交流和存储的热门选择。无论是阅读、编辑、转换还是转曲&#xff0c;大家对PDF文件的操作需求日益增加。因此&#xff0c;一款出色的PDF处理软件不仅要满足多样化的需求&#xff0c;还要通过简洁的界面和强大的功能…

CSS中justify-content: space-between首尾贴边中间等距(两端元素紧贴左右边缘,中间元素等距均匀分布)

justify-content: space-between; 是 CSS Flexbox 布局中的一个属性值&#xff0c;主要作用是在弹性容器的主轴方向上均匀分布子元素&#xff0c;具有以下核心特性&#xff1a; 作用效果&#xff1a; 首尾贴边 第一个子元素紧贴容器起始端 最后一个子元素紧贴容器结束端 中…

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…

全球知名具身智能/AI机器人实验室介绍之AI FACTORY基于慕尼黑工业大学

全球知名具身智能/AI机器人实验室介绍之AI FACTORY基于慕尼黑工业大学 TUM AI FACTORY&#xff0c;即KI.FABRIK&#xff0c;是德国慕尼黑工业大学&#xff08;TUM&#xff09;在巴伐利亚州推出的一个旗舰项目&#xff0c;旨在打造未来工厂&#xff0c;将传统工厂转变为由人工智…

DRV8833 电机控制芯片

引脚分配如图&#xff1a; 要让芯片运行需要将STBY拉高 IN1 IN2 分两组 对应 AO BO&#xff0c;同时拉高电平可以进行刹车&#xff08;慢速衰减。 芯片本身引脚定义&#xff1a; 控制真值表&#xff1a;

如何通过git命令查看项目连接的仓库地址?

要通过 Git 命令查看项目连接的仓库地址&#xff0c;您可以使用以下几种方法&#xff1a; 1. 查看所有远程仓库地址 使用 git remote -v 命令&#xff0c;它会显示项目中配置的所有远程仓库及其对应的 URL&#xff1a; git remote -v输出示例&#xff1a; origin https://…

区块链技术相关

区块链技术参考&#xff1a;区块链 多领域 一、区块链技术演进与2025年核心突破 2025年&#xff0c;区块链技术已从单一分布式账本演变为融合人工智能、物联网、隐私计算的多维技术栈。零知识证明&#xff08;ZKP&#xff09;在性能优化上取得关键进展&#xff0c;以太坊等主流…