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 breakpad
,qBreakpad
(适用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_cast
把void*
指针转换为 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开发 之 抓取崩溃信息(读这一篇就够了)
相关文章:

Qt生成日志与以及捕获崩溃文件(mingw64位,winDbg)————附带详细解说
文章目录 Qt生成日志与以及报错文件(mingw64位,winDbg)0 背景与结果0.1 背景0.2 结果1 WinDbg1.1 安装1.2 使用 2 编写代码2.1 ccrashstack类2.2 编写输出捕获异常的dmp文件2.2 编写输出日志文件2.3 调用生成日志和dmp文件 参考 Qt生成日志与以及报错文件(mingw64位…...
web前端开发如何适配各分辨率
在开发Web应用时,适配不同的显示器分辨率是确保用户体验一致性的关键。以下是一些常见的显示器分辨率。 常见的显示器分辨率 PC屏幕分辨率 1366 x 768:普通液晶显示器 1920 x 1080:高清液晶显示器 2560 x 1440:2K高清显示器 4096…...
本机无法远程别的计算机的方法
在本地计算机上修改组策略 按下 Win R 组合键打开运行窗口,输入 gpedit.msc 并回车,打开组策略编辑器。依次展开路径:计算机配置 > 管理模板 > 系统 > 凭据分配。在右侧找到并双击 加密 Oracle 修正 策略。选择 已启用,…...

智能手表健康监测系统的PSRAM存储芯片CSS6404LS-LI—高带宽、耐高温、微尺寸的三重突破
一、直击智能手表三大核心痛点 痛点场景风险传统方案缺陷连续生物数据流存储100Hz PPG信号产生82MB/s数据洪峰SPI NOR Flash带宽不足(≤50MB/s)高温环境稳定性腕表表面温度达50℃(烈日/运动场景)商用级存储器件(85℃)易触发数据错误极限空间约束PCB面积…...

蓝桥杯国赛题2022
首先这个题应该是一个01背包,背包容量为2022,有2022个物品,第i个物品的体积为i,只不过这里有两个限制条件,一个限制条件是和为2022,另一个限制条件为10个数,两个限制条件那就把加一维࿰…...
Pycharm中添加不了新建的Conda环境(此篇专门给Daidai写的)
安装好了Conda之后,在系统终端也创建好Conda环境,一切显示正常,但在Pycharm中添加不了新建的Conda环境,显示“Conda executable is not found” 解决“Conda executable is not found” conda环境新建如下 D:/Programs/anacond…...
如何选择专业数据可视化开发工具?为您拆解捷码全功能和落地指南!
分享大纲: 1、捷码核心功能:4维能力支撑大屏开发 2、3步上手:可视化大屏开发操作路径 3、适配场景:8大行业已验证方案 在各行各业要求数字化转型时代,数据可视化大屏已成为众多企业数据驱动的核心工具。面对市场上繁杂…...

关于如何使用VScode编译下载keil工程的步骤演示
1、vscode的插件市场下载keil Assistant 2 、点设置 3、复制keil的地址 4、粘贴到第…...
微信小程序动态效果实战指南:从悬浮云朵到丝滑列表加载
小红书爆款交互设计解析,附完整代码! 🔥 一、为什么动态效果是小程序的关键竞争力? 用户留存提升:数据显示,86.3%的微商从业者依赖微信小程序,而动态效果能显著降低跳出率。技术赋能体验&#…...

Redis底层数据结构之深入理解跳表(2)
上一篇文章中我们详细讲述了跳表的增添、查找和修改的操作,这篇文章我们来讲解一下跳表在多线程并发时的安全问题。在Redis中,除了网络IO部分和大文件的后台复制涉及到多线程外,其余任务执行时全部都是单线程,这也就意味着在Redis…...
大模型编程助手-Cline
官网: https://cline.bot/ Cline 是一款深度集成在 Visual Studio Code(VSCode) 中的开源 AI 编程助手插件,旨在通过结合大语言模型(如 Claude 3.5 Sonnet、DeepSeek V3、Google Gemini 等)和工具链&#…...

[蓝桥杯]兰顿蚂蚁
兰顿蚂蚁 题目描述 兰顿蚂蚁,是于 1986 年,由克里斯兰顿提出来的,属于细胞自动机的一种。 平面上的正方形格子被填上黑色或白色。在其中一格正方形内有一只"蚂蚁"。 蚂蚁的头部朝向为:上下左右其中一方。 蚂蚁的移…...

使用 Python 构建并调用 ComfyUI 图像生成 API:完整实战指南
快速打造你自己的本地 AI 图像生成服务,支持 Web 前端一键调用! 📌 前言 在 AIGC 快速发展的今天,ComfyUI 作为一款模块化、节点式的图像生成界面,备受开发者青睐。但默认情况下,ComfyUI 主要通过界面交互…...

嵌入式学习笔记-freeRTOS taskENTER_CRITICAL(_FROM_ISR)跟taskEXIT_CRITICAL(_FROM_ISR)函数解析
一 函数taskENTER_CRITICAL,taskEXIT_CRITICAL 函数taskENTER_CRITICAL最终实现如下: 第①处按照系统设定的configMAX_SYSCALL_INTERRUPT_PRIORITY值对中断进行屏蔽 第②处调用一次自增一次 第③处检查中断状态寄存器位,如果有任何中断位置…...
Unity基础-数学向量
Unity基础-数学向量 二、向量相关用法 概述 向量在Unity游戏开发中扮演着重要角色,用于表示位置、方向、速度等。Unity提供了Vector2、Vector3等结构体来处理向量运算。 1. 向量基础操作 1.1 向量创建和访问 // 创建向量 Vector3 position new Vector3(1, 2,…...
【华为云Astro-服务编排】服务编排中图元的使用与配置
目录 子服务编排图元 子服务编排图元的作用 如何使用子服务编排图元 脚本图元 脚本图元的作用 如何使用脚本图元 记录创建图元 记录创建图元的作用 如何使用记录创建图元 记录删除图元 记录删除图元的作用 如何使用记录删除图元 记录查询图元 记录查询图元的作用…...

1panel面板中部署SpringBoot和Vue前后端分离系统 【图文教程】
1panel面板中部署SpringBoot和Vue前后端分离系统 一,1panel面板部署二,安装OpenResty三,安装MySQL,Redis等Spring boot 运行依赖环境四,SpringBoot 应用配置及打包部署配置打包部署 五 ,前端VUE应用配置打包…...
C++.OpenGL (7/64)摄像机(Camera)
摄像机(Camera) 摄像机系统核心组件 #mermaid-svg-lmysTXAyyzKytiOC {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lmysTXAyyzKytiOC .error-icon{fill:#552222;}#mermaid-svg-lmysTXAyyzKytiOC .error-text{fi…...
使用xdocreport导出word
之前java总用freemaker进行导出,但是改xml实在是太繁琐了,这次找了另一个工具进行体验. 一、简单导出 pom引入 <dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>fr.opensagres.xdocreport.core</arti…...
青少年编程与数学 01-011 系统软件简介 05 macOS操作系统
青少年编程与数学 01-011 系统软件简介 05 macOS操作系统 一、历史发展(一)经典 Mac OS(1984-2001)(二)Mac OS X(2001-2016)(三)macOS(2016-至今&…...
Python打卡训练营学习记录Day43
作业: kaggle找到一个图像数据集,用cnn网络进行训练并且用grad-cam做可视化 进阶:并拆分成多个文件 从谷歌图片中拍摄的 10 种不同类别的动物图片 数据预处理 import os from torchvision import datasets, transforms from torch.utils…...

【Android基础回顾】二:handler消息机制
Android 的 Handler 机制 是 Android 应用中实现线程间通信、任务调度、消息分发的核心机制之一,它基于 消息队列(MessageQueue) 消息循环(Looper) 消息处理器(Handler) 组成。 1 handler的使用…...

每日Prompt:每天上班的状态
提示词 一个穿着清朝官服的僵尸脸上贴着符纸,在电脑面前办公,房间阴暗,电脑桌面很乱,烟灰缸里面满是烟头...
.net ORM框架dapper批量插入
.NET ORM 框架 Dapper 批量插入全解析 在 .NET 开发中,与数据库交互是常见需求。Dapper 作为轻量级的 ORM(对象关系映射)库,在简化数据库交互方面表现出色。今天我们就来深入探讨 Dapper 实现批量插入的几种方法。 为什么需要批…...

C++11 右值引用:从入门到精通
文章目录 一、引言二、左值和右值(一)概念(二)区别和判断方法 三、左值引用和右值引用(一)左值引用(二)右值引用 四、移动语义(一)概念和必要性(二…...

.net 使用MQTT订阅消息
在nuGet下载M2Mqtt V4.3.0版本。(支持.net framework) 订阅主题 public void LoadMQQCData() {string enpoint "xxx.xxx.x.x";//ip地址int port 1883;//端口string user "usrname";//用户名string pwd "pwd";//密码…...
Python实现快速排序的三种经典写法及算法解析
今天想熟悉一下python的基础写法,那就从最经典的快速排序来开始吧: 1、经典分治写法(原地排序) 时间复杂度:平均O(nlogn),最坏O(n) 空间复杂度:O(logn)递归栈空间 特点:通过左右指针…...

【递归、搜索与回溯】综合练习(四)
📝前言说明: 本专栏主要记录本人递归,搜索与回溯算法的学习以及LeetCode刷题记录,按专题划分每题主要记录:(1)本人解法 本人屎山代码;(2)优质解法 优质代码…...

强化学习入门:Gym实现CartPole随机智能体
前言 最近想开一个关于强化学习专栏,因为DeepSeek-R1很火,但本人对于LLM连门都没入。因此,只是记录一些类似的读书笔记,内容不深,大多数只是一些概念的东西,数学公式也不会太多,还望读者多多指教…...

STM32:CAN总线精髓:特性、电路、帧格式与波形分析详解
声明:此博客是我的学习笔记,所看课程是江协科技的CAN总线课程,知识点都大同小异,我仅进行总结并加上了我自己的理解,所引案例也都是课程中的案例,希望对你的理解有所帮助! 知识点1【CAN总线的概…...