-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdebug_helper.cpp
More file actions
234 lines (199 loc) · 9.65 KB
/
debug_helper.cpp
File metadata and controls
234 lines (199 loc) · 9.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#include "debug_helper.h"
#include <QCoreApplication>
#include <QDir>
#include <QSysInfo>
#include <QFileInfo>
#ifdef Q_OS_WIN
#include <Windows.h>
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib")
#endif
static QFile logFile;
void DebugHelper::initLog() {
QString logPath = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath("debug.log");
logFile.setFileName(logPath);
logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
log("=== 程序启动 ===");
log("日志文件: " + logPath);
log("应用程序目录: " + QCoreApplication::applicationDirPath());
logSystemInfo();
}
void DebugHelper::log(const QString& message) {
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
QString logMessage = QString("[%1] %2").arg(timestamp, message);
if (logFile.isOpen()) {
QTextStream stream(&logFile);
stream << logMessage << "\n";
stream.flush();
}
qDebug() << logMessage;
}
void DebugHelper::logSystemInfo() {
log("操作系统: " + QSysInfo::prettyProductName());
log("CPU架构: " + QSysInfo::currentCpuArchitecture());
#ifdef Q_OS_WIN
MEMORYSTATUSEX memStatus;
memStatus.dwLength = sizeof(memStatus);
if (GlobalMemoryStatusEx(&memStatus)) {
log(QString("总内存: %1 MB").arg(memStatus.ullTotalPhys / (1024 * 1024)));
log(QString("可用内存: %1 MB").arg(memStatus.ullAvailPhys / (1024 * 1024)));
}
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
log(QString("CPU核心数: %1").arg(sysInfo.dwNumberOfProcessors));
#endif
}
#ifdef Q_OS_WIN
static QString getExceptionDescription(DWORD exceptionCode) {
switch (exceptionCode) {
case EXCEPTION_ACCESS_VIOLATION: return "访问冲突 (Access Violation)";
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "数组越界 (Array Bounds Exceeded)";
case EXCEPTION_BREAKPOINT: return "断点 (Breakpoint)";
case EXCEPTION_DATATYPE_MISALIGNMENT: return "数据类型未对齐 (Datatype Misalignment)";
case EXCEPTION_FLT_DENORMAL_OPERAND: return "浮点非规格化操作数 (Float Denormal Operand)";
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "浮点除零 (Float Divide By Zero)";
case EXCEPTION_FLT_INEXACT_RESULT: return "浮点不精确结果 (Float Inexact Result)";
case EXCEPTION_FLT_INVALID_OPERATION: return "浮点无效操作 (Float Invalid Operation)";
case EXCEPTION_FLT_OVERFLOW: return "浮点上溢 (Float Overflow)";
case EXCEPTION_FLT_STACK_CHECK: return "浮点栈检查 (Float Stack Check)";
case EXCEPTION_FLT_UNDERFLOW: return "浮点下溢 (Float Underflow)";
case EXCEPTION_ILLEGAL_INSTRUCTION: return "非法指令 (Illegal Instruction)";
case EXCEPTION_IN_PAGE_ERROR: return "页面错误 (In Page Error)";
case EXCEPTION_INT_DIVIDE_BY_ZERO: return "整数除零 (Integer Divide By Zero)";
case EXCEPTION_INT_OVERFLOW: return "整数上溢 (Integer Overflow)";
case EXCEPTION_INVALID_DISPOSITION: return "无效的处理 (Invalid Disposition)";
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "不可继续的异常 (Noncontinuable Exception)";
case EXCEPTION_PRIV_INSTRUCTION: return "特权指令 (Privileged Instruction)";
case EXCEPTION_SINGLE_STEP: return "单步 (Single Step)";
case EXCEPTION_STACK_OVERFLOW: return "栈溢出 (Stack Overflow)";
default: return QString("未知异常 (0x%1)").arg(exceptionCode, 8, 16, QChar('0'));
}
}
static QString getModuleInfo(HANDLE process, PVOID address) {
IMAGEHLP_MODULE64 moduleInfo;
moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
if (SymGetModuleInfo64(process, (DWORD64)address, &moduleInfo)) {
return QString("%1 (Base: 0x%2)").arg(QString::fromLocal8Bit(moduleInfo.ModuleName))
.arg(moduleInfo.BaseOfImage, 0, 16);
}
return "未知模块";
}
static QString getSymbolInfo(HANDLE process, DWORD64 address) {
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
DWORD64 displacement = 0;
if (SymFromAddr(process, address, &displacement, symbol)) {
return QString("%1+%2").arg(QString::fromLocal8Bit(symbol->Name))
.arg(displacement);
}
return "";
}
LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) {
QString crashLogPath = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath("crash.log");
QFile logFile(crashLogPath);
logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
QTextStream stream(&logFile);
stream << "=== 程序崩溃 ===" << "\n";
stream << "时间: " << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") << "\n";
stream << "\n";
DWORD exceptionCode = ExceptionInfo->ExceptionRecord->ExceptionCode;
PVOID exceptionAddress = ExceptionInfo->ExceptionRecord->ExceptionAddress;
stream << "【异常信息】" << "\n";
stream << "异常代码: 0x" << QString::number(exceptionCode, 16) << "\n";
stream << "异常描述: " << getExceptionDescription(exceptionCode) << "\n";
stream << "异常地址: 0x" << QString::number((quintptr)exceptionAddress, 16) << "\n";
stream << "\n";
HANDLE process = GetCurrentProcess();
SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
SymInitialize(process, NULL, TRUE);
stream << "【模块信息】" << "\n";
stream << "崩溃所在模块: " << getModuleInfo(process, exceptionAddress) << "\n";
stream << "\n";
QString symbolInfo = getSymbolInfo(process, (DWORD64)exceptionAddress);
if (!symbolInfo.isEmpty()) {
stream << "【符号信息】" << "\n";
stream << "函数: " << symbolInfo << "\n";
stream << "\n";
}
CONTEXT* context = ExceptionInfo->ContextRecord;
STACKFRAME64 stackFrame;
memset(&stackFrame, 0, sizeof(STACKFRAME64));
#ifdef _M_X64
DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
stackFrame.AddrPC.Offset = context->Rip;
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrFrame.Offset = context->Rbp;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrStack.Offset = context->Rsp;
stackFrame.AddrStack.Mode = AddrModeFlat;
#else
DWORD machineType = IMAGE_FILE_MACHINE_I386;
stackFrame.AddrPC.Offset = context->Eip;
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrFrame.Offset = context->Ebp;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrStack.Offset = context->Esp;
stackFrame.AddrStack.Mode = AddrModeFlat;
#endif
stream << "【调用堆栈】" << "\n";
for (int i = 0; i < 32; i++) {
if (!StackWalk64(machineType, process, GetCurrentThread(), &stackFrame, context, NULL,
SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
break;
}
if (stackFrame.AddrPC.Offset == 0) break;
QString moduleName = getModuleInfo(process, (PVOID)stackFrame.AddrPC.Offset);
QString symbolName = getSymbolInfo(process, stackFrame.AddrPC.Offset);
stream << " [" << i << "] 0x" << QString::number(stackFrame.AddrPC.Offset, 16);
stream << " - " << moduleName;
if (!symbolName.isEmpty()) {
stream << " - " << symbolName;
}
stream << "\n";
}
stream << "\n";
stream << "【寄存器信息】" << "\n";
#ifdef _M_X64
stream << "RAX: 0x" << QString::number(context->Rax, 16) << "\n";
stream << "RBX: 0x" << QString::number(context->Rbx, 16) << "\n";
stream << "RCX: 0x" << QString::number(context->Rcx, 16) << "\n";
stream << "RDX: 0x" << QString::number(context->Rdx, 16) << "\n";
stream << "RSI: 0x" << QString::number(context->Rsi, 16) << "\n";
stream << "RDI: 0x" << QString::number(context->Rdi, 16) << "\n";
stream << "RBP: 0x" << QString::number(context->Rbp, 16) << "\n";
stream << "RSP: 0x" << QString::number(context->Rsp, 16) << "\n";
stream << "RIP: 0x" << QString::number(context->Rip, 16) << "\n";
#else
stream << "EAX: 0x" << QString::number(context->Eax, 16) << "\n";
stream << "EBX: 0x" << QString::number(context->Ebx, 16) << "\n";
stream << "ECX: 0x" << QString::number(context->Ecx, 16) << "\n";
stream << "EDX: 0x" << QString::number(context->Edx, 16) << "\n";
stream << "ESI: 0x" << QString::number(context->Esi, 16) << "\n";
stream << "EDI: 0x" << QString::number(context->Edi, 16) << "\n";
stream << "EBP: 0x" << QString::number(context->Ebp, 16) << "\n";
stream << "ESP: 0x" << QString::number(context->Esp, 16) << "\n";
stream << "EIP: 0x" << QString::number(context->Eip, 16) << "\n";
#endif
SymCleanup(process);
stream << "\n";
stream << "==================" << "\n";
logFile.close();
QString errorMessage = QString("程序发生错误!\n\n"
"异常类型: %1\n"
"异常地址: 0x%2\n\n"
"详细信息已保存到 crash.log 文件。\n"
"请将 crash.log 和 debug.log 发送给开发者。")
.arg(getExceptionDescription(exceptionCode))
.arg((quintptr)exceptionAddress, 0, 16);
MessageBoxA(NULL, errorMessage.toLocal8Bit().constData(), "程序错误", MB_OK | MB_ICONERROR);
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
void DebugHelper::installCrashHandler() {
#ifdef Q_OS_WIN
SetUnhandledExceptionFilter(ExceptionHandler);
log("崩溃处理器已安装");
#endif
}