RabbitCommon v2.3.4
Loading...
Searching...
No Matches
QMiniDumper.cpp
1// Copyright Copyright (c) Kang Lin studio, All Rights Reserved
2// Author Kang Lin <kl222@126.com>
3
4// Window以及Ubuntu下的core dump调试方法: https://blog.csdn.net/zhoukehu_CSDN/article/details/125152019?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-125152019-blog-109705192.235^v38^pc_relevant_sort_base3&spm=1001.2101.3001.4242.1&utm_relevant_index=3
5// Windows平台coredump处理: https://blog.csdn.net/mumufan05/article/details/109705192?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2-109705192-blog-7306282.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2-109705192-blog-7306282.pc_relevant_aa&utm_relevant_index=5
6// Visual Studio 调试器中的转储文件: https://learn.microsoft.com/zh-cn/visualstudio/debugger/using-dump-files?view=vs-2022
7
8#include <qglobal.h>
9
10#ifdef Q_OS_WIN
11#include <Windows.h>
12#include "dbghelp.h"
13#include <QDir>
14#include <QDateTime>
15#include <QTranslator>
16#include <QCoreApplication>
17#include <QLoggingCategory>
18
19#include <QSettings>
20#include "MiniDumper.h"
21#include "Log/Log.h"
22#include "RabbitCommonDir.h"
23#include "RabbitCommonTools.h"
24
25//#pragma comment( lib, "Dbghelp.lib" )
26
27namespace RabbitCommon {
28
29LPTOP_LEVEL_EXCEPTION_FILTER g_UnhandledException = nullptr;
30
31static Q_LOGGING_CATEGORY(log, "RabbitCommon.CoreDump.QMinDumper")
32const int MaxNameLen = 256;
33QString PrintStack(struct _EXCEPTION_POINTERS *pException) //Prints stack trace based on context record
34{
35 QString szStack;
36 int no = 1;
37 BOOL result = false;
38 HANDLE process = NULL;
39 HANDLE thread = NULL;
40 HMODULE hModule = NULL;
41
42 STACKFRAME stack;
43 ULONG frame = 0;
44 DWORD64 displacement = 0;
45
46 DWORD disp = 0;
47
48 QScopedArrayPointer<char> buffer(new char[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]);
49 QScopedArrayPointer<char> name(new char[MaxNameLen]);
50 QScopedArrayPointer<char> module(new char[MaxNameLen]);
51 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer.data();
52
53 szStack = "*** Exception 0x" + QString::number(pException->ExceptionRecord->ExceptionCode, 16) + " occurred ***\n\n";
54 PCONTEXT ctx = pException->ContextRecord;
55
56 // 创建上下文副本
57 // On x64, StackWalk64 modifies the context record, that could
58 // cause crashes, so we create a copy to prevent it
59 CONTEXT ctxCopy;
60 memcpy(&ctxCopy, ctx, sizeof(CONTEXT));
61
62 memset(&stack, 0, sizeof( STACKFRAME ));
63
64 process = GetCurrentProcess();
65 thread = GetCurrentThread();
66 displacement = 0;
67
68 // ARM64 架构特定的初始化
69#if defined(_M_ARM64)
70 stack.AddrPC.Offset = ctxCopy.Pc;
71 stack.AddrPC.Mode = AddrModeFlat;
72 stack.AddrStack.Offset = ctxCopy.Sp;
73 stack.AddrStack.Mode = AddrModeFlat;
74 stack.AddrFrame.Offset = ctxCopy.Fp;
75 stack.AddrFrame.Mode = AddrModeFlat;
76#elif defined(_M_AMD64)
77 stack.AddrPC.Offset = ctxCopy.Rip;
78 stack.AddrPC.Mode = AddrModeFlat;
79 stack.AddrStack.Offset = ctxCopy.Rsp;
80 stack.AddrStack.Mode = AddrModeFlat;
81 stack.AddrFrame.Offset = ctxCopy.Rbp;
82 stack.AddrFrame.Mode = AddrModeFlat;
83#elif defined(_M_IX86)
84 stack.AddrPC.Offset = ctxCopy.Eip;
85 stack.AddrPC.Mode = AddrModeFlat;
86 stack.AddrStack.Offset = ctxCopy.Esp;
87 stack.AddrStack.Mode = AddrModeFlat;
88 stack.AddrFrame.Offset = ctxCopy.Ebp;
89 stack.AddrFrame.Mode = AddrModeFlat;
90#else
91#error "Unsupported architecture"
92#endif
93
94 // 初始化符号
95 SymInitialize(process, NULL, TRUE);
96
97 // 设置机器类型
98 DWORD machineType;
99#if defined(_M_ARM64)
100 machineType = IMAGE_FILE_MACHINE_ARM64;
101#elif defined(_M_AMD64)
102 machineType = IMAGE_FILE_MACHINE_AMD64;
103#elif defined(_M_IX86)
104 machineType = IMAGE_FILE_MACHINE_I386;
105#endif
106
107 for( frame = 0; ; frame++ )
108 {
109 //get next call from stack
110 result = StackWalk
111 (
112 machineType,
113 process,
114 thread,
115 &stack,
116 &ctxCopy,
117 NULL,
118 SymFunctionTableAccess,
119 SymGetModuleBase,
120 NULL
121 );
122
123 if( !result || stack.AddrPC.Offset == 0) break;
124
125 //get symbol name for address
126 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
127 pSymbol->MaxNameLen = MAX_SYM_NAME;
128 if (!SymFromAddr(process, stack.AddrPC.Offset, &displacement, pSymbol)) {
129 szStack += QString::number(no++) + " 0x" + QString::number(stack.AddrPC.Offset, 16)
130 + " [Unknown Symbol]\n";
131 continue;
132 }
133
134 // 尝试获取行号信息
135#if defined(_M_IX86)
136 QScopedPointer<IMAGEHLP_LINE> line(new IMAGEHLP_LINE());
137 line->SizeOfStruct = sizeof(IMAGEHLP_LINE);
138
139 //try to get line
140 if (SymGetLineFromAddr(process, stack.AddrPC.Offset, &disp, line.data()))
141 {
142 szStack += QString::number(no++) + " 0x" + QString::number(pSymbol->Address, 16)
143 + " [" + QString(pSymbol->Name) + "] in "
144 + QString(line->FileName)
145 + ":" + QString::number(line->LineNumber)
146 + "\n";
147 }
148#else
149 QScopedPointer<IMAGEHLP_LINE64> line(new IMAGEHLP_LINE64());
150 line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
151
152 if (SymGetLineFromAddr64(process, stack.AddrPC.Offset, &disp, line.data())) {
153 szStack += QString::number(no++) + " 0x" + QString::number(pSymbol->Address, 16)
154 + " [" + QString::fromUtf8(pSymbol->Name) + "] in "
155 + QString::fromUtf8(line->FileName)
156 + ":" + QString::number(line->LineNumber)
157 + "\n";
158 }
159#endif
160 else
161 {
162 // 获取模块信息
163 hModule = NULL;
164 lstrcpyA(module.data(), "");
165 GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
166 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
167 (LPCTSTR)(stack.AddrPC.Offset), &hModule);
168
169 //at least print module name
170 if(hModule != NULL)
171 GetModuleFileNameA(hModule, module.data(), MaxNameLen);
172
173 szStack += QString::number(no++) + " 0x" + QString::number(pSymbol->Address, 16)
174 + " [" + QString(pSymbol->Name)
175 + "] in " + module.data() + "\n";
176 }
177 }
178 qCritical(log) << szStack.toStdString().c_str();
179 return szStack;
180}
181
182QString CoreDump(struct _EXCEPTION_POINTERS *pException)
183{
184 QString szPath = CDir::Instance()->GetDirLog() + QDir::separator() + "Core";
185 if (!QDir(szPath).exists())
186 {
187 QDir().mkdir(szPath);
188 }
189
190 QString dumpName = QDir::toNativeSeparators(
191 szPath + QDir::separator()
192 + qApp->applicationName()
193 + QString("_%1_%2_%3.dmp")
194 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"))
195 .arg(GetCurrentProcessId())
196 .arg(GetCurrentThreadId())
197 );
198 qCritical(log) << "The core dump file:" << dumpName;
199
200 QString msg;
201 HANDLE hDumpFile = CreateFileW(dumpName.toStdWString().c_str(),
202 GENERIC_READ | GENERIC_WRITE,
203 FILE_SHARE_WRITE | FILE_SHARE_READ,
204 0, CREATE_ALWAYS, 0, 0);
205 if (hDumpFile != INVALID_HANDLE_VALUE)
206 {
207 MINIDUMP_EXCEPTION_INFORMATION ExpParam;
208 ExpParam.ThreadId = GetCurrentThreadId();
209 ExpParam.ExceptionPointers = pException;
210 ExpParam.ClientPointers = TRUE;
211
212 // 创建Dump文件
213 MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
214 hDumpFile, MiniDumpWithDataSegs,
215 pException ? &ExpParam : nullptr, nullptr, nullptr);
216 CloseHandle(hDumpFile);
217 } else
218 qCritical(log) << "Core write file fail.";
219
220 return dumpName;
221}
222
223LONG WINAPI AppExceptionCallback(struct _EXCEPTION_POINTERS *pException)
224{
225 QString szStack;
226 QString dumpName;
227 bool bDumpToLogFile = true;
228 bool bDumpFile = true;
229 QString szConfigure = CLog::Instance()->GetLogConfigureFile();
230 if(!szConfigure.isEmpty()) {
231 QSettings set(szConfigure, QSettings::IniFormat);
232 bDumpToLogFile = set.value("CoreDump/DumpToLogFile", true).toBool();
233 bDumpFile = set.value("CoreDump/DumpFile", true).toBool();
234 }
235 if(!(bDumpFile || bDumpToLogFile))
236 return EXCEPTION_CONTINUE_SEARCH;
237 if(bDumpToLogFile)
238 szStack = PrintStack(pException);
239 if(bDumpFile)
240 dumpName = CoreDump(pException);
241
242 // 提示
243 QString szTitle = QObject::tr("Application Error");
244 QString szContent
245 = QObject::tr("I'm Sorry, Application is Crash!") + "\n\n"
246 + QObject::tr("Current path: ") + QDir::currentPath() + "\n\n";
247 if(bDumpFile)
248 szContent += QObject::tr("Dump file: ") + dumpName + "\n\n";
249 if(bDumpToLogFile)
250 szContent += QObject::tr("Log file: ") + RabbitCommon::CLog::Instance()->GetLogFile();
251#ifdef HAVE_RABBITCOMMON_GUI
252 CTools::Instance()->ShowCoreDialog(szTitle, szContent, szStack, dumpName);
253#endif
254
255 LONG ret = EXCEPTION_EXECUTE_HANDLER;
256 if(g_UnhandledException) {
257 qCritical(log) << "Call system default exception";
258 ret = g_UnhandledException(pException);
259 }
260 qCritical(log) << "Exception exit:" << ret;
261 return ret;
262}
263
264void EnableMiniDumper()
265{
266 g_UnhandledException = SetUnhandledExceptionFilter(AppExceptionCallback);
267}
268
269} // namespace RabbitCommon
270
271#endif //Q_OS_WIN