RabbitCommon v2.3.3
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 // On x64, StackWalk64 modifies the context record, that could
57 // cause crashes, so we create a copy to prevent it
58 CONTEXT ctxCopy;
59 memcpy(&ctxCopy, ctx, sizeof(CONTEXT));
60
61 memset(&stack, 0, sizeof( STACKFRAME ));
62
63 process = GetCurrentProcess();
64 thread = GetCurrentThread();
65 displacement = 0;
66#if !defined(_M_AMD64)
67 stack.AddrPC.Offset = (*ctx).Eip;
68 stack.AddrPC.Mode = AddrModeFlat;
69 stack.AddrStack.Offset = (*ctx).Esp;
70 stack.AddrStack.Mode = AddrModeFlat;
71 stack.AddrFrame.Offset = (*ctx).Ebp;
72 stack.AddrFrame.Mode = AddrModeFlat;
73#endif
74
75 SymInitialize( process, NULL, TRUE ); //load symbols
76
77 for( frame = 0; ; frame++ )
78 {
79 //get next call from stack
80 result = StackWalk
81 (
82#if defined(_M_AMD64)
83 IMAGE_FILE_MACHINE_AMD64
84#else
85 IMAGE_FILE_MACHINE_I386
86#endif
87 ,
88 process,
89 thread,
90 &stack,
91 &ctxCopy,
92 NULL,
93 SymFunctionTableAccess,
94 SymGetModuleBase,
95 NULL
96 );
97
98 if( !result ) break;
99
100 //get symbol name for address
101 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
102 pSymbol->MaxNameLen = MAX_SYM_NAME;
103 SymFromAddr(process, ( ULONG64 )stack.AddrPC.Offset, &displacement, pSymbol);
104
105 QScopedPointer<IMAGEHLP_LINE> line(new IMAGEHLP_LINE());
106 line->SizeOfStruct = sizeof(IMAGEHLP_LINE);
107
108 //try to get line
109 if (SymGetLineFromAddr(process, stack.AddrPC.Offset, &disp, line.data()))
110 {
111 szStack += QString::number(no++) + " 0x" + QString::number(pSymbol->Address, 16)
112 + " [" + QString(pSymbol->Name) + "] in "
113 + QString(line->FileName)
114 + ":" + QString::number(line->LineNumber)
115 + "\n";
116 }
117 else
118 {
119 //failed to get line
120 hModule = NULL;
121 lstrcpyA(module.data(), "");
122 GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
123 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
124 (LPCTSTR)(stack.AddrPC.Offset), &hModule);
125
126 //at least print module name
127 if(hModule != NULL)
128 GetModuleFileNameA(hModule, module.data(), MaxNameLen);
129
130 szStack += QString::number(no++) + " 0x" + QString::number(pSymbol->Address, 16)
131 + " [" + QString(pSymbol->Name)
132 + "] in " + module.data() + "\n";
133 }
134 }
135 qCritical(log) << szStack.toStdString().c_str();
136 return szStack;
137}
138
139QString CoreDump(struct _EXCEPTION_POINTERS *pException)
140{
141 QString szPath = CDir::Instance()->GetDirLog() + QDir::separator() + "Core";
142 if (!QDir(szPath).exists())
143 {
144 QDir().mkdir(szPath);
145 }
146
147 QString dumpName = QDir::toNativeSeparators(
148 szPath + QDir::separator()
149 + qApp->applicationName()
150 + QString("_%1_%2_%3.dmp")
151 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"))
152 .arg(GetCurrentProcessId())
153 .arg(GetCurrentThreadId())
154 );
155 qCritical(log) << "The core dump file:" << dumpName;
156
157 QString msg;
158 HANDLE hDumpFile = CreateFileW(dumpName.toStdWString().c_str(),
159 GENERIC_READ | GENERIC_WRITE,
160 FILE_SHARE_WRITE | FILE_SHARE_READ,
161 0, CREATE_ALWAYS, 0, 0);
162 if (hDumpFile != INVALID_HANDLE_VALUE)
163 {
164 MINIDUMP_EXCEPTION_INFORMATION ExpParam;
165 ExpParam.ThreadId = GetCurrentThreadId();
166 ExpParam.ExceptionPointers = pException;
167 ExpParam.ClientPointers = TRUE;
168
169 // 创建Dump文件
170 MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
171 hDumpFile, MiniDumpWithDataSegs,
172 pException ? &ExpParam : nullptr, nullptr, nullptr);
173 CloseHandle(hDumpFile);
174 } else
175 qCritical(log) << "Core write file fail.";
176
177 return dumpName;
178}
179
180LONG WINAPI AppExceptionCallback(struct _EXCEPTION_POINTERS *pException)
181{
182 QString szStack;
183 QString dumpName;
184 bool bDumpToLogFile = true;
185 bool bDumpFile = true;
186 QString szConfigure = CLog::Instance()->GetLogConfigureFile();
187 if(!szConfigure.isEmpty()) {
188 QSettings set(szConfigure, QSettings::IniFormat);
189 bDumpToLogFile = set.value("CoreDump/DumpToLogFile", true).toBool();
190 bDumpFile = set.value("CoreDump/DumpFile", true).toBool();
191 }
192 if(!(bDumpFile || bDumpToLogFile))
193 return EXCEPTION_CONTINUE_SEARCH;
194 if(bDumpToLogFile)
195 szStack = PrintStack(pException);
196 if(bDumpFile)
197 dumpName = CoreDump(pException);
198
199 // 提示
200 QString szTitle = QObject::tr("Application Error");
201 QString szContent
202 = QObject::tr("I'm Sorry, Application is Crash!") + "\n\n"
203 + QObject::tr("Current path: ") + QDir::currentPath() + "\n\n";
204 if(bDumpFile)
205 szContent += QObject::tr("Dump file: ") + dumpName + "\n\n";
206 if(bDumpToLogFile)
207 szContent += QObject::tr("Log file: ") + RabbitCommon::CLog::Instance()->GetLogFile();
208#ifdef HAVE_RABBITCOMMON_GUI
209 CTools::Instance()->ShowCoreDialog(szTitle, szContent, szStack, dumpName);
210#endif
211
212 LONG ret = EXCEPTION_EXECUTE_HANDLER;
213 if(g_UnhandledException) {
214 qCritical(log) << "Call system default exception";
215 ret = g_UnhandledException(pException);
216 }
217 qCritical(log) << "Exception exit:" << ret;
218 return ret;
219}
220
221void EnableMiniDumper()
222{
223 g_UnhandledException = SetUnhandledExceptionFilter(AppExceptionCallback);
224}
225
226} // namespace RabbitCommon
227
228#endif //Q_OS_WIN