RabbitCommon v2.3.3
Loading...
Searching...
No Matches
MiniDumper.cpp
1/*
2 * Copyright Copyright (c) Kang Lin studio, All Rights Reserved
3 * Author Kang Lin <kl222@126.com>
4 *
5 * Windows下dump文件生成与分析: https://blog.csdn.net/icandoit_2014/article/details/78739962
6 * windows下C++程序崩溃时能够产生core文件: https://blog.csdn.net/Manketon/article/details/81565154
7 * windows下生成core dump文件: https://blog.csdn.net/freedom8531/article/details/53195919
8 */
9
10#include <windows.h>
11#include <stdio.h>
12#include <assert.h>
13#include <time.h>
14#include <stdlib.h>
15#include <tchar.h>
16#include <strsafe.h>
17#include <dbghelp.h>
18#include "MiniDumper.h"
19
20namespace RabbitCommon {
21
22#ifdef UNICODE
23 #define _tcssprintf wsprintf
24 #define tcsplitpath _wsplitpath
25#else
26 #define _tcssprintf sprintf
27 #define tcsplitpath _splitpath
28#endif
29
30const int USER_DATA_BUFFER_SIZE = 4096;
31
32//-----------------------------------------------------------------------------
33// GLOBALS
34//-----------------------------------------------------------------------------
35CMiniDumper* CMiniDumper::s_pMiniDumper = NULL;
36LPCRITICAL_SECTION CMiniDumper::s_pCriticalSection = NULL;
37
38// Based on dbghelp.h
39typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess,
40 DWORD dwPid,
41 HANDLE hFile,
42 MINIDUMP_TYPE DumpType,
43 CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
44 CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
45 CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
46
47//-----------------------------------------------------------------------------
48// Name: CMiniDumper()
49// Desc: Constructor
50//-----------------------------------------------------------------------------
51CMiniDumper::CMiniDumper( bool bPromptUserForMiniDump )
52{
53 // Our CMiniDumper should act alone as a singleton.
54 assert( !s_pMiniDumper );
55
56 s_pMiniDumper = this;
57 m_bPromptUserForMiniDump = bPromptUserForMiniDump;
58
59 // The SetUnhandledExceptionFilter function enables an application to
60 // supersede the top-level exception handler of each thread and process.
61 // After calling this function, if an exception occurs in a process
62 // that is not being debugged, and the exception makes it to the
63 // unhandled exception filter, that filter will call the exception
64 // filter function specified by the lpTopLevelExceptionFilter parameter.
65 ::SetUnhandledExceptionFilter( unhandledExceptionHandler );
66
67 // Since DBGHELP.dll is not inherently thread-safe, making calls into it
68 // from more than one thread simultaneously may yield undefined behavior.
69 // This means that if your application has multiple threads, or is
70 // called by multiple threads in a non-synchronized manner, you need to
71 // make sure that all calls into DBGHELP.dll are isolated via a global
72 // critical section.
73 s_pCriticalSection = new CRITICAL_SECTION;
74
75 if( s_pCriticalSection )
76 InitializeCriticalSection( s_pCriticalSection );
77}
78
79//-----------------------------------------------------------------------------
80// Name: ~CMiniDumper()
81// Desc: Destructor
82//-----------------------------------------------------------------------------
83CMiniDumper::~CMiniDumper( void )
84{
85 if( s_pCriticalSection )
86 {
87 DeleteCriticalSection( s_pCriticalSection );
88 delete s_pCriticalSection;
89 }
90}
91
92//-----------------------------------------------------------------------------
93// Name: unhandledExceptionHandler()
94// Desc: Call-back filter function for unhandled exceptions
95//-----------------------------------------------------------------------------
96LONG CMiniDumper::unhandledExceptionHandler( _EXCEPTION_POINTERS *pExceptionInfo )
97{
98 if( !s_pMiniDumper )
99 return EXCEPTION_CONTINUE_SEARCH;
100
101 return s_pMiniDumper->writeMiniDump( pExceptionInfo );
102}
103
104//-----------------------------------------------------------------------------
105// Name: setMiniDumpFileName()
106// Desc:
107//-----------------------------------------------------------------------------
108void CMiniDumper::setMiniDumpFileName( void )
109{
110 time_t currentTime;
111 time( &currentTime );
112
113 wsprintf( m_szMiniDumpPath,
114 L"%s%s.%ld.dmp",
115 m_szAppPath,
116 m_szAppBaseName,
117 currentTime );
118}
119
120//-----------------------------------------------------------------------------
121// Name: getImpersonationToken()
122// Desc: The method acts as a potential workaround for the fact that the
123// current thread may not have a token assigned to it, and if not, the
124// process token is received.
125//-----------------------------------------------------------------------------
126bool CMiniDumper::getImpersonationToken( HANDLE* phToken )
127{
128 *phToken = NULL;
129
130 if( !OpenThreadToken( GetCurrentThread(),
131 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
132 TRUE,
133 phToken) )
134 {
135 if( GetLastError() == ERROR_NO_TOKEN )
136 {
137 // No impersonation token for the current thread is available.
138 // Let's go for the process token instead.
139 if( !OpenProcessToken( GetCurrentProcess(),
140 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
141 phToken) )
142 return false;
143 }
144 else
145 return false;
146 }
147
148 return true;
149}
150
151//-----------------------------------------------------------------------------
152// Name: enablePrivilege()
153// Desc: Since a MiniDump contains a lot of meta-data about the OS and
154// application state at the time of the dump, it is a rather privileged
155// operation. This means we need to set the SeDebugPrivilege to be able
156// to call MiniDumpWriteDump.
157//-----------------------------------------------------------------------------
158BOOL CMiniDumper::enablePrivilege( LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld )
159{
160 BOOL bOk = FALSE;
161
162 TOKEN_PRIVILEGES tp;
163 tp.PrivilegeCount = 1;
164 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
165 bOk = LookupPrivilegeValue( 0, pszPriv, &tp.Privileges[0].Luid );
166
167 if( bOk )
168 {
169 DWORD cbOld = sizeof(*ptpOld);
170 bOk = AdjustTokenPrivileges( hToken, FALSE, &tp, cbOld, ptpOld, &cbOld );
171 }
172
173 return (bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()));
174}
175
176//-----------------------------------------------------------------------------
177// Name: restorePrivilege()
178// Desc:
179//-----------------------------------------------------------------------------
180BOOL CMiniDumper::restorePrivilege( HANDLE hToken, TOKEN_PRIVILEGES* ptpOld )
181{
182 BOOL bOk = AdjustTokenPrivileges(hToken, FALSE, ptpOld, 0, NULL, NULL);
183 return ( bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()) );
184}
185
186//-----------------------------------------------------------------------------
187// Name: writeMiniDump()
188// Desc:
189//-----------------------------------------------------------------------------
190LONG CMiniDumper::writeMiniDump( _EXCEPTION_POINTERS *pExceptionInfo )
191{
192 LONG retval = EXCEPTION_CONTINUE_SEARCH;
193 m_pExceptionInfo = pExceptionInfo;
194
195 HANDLE hImpersonationToken = NULL;
196 if( !getImpersonationToken( &hImpersonationToken ) )
197 return FALSE;
198
199 // You have to find the right dbghelp.dll.
200 // Look next to the EXE first since the one in System32 might be old (Win2k)
201
202 HMODULE hDll = NULL;
203 TCHAR szDbgHelpPath[MAX_PATH];
204
205 if( GetModuleFileName( NULL, m_szAppPath, _MAX_PATH ) )
206 {
207 TCHAR *pSlash = wcsrchr( m_szAppPath, '\\' );
208
209 if( pSlash )
210 {
211 _tcscpy_s( m_szAppBaseName, pSlash + 1);
212 *(pSlash+1) = 0;
213 }
214
215 wcscpy_s( szDbgHelpPath, m_szAppPath );
216 wcscat_s( szDbgHelpPath, L"DBGHELP.DLL");
217 hDll = ::LoadLibrary( szDbgHelpPath );
218 }
219
220 if( hDll == NULL )
221 {
222 // If we haven't found it yet - try one more time.
223 hDll = ::LoadLibrary( L"DBGHELP.DLL");
224 }
225
226 LPCTSTR szResult = NULL;
227
228 if( hDll )
229 {
230 // Get the address of the MiniDumpWriteDump function, which writes
231 // user-mode mini-dump information to a specified file.
232 MINIDUMPWRITEDUMP MiniDumpWriteDump =
233 (MINIDUMPWRITEDUMP)::GetProcAddress( hDll, "MiniDumpWriteDump" );
234
235 if( MiniDumpWriteDump != NULL )
236 {
237 TCHAR szScratch[USER_DATA_BUFFER_SIZE];
238
239 setMiniDumpFileName();
240
241 // Ask the user if he or she wants to save a mini-dump file...
242 wsprintf( szScratch,
243 L"There was an unexpected error:\n\nWould you "
244 L"like to create a mini-dump file?\n\n%s ",
245 m_szMiniDumpPath);
246
247 // Create the mini-dump file...
248 HANDLE hFile = ::CreateFile( m_szMiniDumpPath,
249 GENERIC_WRITE,
250 FILE_SHARE_WRITE,
251 NULL,
252 CREATE_ALWAYS,
253 FILE_ATTRIBUTE_NORMAL,
254 NULL );
255
256 if( hFile != INVALID_HANDLE_VALUE )
257 {
258 _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
259 ExInfo.ThreadId = ::GetCurrentThreadId();
260 ExInfo.ExceptionPointers = pExceptionInfo;
261 ExInfo.ClientPointers = NULL;
262
263 // We need the SeDebugPrivilege to be able to run MiniDumpWriteDump
264 TOKEN_PRIVILEGES tp;
265 BOOL bPrivilegeEnabled = enablePrivilege( SE_DEBUG_NAME, hImpersonationToken, &tp );
266
267 BOOL bOk;
268
269 // DBGHELP.dll is not thread-safe, so we need to restrict access...
270 EnterCriticalSection( s_pCriticalSection );
271 {
272 // Write out the mini-dump data to the file...
273 bOk = MiniDumpWriteDump( GetCurrentProcess(),
274 GetCurrentProcessId(),
275 hFile,
276 MiniDumpNormal,
277 &ExInfo,
278 NULL,
279 NULL );
280 }
281 LeaveCriticalSection( s_pCriticalSection );
282
283 // Restore the privileges when done
284 if( bPrivilegeEnabled )
285 restorePrivilege( hImpersonationToken, &tp );
286
287 if( bOk )
288 {
289 szResult = NULL;
290 retval = EXCEPTION_EXECUTE_HANDLER;
291 }
292 else
293 {
294 wsprintf( szScratch,
295 L"Failed to save the mini-dump file to '%s' (error %d)",
296 m_szMiniDumpPath,
297 GetLastError() );
298
299 szResult = szScratch;
300 }
301
302 ::CloseHandle( hFile );
303 }
304 else
305 {
306 wsprintf( szScratch,
307 L"Failed to create the mini-dump file '%s' (error %d)",
308 m_szMiniDumpPath,
309 GetLastError() );
310
311 szResult = szScratch;
312 }
313 }
314 else
315 {
316 szResult = L"Call to GetProcAddress failed to find MiniDumpWriteDump. "
317 L"The DBGHELP.DLL is possibly outdated." ;
318 }
319 }
320 else
321 {
322 szResult = L"Call to LoadLibrary failed to find DBGHELP.DLL.";
323 }
324
325 if(m_bPromptUserForMiniDump)
326 {
327 if( szResult )
328 ::MessageBox( NULL, szResult, NULL, MB_OK );
329 else
330 {
331 TCHAR msg[1024];
332 wsprintf(msg, _T("I'm Sorry, Application is Crash! The path: %s"), m_szMiniDumpPath);
333 ::MessageBox(NULL, msg, _T("Application Error"), MB_OK);
334 }
335 }
336
337 TerminateProcess( GetCurrentProcess(), 0 );
338
339 return retval;
340}
341
342} //namespace RabbitCommon