DAWorkbench 0.0.1
DAWorkbench API
载入中...
搜索中...
未找到
DADumpCapture.h
1#ifndef DADUMPCAPTURE_H
2#define DADUMPCAPTURE_H
3
4// stl
5#include <functional>
6#include <fstream>
7// qt
8#include <QDebug>
9#include <QtGlobal>
10#include <QObject>
11#include <QMessageBox>
12#include <QDir>
13#include <QApplication>
14#include <QDateTime>
15#include <QTextStream>
16#include <QSysInfo>
17#include <QVersionNumber>
18
19// win32
20#ifdef Q_OS_WIN
21#ifdef Q_CC_MSVC
22
23// msvc下才有用
24#include <Windows.h>
25#include <DbgHelp.h>
26#include <tchar.h>
27#pragma comment(lib, "dbghelp.lib")
28
29#endif
30#endif
31
32namespace DA
33{
37using FpPreposeDump = std::function< QString() >;
38using FpPostDump = std::function< void(const QString& dumpPath, bool success) >;
39
40// win32
41#ifdef Q_OS_WIN
42#ifdef Q_CC_MSVC
43
44LONG applicationCrashHandler(_EXCEPTION_POINTERS* pException);
45#endif // Q_CC_MSVC
46#endif // Q_OS_WIN
47
82{
83private:
84 static FpPreposeDump s_fp_prepose_dump;
85 static FpPostDump s_fp_post_dump;
86 static bool s_initialized;
87
88public:
94 {
95 return s_fp_prepose_dump;
96 }
97
102 static FpPostDump getPostDumpFunc()
103 {
104 return s_fp_post_dump;
105 }
106
132 static void initDump(FpPreposeDump fpPre = nullptr, FpPostDump fpPost = nullptr)
133 {
134#ifdef Q_OS_WIN
135#ifdef Q_CC_MSVC
136 if (s_initialized) {
137 qDebug() << "Dump capture already initialized";
138 return;
139 }
140
141 // 设置默认前置处理函数
142 if (nullptr == fpPre) {
143 fpPre = []() -> QString {
144 QString dumppath = QDir::toNativeSeparators(QApplication::applicationDirPath() + "/dumps");
145 QDir dir;
146 if (!dir.exists(dumppath)) {
147 if (!dir.mkpath(dumppath)) {
148 qDebug() << "Failed to create dump directory:" << dumppath;
149 // 如果创建失败,使用临时目录
150 dumppath = QDir::toNativeSeparators(QDir::tempPath() + "/app_dumps");
151 dir.mkpath(dumppath);
152 }
153 }
154 QString filename = QString("dump_%1_%2.dmp")
155 .arg(QApplication::applicationName())
156 .arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz"));
157 QString fullPath = QString("%1/%2").arg(dumppath).arg(filename);
158 return QDir::toNativeSeparators(fullPath);
159 };
160 }
161
162 s_fp_prepose_dump = fpPre;
163 s_fp_post_dump = fpPost;
164 s_initialized = true;
165
166 // 注册异常捕获函数
167 LPTOP_LEVEL_EXCEPTION_FILTER oldFilter = SetUnhandledExceptionFilter(applicationCrashHandler);
168 if (oldFilter) {
169 qDebug() << "Previous exception filter was replaced";
170 }
171
172 qDebug() << "Dump capture initialized successfully";
173#endif
174#endif
175 }
176
181 static bool isInitialized()
182 {
183 return s_initialized;
184 }
185
195 static QString getDefaultDumpDirectory()
196 {
197 QString dumppath = QDir::toNativeSeparators(QApplication::applicationDirPath() + "/dumps");
198 QDir dir;
199 if (!dir.exists(dumppath)) {
200 dir.mkpath(dumppath);
201 }
202 return dumppath;
203 }
204
224 static void cleanupOldDumps(int daysToKeep = 7, const QString& directory = QString())
225 {
226 QString dumpDir = directory.isEmpty() ? getDefaultDumpDirectory() : directory;
227 QDir dir(dumpDir);
228
229 if (!dir.exists()) {
230 return;
231 }
232
233 QDateTime cutoffDate = QDateTime::currentDateTime().addDays(-daysToKeep);
234 QFileInfoList files = dir.entryInfoList(QStringList() << "*.dmp" << "*_info.txt",
235 QDir::Files | QDir::NoDotAndDotDot);
236
237 int removedCount = 0;
238 for (const QFileInfo& file : files) {
239 if (file.lastModified() < cutoffDate) {
240 if (QFile::remove(file.absoluteFilePath())) {
241 qDebug() << "Removed old dump file:" << file.fileName();
242 removedCount++;
243 }
244 }
245 }
246
247 if (removedCount > 0) {
248 qDebug() << "总共清理了" << removedCount << "个旧文件";
249 }
250 }
251};
252
253// 定义静态成员变量
254#ifdef Q_OS_WIN
255#ifdef Q_CC_MSVC
256FpPreposeDump DADumpCapture::s_fp_prepose_dump = nullptr;
257FpPostDump DADumpCapture::s_fp_post_dump = nullptr;
258bool DADumpCapture::s_initialized = false;
259
260// 程式异常捕获
261LONG applicationCrashHandler(_EXCEPTION_POINTERS* pException)
262{
263 QString createPath;
264 bool dumpSuccess = false;
265 LPCWSTR lpcwStr = NULL;
266 try {
267 // 访问DADumpCapture的静态成员变量
269 if (fpDump) {
270 createPath = fpDump();
271 } else {
272 // 默认路径生成
273 QString dumppath = QDir::toNativeSeparators(QApplication::applicationDirPath() + "/dumps");
274 QDir dir;
275 if (!dir.exists(dumppath)) {
276 dir.mkpath(dumppath);
277 }
278 createPath = QString("%1/dump_%2.dmp").arg(dumppath).arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz"));
279 }
280
281 qDebug() << "Application crashed, writing dump file at:" << createPath;
282
283 // 记录系统信息到文本文件
284 QString infoPath = createPath;
285 infoPath.replace(".dmp", "_info.txt");
286
287 // 使用C风格文件操作避免C++异常
288 FILE* infoFile = nullptr;
289#ifdef _MSC_VER
290 fopen_s(&infoFile, infoPath.toLocal8Bit().constData(), "w");
291#else
292 infoFile = fopen(infoPath.toLocal8Bit().constData(), "w");
293#endif
294
295 if (infoFile) {
296 // 应用程序信息
297 fprintf(infoFile, "=== Application Information ===\n");
298 fprintf(infoFile, "Application: %s\n", QApplication::applicationName().toLocal8Bit().constData());
299 fprintf(infoFile, "Version: %s\n", QApplication::applicationVersion().toLocal8Bit().constData());
300 fprintf(infoFile, "Organization: %s\n", QApplication::organizationName().toLocal8Bit().constData());
301 fprintf(infoFile, "Organization Domain: %s\n", QApplication::organizationDomain().toLocal8Bit().constData());
302
303 // 崩溃时间信息
304 fprintf(infoFile, "\n=== Crash Information ===\n");
305 fprintf(infoFile,
306 "Crash Time: %s\n",
307 QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz").toLocal8Bit().constData());
308 fprintf(infoFile, "Process ID: %lu\n", GetCurrentProcessId());
309 fprintf(infoFile, "Thread ID: %lu\n", GetCurrentThreadId());
310 fprintf(infoFile, "Exception Code: 0x%08lX\n", pException->ExceptionRecord->ExceptionCode);
311 fprintf(infoFile, "Exception Address: 0x%p\n", pException->ExceptionRecord->ExceptionAddress);
312
313 // 系统信息
314 fprintf(infoFile, "\n=== System Information ===\n");
315 fprintf(infoFile, "Build ABI: %s\n", QSysInfo::buildAbi().toLocal8Bit().constData());
316 fprintf(infoFile, "Build CPU Architecture: %s\n", QSysInfo::buildCpuArchitecture().toLocal8Bit().constData());
317 fprintf(infoFile, "Current CPU Architecture: %s\n", QSysInfo::currentCpuArchitecture().toLocal8Bit().constData());
318 fprintf(infoFile, "Kernel Type: %s\n", QSysInfo::kernelType().toLocal8Bit().constData());
319 fprintf(infoFile, "Kernel Version: %s\n", QSysInfo::kernelVersion().toLocal8Bit().constData());
320 fprintf(infoFile, "Machine Host Name: %s\n", QSysInfo::machineHostName().toLocal8Bit().constData());
321 fprintf(infoFile, "Pretty Product Name: %s\n", QSysInfo::prettyProductName().toLocal8Bit().constData());
322 fprintf(infoFile, "Product Type: %s\n", QSysInfo::productType().toLocal8Bit().constData());
323 fprintf(infoFile, "Product Version: %s\n", QSysInfo::productVersion().toLocal8Bit().constData());
324
325 fclose(infoFile);
326 }
327
328 std::wstring wlpstr = createPath.toStdWString();
329 lpcwStr = wlpstr.c_str();
330 HANDLE hDumpFile = CreateFileW(lpcwStr, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
331
332 if (hDumpFile != INVALID_HANDLE_VALUE) {
333 // Dump信息
334 MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
335 dumpInfo.ExceptionPointers = pException;
336 dumpInfo.ThreadId = GetCurrentThreadId();
337 dumpInfo.ClientPointers = TRUE; // 设置为TRUE以获取更多信息
338
339 // 使用更详细的dump类型
340 MINIDUMP_TYPE dumpType = static_cast< MINIDUMP_TYPE >(MiniDumpNormal | MiniDumpWithDataSegs
341 | MiniDumpWithHandleData | MiniDumpWithUnloadedModules
342 | MiniDumpWithProcessThreadData);
343
344 // 写入Dump文件内容
345 BOOL result = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, dumpType, &dumpInfo, NULL, NULL);
346
347 if (result) {
348 qDebug() << "Dump file created successfully:" << createPath;
349 dumpSuccess = true;
350 } else {
351 DWORD error = GetLastError();
352 qDebug() << "Failed to create dump file, error code:" << error;
353 }
354
355 CloseHandle(hDumpFile);
356 } else {
357 DWORD error = GetLastError();
358 qDebug() << "Failed to create dump file handle, error code:" << error;
359 }
360
361 FpPostDump fpPostDump = DADumpCapture::getPostDumpFunc();
362 if (fpPostDump) {
363 fpPostDump(createPath, dumpSuccess);
364 }
365 } catch (const std::exception& e) {
366 qDebug() << "Exception in create dump:" << e.what();
367 }
368
369 return EXCEPTION_EXECUTE_HANDLER;
370}
371
372#endif // Q_CC_MSVC
373#endif // Q_OS_WIN
374} // end DA
375#endif // DADUMPCAPTURE_H
Dump文件捕获类
Definition DADumpCapture.h:82
static bool isInitialized()
检查是否已初始化
Definition DADumpCapture.h:181
static FpPreposeDump getPreposeDumpFunc()
获取前置处理函数指针
Definition DADumpCapture.h:93
static FpPostDump getPostDumpFunc()
获取后置处理函数指针
Definition DADumpCapture.h:102
static QString getDefaultDumpDirectory()
获取默认dump目录
Definition DADumpCapture.h:195
static void cleanupOldDumps(int daysToKeep=7, const QString &directory=QString())
清理旧的dump文件
Definition DADumpCapture.h:224
static void initDump(FpPreposeDump fpPre=nullptr, FpPostDump fpPost=nullptr)
初始化dump捕获
Definition DADumpCapture.h:132
序列化类都是带异常的,使用中需要处理异常
Definition AppMainWindow.cpp:44
std::function< QString() > FpPreposeDump
函数指针,这个函数指针会在形成dump文件之前执行,要求返回形成dump文件的完整路径,如果没有会赋予一个默认的
Definition DADumpCapture.h:37