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