199 lines
6.5 KiB
C++
199 lines
6.5 KiB
C++
#include "CrashHandler.h"
|
||
#include <QDir>
|
||
#include <QDateTime>
|
||
#include <QStandardPaths>
|
||
#include <QDebug>
|
||
#include <QApplication>
|
||
#include <QMessageBox>
|
||
#include <signal.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <time.h>
|
||
|
||
#ifdef Q_OS_WIN
|
||
#include <windows.h>
|
||
#ifdef _MSC_VER
|
||
#include <dbghelp.h>
|
||
#pragma comment(lib, "dbghelp.lib")
|
||
#endif
|
||
#endif
|
||
|
||
QString CrashHandler::dumpPath;
|
||
|
||
void CrashHandler::registerCrashHandler()
|
||
{
|
||
signal(SIGSEGV, crashHandlerFunction); // 段错误
|
||
signal(SIGABRT, crashHandlerFunction); // abort调用
|
||
signal(SIGFPE, crashHandlerFunction); // 浮点异常
|
||
signal(SIGILL, crashHandlerFunction); // 非法指令
|
||
|
||
#ifdef Q_OS_WIN
|
||
SetUnhandledExceptionFilter(windowsCrashHandler);
|
||
#endif
|
||
}
|
||
|
||
void CrashHandler::setDumpPath(const QString& path)
|
||
{
|
||
dumpPath = path;
|
||
QDir dir(dumpPath);
|
||
if (!dir.exists()) {
|
||
dir.mkpath(dumpPath);
|
||
}
|
||
}
|
||
|
||
QString CrashHandler::getDumpPath()
|
||
{
|
||
if (dumpPath.isEmpty()) {
|
||
dumpPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/crash_dumps";
|
||
QDir dir(dumpPath);
|
||
if (!dir.exists()) {
|
||
dir.mkpath(dumpPath);
|
||
}
|
||
}
|
||
return dumpPath;
|
||
}
|
||
|
||
#ifdef Q_OS_WIN
|
||
LONG WINAPI CrashHandler::windowsCrashHandler(struct _EXCEPTION_POINTERS* exceptionInfo)
|
||
{
|
||
// 使用 C 函数生成时间戳,避免依赖 Qt(可能已损坏)
|
||
time_t now = time(NULL);
|
||
struct tm timeinfo;
|
||
localtime_s(&timeinfo, &now);
|
||
|
||
char timestamp[64];
|
||
strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &timeinfo);
|
||
|
||
// 获取dump路径
|
||
QString dumpDir = CrashHandler::getDumpPath();
|
||
|
||
// 创建目录(如果不存在)
|
||
CreateDirectoryW((WCHAR*)dumpDir.utf16(), NULL);
|
||
|
||
// 构建文件路径
|
||
QString dumpFilePath = dumpDir + "/crash_dump_" + QString(timestamp) + ".dmp";
|
||
QString logFilePath = dumpDir + "/crash_log_" + QString(timestamp) + ".txt";
|
||
|
||
// 先写日志文件
|
||
FILE* logFile = _wfopen((WCHAR*)logFilePath.utf16(), L"w");
|
||
if (logFile) {
|
||
fprintf(logFile, "Application crashed at: %s\n", timestamp);
|
||
fprintf(logFile, "Exception Code: 0x%08X\n", exceptionInfo->ExceptionRecord->ExceptionCode);
|
||
fprintf(logFile, "Exception Address: 0x%p\n", exceptionInfo->ExceptionRecord->ExceptionAddress);
|
||
fprintf(logFile, "Dump file: %s\n", dumpFilePath.toLocal8Bit().constData());
|
||
fclose(logFile);
|
||
}
|
||
|
||
#ifdef _MSC_VER
|
||
// 创建 dump 文件
|
||
HANDLE hFile = CreateFileW((WCHAR*)dumpFilePath.utf16(),
|
||
GENERIC_WRITE,
|
||
0,
|
||
NULL,
|
||
CREATE_ALWAYS,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
NULL);
|
||
|
||
if (hFile != INVALID_HANDLE_VALUE) {
|
||
MINIDUMP_EXCEPTION_INFORMATION exceptionInfoEx;
|
||
exceptionInfoEx.ThreadId = GetCurrentThreadId();
|
||
exceptionInfoEx.ExceptionPointers = exceptionInfo;
|
||
exceptionInfoEx.ClientPointers = FALSE;
|
||
|
||
// 使用更详细的 dump 类型
|
||
MINIDUMP_TYPE dumpType = (MINIDUMP_TYPE)(
|
||
MiniDumpWithDataSegs |
|
||
MiniDumpWithHandleData |
|
||
MiniDumpWithThreadInfo);
|
||
|
||
BOOL success = MiniDumpWriteDump(
|
||
GetCurrentProcess(),
|
||
GetCurrentProcessId(),
|
||
hFile,
|
||
dumpType,
|
||
&exceptionInfoEx,
|
||
NULL,
|
||
NULL);
|
||
|
||
CloseHandle(hFile);
|
||
|
||
if (success) {
|
||
// 成功写入,显示消息框
|
||
wchar_t msg[512];
|
||
swprintf_s(msg, 512, L"程序崩溃!\n\nDump文件已保存至:\n%s\n\n请联系技术支持。",
|
||
(WCHAR*)dumpFilePath.utf16());
|
||
MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||
} else {
|
||
DWORD error = GetLastError();
|
||
wchar_t msg[256];
|
||
swprintf_s(msg, 256, L"程序崩溃!\n\n无法创建dump文件(错误代码: %d)\n路径: %s",
|
||
error, (WCHAR*)dumpFilePath.utf16());
|
||
MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||
}
|
||
} else {
|
||
DWORD error = GetLastError();
|
||
wchar_t msg[256];
|
||
swprintf_s(msg, 256, L"程序崩溃!\n\n无法创建dump文件(文件创建失败,错误代码: %d)\n路径: %s",
|
||
error, (WCHAR*)dumpFilePath.utf16());
|
||
MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||
}
|
||
#else
|
||
// 非 MSVC 编译器,只显示崩溃信息
|
||
wchar_t msg[256];
|
||
swprintf_s(msg, 256, L"程序崩溃!\n\n日志文件: %s", (WCHAR*)logFilePath.utf16());
|
||
MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||
#endif
|
||
|
||
return EXCEPTION_EXECUTE_HANDLER;
|
||
}
|
||
#endif
|
||
|
||
void CrashHandler::crashHandlerFunction(int sig)
|
||
{
|
||
// 使用 C 函数生成时间戳,避免依赖可能已损坏的 Qt
|
||
time_t now = time(NULL);
|
||
struct tm timeinfo;
|
||
#ifdef Q_OS_WIN
|
||
localtime_s(&timeinfo, &now);
|
||
#else
|
||
localtime_r(&now, &timeinfo);
|
||
#endif
|
||
|
||
char timestamp[64];
|
||
strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &timeinfo);
|
||
|
||
QString dumpDir = getDumpPath();
|
||
QString dumpFilePath = dumpDir + "/crash_signal_" + QString(timestamp) + ".txt";
|
||
|
||
FILE* file = fopen(dumpFilePath.toLocal8Bit().constData(), "w");
|
||
if (file) {
|
||
fprintf(file, "Crash occurred at: %s\n", timestamp);
|
||
fprintf(file, "Signal: %d (", sig);
|
||
switch(sig) {
|
||
case SIGSEGV: fprintf(file, "SIGSEGV - Segmentation fault"); break;
|
||
case SIGABRT: fprintf(file, "SIGABRT - Abort signal"); break;
|
||
case SIGFPE: fprintf(file, "SIGFPE - Floating point exception"); break;
|
||
case SIGILL: fprintf(file, "SIGILL - Illegal instruction"); break;
|
||
default: fprintf(file, "Unknown signal"); break;
|
||
}
|
||
fprintf(file, ")\n");
|
||
|
||
if (qApp) {
|
||
fprintf(file, "Application: %s\n", qApp->applicationName().toLocal8Bit().constData());
|
||
fprintf(file, "Version: %s\n", qApp->applicationVersion().toLocal8Bit().constData());
|
||
}
|
||
|
||
fclose(file);
|
||
|
||
#ifdef Q_OS_WIN
|
||
wchar_t msg[512];
|
||
swprintf_s(msg, 512, L"程序崩溃(信号 %d)!\n\n日志文件已保存至:\n%s",
|
||
sig, (WCHAR*)dumpFilePath.utf16());
|
||
MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||
#endif
|
||
}
|
||
|
||
// 调用默认的信号处理函数来终止程序
|
||
signal(sig, SIG_DFL);
|
||
raise(sig);
|
||
} |