1 | //========================================== |
---|
2 | // Matt Pietrek |
---|
3 | // MSDN Magazine, 2002 |
---|
4 | // FILE: WheatyExceptionReport.CPP |
---|
5 | //========================================== |
---|
6 | #define WIN32_LEAN_AND_MEAN |
---|
7 | #pragma warning(disable:4996) |
---|
8 | #pragma warning(disable:4312) |
---|
9 | #pragma warning(disable:4311) |
---|
10 | #include <windows.h> |
---|
11 | #include <tlhelp32.h> |
---|
12 | #include <stdio.h> |
---|
13 | #include <tchar.h> |
---|
14 | #define _NO_CVCONST_H |
---|
15 | #include <dbghelp.h> |
---|
16 | #include "WheatyExceptionReport.h" |
---|
17 | #include "svn_revision.h" |
---|
18 | #define CrashFolder _T("Crashs") |
---|
19 | //#pragma comment(linker, "/defaultlib:dbghelp.lib") |
---|
20 | |
---|
21 | inline LPTSTR ErrorMessage(DWORD dw) |
---|
22 | { |
---|
23 | LPVOID lpMsgBuf; |
---|
24 | FormatMessage( |
---|
25 | FORMAT_MESSAGE_ALLOCATE_BUFFER | |
---|
26 | FORMAT_MESSAGE_FROM_SYSTEM, |
---|
27 | NULL, |
---|
28 | dw, |
---|
29 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
---|
30 | (LPTSTR) &lpMsgBuf, |
---|
31 | 0, NULL ); |
---|
32 | return (LPTSTR)lpMsgBuf; |
---|
33 | } |
---|
34 | |
---|
35 | //============================== Global Variables ============================= |
---|
36 | |
---|
37 | // |
---|
38 | // Declare the static variables of the WheatyExceptionReport class |
---|
39 | // |
---|
40 | TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH]; |
---|
41 | LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter; |
---|
42 | HANDLE WheatyExceptionReport::m_hReportFile; |
---|
43 | HANDLE WheatyExceptionReport::m_hProcess; |
---|
44 | |
---|
45 | // Declare global instance of class |
---|
46 | WheatyExceptionReport g_WheatyExceptionReport; |
---|
47 | |
---|
48 | //============================== Class Methods ============================= |
---|
49 | |
---|
50 | WheatyExceptionReport::WheatyExceptionReport( ) // Constructor |
---|
51 | { |
---|
52 | // Install the unhandled exception filter function |
---|
53 | m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter); |
---|
54 | m_hProcess = GetCurrentProcess(); |
---|
55 | } |
---|
56 | |
---|
57 | //============ |
---|
58 | // Destructor |
---|
59 | //============ |
---|
60 | WheatyExceptionReport::~WheatyExceptionReport( ) |
---|
61 | { |
---|
62 | if(m_previousFilter) |
---|
63 | SetUnhandledExceptionFilter( m_previousFilter ); |
---|
64 | } |
---|
65 | |
---|
66 | //=========================================================== |
---|
67 | // Entry point where control comes on an unhandled exception |
---|
68 | //=========================================================== |
---|
69 | LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter( |
---|
70 | PEXCEPTION_POINTERS pExceptionInfo ) |
---|
71 | { |
---|
72 | TCHAR module_folder_name[MAX_PATH]; |
---|
73 | GetModuleFileName( 0, module_folder_name, MAX_PATH ); |
---|
74 | TCHAR* pos = _tcsrchr(module_folder_name, '\\'); |
---|
75 | if(!pos) |
---|
76 | return 0; |
---|
77 | pos[0] = '\0'; |
---|
78 | ++pos; |
---|
79 | |
---|
80 | TCHAR crash_folder_path[MAX_PATH]; |
---|
81 | sprintf(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder); |
---|
82 | if(!CreateDirectory(crash_folder_path, NULL)) |
---|
83 | { |
---|
84 | if(GetLastError() != ERROR_ALREADY_EXISTS) |
---|
85 | return 0; |
---|
86 | } |
---|
87 | |
---|
88 | SYSTEMTIME systime; |
---|
89 | GetLocalTime(&systime); |
---|
90 | sprintf(m_szLogFileName, "%s\\%s_[%u-%u_%u-%u-%u].txt", |
---|
91 | crash_folder_path, pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond |
---|
92 | ); |
---|
93 | |
---|
94 | m_hReportFile = CreateFile( m_szLogFileName, |
---|
95 | GENERIC_WRITE, |
---|
96 | 0, |
---|
97 | 0, |
---|
98 | OPEN_ALWAYS, |
---|
99 | FILE_FLAG_WRITE_THROUGH, |
---|
100 | 0 ); |
---|
101 | |
---|
102 | if ( m_hReportFile ) |
---|
103 | { |
---|
104 | SetFilePointer( m_hReportFile, 0, 0, FILE_END ); |
---|
105 | |
---|
106 | GenerateExceptionReport( pExceptionInfo ); |
---|
107 | |
---|
108 | CloseHandle( m_hReportFile ); |
---|
109 | m_hReportFile = 0; |
---|
110 | } |
---|
111 | |
---|
112 | if ( m_previousFilter ) |
---|
113 | return m_previousFilter( pExceptionInfo ); |
---|
114 | else |
---|
115 | return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/; |
---|
116 | } |
---|
117 | |
---|
118 | BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount) |
---|
119 | { |
---|
120 | if(!sProcessorName) |
---|
121 | return FALSE; |
---|
122 | |
---|
123 | HKEY hKey; |
---|
124 | LONG lRet; |
---|
125 | lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), |
---|
126 | 0, KEY_QUERY_VALUE, &hKey); |
---|
127 | if (lRet != ERROR_SUCCESS) |
---|
128 | return FALSE; |
---|
129 | TCHAR szTmp[2048]; |
---|
130 | DWORD cntBytes = sizeof(szTmp); |
---|
131 | lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), NULL, NULL, |
---|
132 | (LPBYTE)szTmp, &cntBytes); |
---|
133 | if (lRet != ERROR_SUCCESS) |
---|
134 | return FALSE; |
---|
135 | ::RegCloseKey(hKey); |
---|
136 | sProcessorName[0] = '\0'; |
---|
137 | // Skip spaces |
---|
138 | TCHAR* psz = szTmp; |
---|
139 | while (iswspace(*psz)) |
---|
140 | ++psz; |
---|
141 | _tcsncpy(sProcessorName, psz, maxcount); |
---|
142 | return TRUE; |
---|
143 | } |
---|
144 | |
---|
145 | BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax) |
---|
146 | { |
---|
147 | // Try calling GetVersionEx using the OSVERSIONINFOEX structure. |
---|
148 | // If that fails, try using the OSVERSIONINFO structure. |
---|
149 | OSVERSIONINFOEX osvi = { 0 }; |
---|
150 | osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); |
---|
151 | BOOL bOsVersionInfoEx; |
---|
152 | bOsVersionInfoEx = ::GetVersionEx((LPOSVERSIONINFO)(&osvi)); |
---|
153 | if (!bOsVersionInfoEx) |
---|
154 | { |
---|
155 | osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); |
---|
156 | if (!::GetVersionEx((OSVERSIONINFO*)&osvi)) |
---|
157 | return FALSE; |
---|
158 | } |
---|
159 | *szVersion = _T('\0'); |
---|
160 | TCHAR wszTmp[128]; |
---|
161 | switch (osvi.dwPlatformId) |
---|
162 | { |
---|
163 | // Windows NT product family. |
---|
164 | case VER_PLATFORM_WIN32_NT: |
---|
165 | // Test for the specific product family. |
---|
166 | if (osvi.dwMajorVersion == 6) |
---|
167 | _tcsncat(szVersion, _T("Windows Vista or Windows Server 2008 "), cntMax); |
---|
168 | if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) |
---|
169 | _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax); |
---|
170 | if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) |
---|
171 | _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax); |
---|
172 | if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) |
---|
173 | _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax); |
---|
174 | if (osvi.dwMajorVersion <= 4 ) |
---|
175 | _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax); |
---|
176 | |
---|
177 | // Test for specific product on Windows NT 4.0 SP6 and later. |
---|
178 | if (bOsVersionInfoEx) |
---|
179 | { |
---|
180 | // Test for the workstation type. |
---|
181 | #if WINVER < 0x0500 |
---|
182 | if (osvi.wReserved[1] == VER_NT_WORKSTATION) |
---|
183 | #else |
---|
184 | if (osvi.wProductType == VER_NT_WORKSTATION) |
---|
185 | #endif // WINVER < 0x0500 |
---|
186 | { |
---|
187 | if (osvi.dwMajorVersion == 4) |
---|
188 | _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax); |
---|
189 | #if WINVER < 0x0500 |
---|
190 | else if (osvi.wReserved[0] & VER_SUITE_PERSONAL) |
---|
191 | #else |
---|
192 | else if (osvi.wSuiteMask & VER_SUITE_PERSONAL) |
---|
193 | #endif // WINVER < 0x0500 |
---|
194 | _tcsncat(szVersion, _T("Home Edition "), cntMax); |
---|
195 | #if WINVER < 0x0500 |
---|
196 | else if (osvi.wReserved[0] & VER_SUITE_EMBEDDEDNT) |
---|
197 | #else |
---|
198 | else if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT) |
---|
199 | #endif // WINVER < 0x0500 |
---|
200 | _tcsncat(szVersion, _T("Embedded "), cntMax); |
---|
201 | else |
---|
202 | _tcsncat(szVersion, _T("Professional "), cntMax); |
---|
203 | } |
---|
204 | // Test for the server type. |
---|
205 | #if WINVER < 0x0500 |
---|
206 | else if (osvi.wReserved[1] == VER_NT_SERVER) |
---|
207 | #else |
---|
208 | else if (osvi.wProductType == VER_NT_SERVER) |
---|
209 | #endif // WINVER < 0x0500 |
---|
210 | { |
---|
211 | if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) |
---|
212 | { |
---|
213 | #if WINVER < 0x0500 |
---|
214 | if (osvi.wReserved[0] & VER_SUITE_DATACENTER) |
---|
215 | #else |
---|
216 | if (osvi.wSuiteMask & VER_SUITE_DATACENTER) |
---|
217 | #endif // WINVER < 0x0500 |
---|
218 | _tcsncat(szVersion, _T("Datacenter Edition "), cntMax); |
---|
219 | #if WINVER < 0x0500 |
---|
220 | else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE) |
---|
221 | #else |
---|
222 | else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) |
---|
223 | #endif // WINVER < 0x0500 |
---|
224 | _tcsncat(szVersion, _T("Enterprise Edition "), cntMax); |
---|
225 | #if WINVER < 0x0500 |
---|
226 | else if (osvi.wReserved[0] == VER_SUITE_BLADE) |
---|
227 | #else |
---|
228 | else if (osvi.wSuiteMask == VER_SUITE_BLADE) |
---|
229 | #endif // WINVER < 0x0500 |
---|
230 | _tcsncat(szVersion, _T("Web Edition "), cntMax); |
---|
231 | else |
---|
232 | _tcsncat(szVersion, _T("Standard Edition "), cntMax); |
---|
233 | } |
---|
234 | else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) |
---|
235 | { |
---|
236 | #if WINVER < 0x0500 |
---|
237 | if (osvi.wReserved[0] & VER_SUITE_DATACENTER) |
---|
238 | #else |
---|
239 | if (osvi.wSuiteMask & VER_SUITE_DATACENTER) |
---|
240 | #endif // WINVER < 0x0500 |
---|
241 | _tcsncat(szVersion, _T("Datacenter Server "), cntMax); |
---|
242 | #if WINVER < 0x0500 |
---|
243 | else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE ) |
---|
244 | #else |
---|
245 | else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) |
---|
246 | #endif // WINVER < 0x0500 |
---|
247 | _tcsncat(szVersion, _T("Advanced Server "), cntMax); |
---|
248 | else |
---|
249 | _tcsncat(szVersion, _T("Server "), cntMax); |
---|
250 | } |
---|
251 | else // Windows NT 4.0 |
---|
252 | { |
---|
253 | #if WINVER < 0x0500 |
---|
254 | if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE) |
---|
255 | #else |
---|
256 | if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) |
---|
257 | #endif // WINVER < 0x0500 |
---|
258 | _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax); |
---|
259 | else |
---|
260 | _tcsncat(szVersion, _T("Server 4.0 "), cntMax); |
---|
261 | } |
---|
262 | } |
---|
263 | } |
---|
264 | // Display service pack (if any) and build number. |
---|
265 | if (osvi.dwMajorVersion == 4 && _tcsicmp(osvi.szCSDVersion, _T("Service Pack 6")) == 0) |
---|
266 | { |
---|
267 | HKEY hKey; |
---|
268 | LONG lRet; |
---|
269 | |
---|
270 | // Test for SP6 versus SP6a. |
---|
271 | lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey); |
---|
272 | if (lRet == ERROR_SUCCESS) |
---|
273 | { |
---|
274 | _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"), |
---|
275 | osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); |
---|
276 | _tcsncat(szVersion, wszTmp, cntMax); |
---|
277 | } |
---|
278 | else // Windows NT 4.0 prior to SP6a |
---|
279 | { |
---|
280 | _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), |
---|
281 | osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); |
---|
282 | _tcsncat(szVersion, wszTmp, cntMax); |
---|
283 | } |
---|
284 | ::RegCloseKey(hKey); |
---|
285 | } |
---|
286 | else // Windows NT 3.51 and earlier or Windows 2000 and later |
---|
287 | { |
---|
288 | if (!_tcslen(osvi.szCSDVersion)) |
---|
289 | _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"), |
---|
290 | osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); |
---|
291 | else |
---|
292 | _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), |
---|
293 | osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); |
---|
294 | _tcsncat(szVersion, wszTmp, cntMax); |
---|
295 | } |
---|
296 | break; |
---|
297 | default: |
---|
298 | _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), |
---|
299 | osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); |
---|
300 | _tcsncat(szVersion, wszTmp, cntMax); |
---|
301 | break; |
---|
302 | } |
---|
303 | |
---|
304 | return TRUE; |
---|
305 | } |
---|
306 | |
---|
307 | void WheatyExceptionReport::PrintSystemInfo() |
---|
308 | { |
---|
309 | SYSTEM_INFO SystemInfo; |
---|
310 | ::GetSystemInfo(&SystemInfo); |
---|
311 | |
---|
312 | MEMORYSTATUS MemoryStatus; |
---|
313 | MemoryStatus.dwLength = sizeof (MEMORYSTATUS); |
---|
314 | ::GlobalMemoryStatus(&MemoryStatus); |
---|
315 | TCHAR sString[1024]; |
---|
316 | _tprintf(_T("//=====================================================\r\n")); |
---|
317 | if (_GetProcessorName(sString, countof(sString))) |
---|
318 | _tprintf(_T("*** Hardware ***\r\nProcessor: %s\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"), |
---|
319 | sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400); |
---|
320 | else |
---|
321 | _tprintf(_T("*** Hardware ***\r\nProcessor: <unknown>\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"), |
---|
322 | SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400); |
---|
323 | |
---|
324 | if(_GetWindowsVersion(sString, countof(sString))) |
---|
325 | _tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString); |
---|
326 | else |
---|
327 | _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n")); |
---|
328 | } |
---|
329 | |
---|
330 | //=========================================================================== |
---|
331 | void WheatyExceptionReport::printTracesForAllThreads() |
---|
332 | { |
---|
333 | HANDLE hThreadSnap = INVALID_HANDLE_VALUE; |
---|
334 | THREADENTRY32 te32; |
---|
335 | |
---|
336 | DWORD dwOwnerPID = GetCurrentProcessId(); |
---|
337 | m_hProcess = GetCurrentProcess(); |
---|
338 | // Take a snapshot of all running threads |
---|
339 | hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); |
---|
340 | if( hThreadSnap == INVALID_HANDLE_VALUE ) |
---|
341 | return; |
---|
342 | |
---|
343 | // Fill in the size of the structure before using it. |
---|
344 | te32.dwSize = sizeof(THREADENTRY32 ); |
---|
345 | |
---|
346 | // Retrieve information about the first thread, |
---|
347 | // and exit if unsuccessful |
---|
348 | if( !Thread32First( hThreadSnap, &te32 ) ) |
---|
349 | { |
---|
350 | CloseHandle( hThreadSnap ); // Must clean up the |
---|
351 | // snapshot object! |
---|
352 | return; |
---|
353 | } |
---|
354 | |
---|
355 | // Now walk the thread list of the system, |
---|
356 | // and display information about each thread |
---|
357 | // associated with the specified process |
---|
358 | do |
---|
359 | { |
---|
360 | if( te32.th32OwnerProcessID == dwOwnerPID ) |
---|
361 | { |
---|
362 | CONTEXT context; |
---|
363 | context.ContextFlags = 0xffffffff; |
---|
364 | HANDLE threadHandle = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION,false, te32.th32ThreadID); |
---|
365 | if(threadHandle && GetThreadContext(threadHandle, &context)) |
---|
366 | { |
---|
367 | WriteStackDetails( &context, false, threadHandle ); |
---|
368 | } |
---|
369 | CloseHandle(threadHandle); |
---|
370 | } |
---|
371 | } while( Thread32Next(hThreadSnap, &te32 ) ); |
---|
372 | |
---|
373 | // Don't forget to clean up the snapshot object. |
---|
374 | CloseHandle( hThreadSnap ); |
---|
375 | } |
---|
376 | |
---|
377 | |
---|
378 | //=========================================================================== |
---|
379 | // Open the report file, and write the desired information to it. Called by |
---|
380 | // WheatyUnhandledExceptionFilter |
---|
381 | //=========================================================================== |
---|
382 | void WheatyExceptionReport::GenerateExceptionReport( |
---|
383 | PEXCEPTION_POINTERS pExceptionInfo ) |
---|
384 | { |
---|
385 | SYSTEMTIME systime; |
---|
386 | GetLocalTime(&systime); |
---|
387 | |
---|
388 | // Start out with a banner |
---|
389 | _tprintf(_T("Revision: %s\r\n"), SVN_REVISION); |
---|
390 | _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute); |
---|
391 | PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; |
---|
392 | |
---|
393 | PrintSystemInfo(); |
---|
394 | // First print information about the type of fault |
---|
395 | _tprintf(_T("\r\n//=====================================================\r\n")); |
---|
396 | _tprintf( _T("Exception code: %08X %s\r\n"), |
---|
397 | pExceptionRecord->ExceptionCode, |
---|
398 | GetExceptionString(pExceptionRecord->ExceptionCode) ); |
---|
399 | |
---|
400 | // Now print information about where the fault occured |
---|
401 | TCHAR szFaultingModule[MAX_PATH]; |
---|
402 | DWORD section; |
---|
403 | DWORD_PTR offset; |
---|
404 | GetLogicalAddress( pExceptionRecord->ExceptionAddress, |
---|
405 | szFaultingModule, |
---|
406 | sizeof( szFaultingModule ), |
---|
407 | section, offset ); |
---|
408 | |
---|
409 | #ifdef _M_IX86 |
---|
410 | _tprintf( _T("Fault address: %08X %02X:%08X %s\r\n"), |
---|
411 | pExceptionRecord->ExceptionAddress, |
---|
412 | section, offset, szFaultingModule ); |
---|
413 | #endif |
---|
414 | #ifdef _M_X64 |
---|
415 | _tprintf( _T("Fault address: %016I64X %02X:%016I64X %s\r\n"), |
---|
416 | pExceptionRecord->ExceptionAddress, |
---|
417 | section, offset, szFaultingModule ); |
---|
418 | #endif |
---|
419 | |
---|
420 | PCONTEXT pCtx = pExceptionInfo->ContextRecord; |
---|
421 | |
---|
422 | // Show the registers |
---|
423 | #ifdef _M_IX86 // X86 Only! |
---|
424 | _tprintf( _T("\r\nRegisters:\r\n") ); |
---|
425 | |
---|
426 | _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n") |
---|
427 | ,pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, |
---|
428 | pCtx->Esi, pCtx->Edi ); |
---|
429 | |
---|
430 | _tprintf( _T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip ); |
---|
431 | _tprintf( _T("SS:ESP:%04X:%08X EBP:%08X\r\n"), |
---|
432 | pCtx->SegSs, pCtx->Esp, pCtx->Ebp ); |
---|
433 | _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), |
---|
434 | pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs ); |
---|
435 | _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags ); |
---|
436 | #endif |
---|
437 | |
---|
438 | #ifdef _M_X64 |
---|
439 | _tprintf( _T("\r\nRegisters:\r\n") ); |
---|
440 | _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n") |
---|
441 | _T("R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n") |
---|
442 | ,pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx, |
---|
443 | pCtx->Rsi, pCtx->Rdi ,pCtx->R9,pCtx->R10,pCtx->R11,pCtx->R12,pCtx->R13,pCtx->R14,pCtx->R15); |
---|
444 | _tprintf( _T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip ); |
---|
445 | _tprintf( _T("SS:RSP:%04X:%016X RBP:%08X\r\n"), |
---|
446 | pCtx->SegSs, pCtx->Rsp, pCtx->Rbp ); |
---|
447 | _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), |
---|
448 | pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs ); |
---|
449 | _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags ); |
---|
450 | #endif |
---|
451 | |
---|
452 | SymSetOptions( SYMOPT_DEFERRED_LOADS ); |
---|
453 | |
---|
454 | // Initialize DbgHelp |
---|
455 | if ( !SymInitialize( GetCurrentProcess(), 0, TRUE ) ) |
---|
456 | { |
---|
457 | _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"), |
---|
458 | ErrorMessage(GetLastError())); |
---|
459 | } |
---|
460 | |
---|
461 | CONTEXT trashableContext = *pCtx; |
---|
462 | |
---|
463 | WriteStackDetails( &trashableContext, false, NULL ); |
---|
464 | printTracesForAllThreads(); |
---|
465 | |
---|
466 | // #ifdef _M_IX86 // X86 Only! |
---|
467 | |
---|
468 | _tprintf( _T("========================\r\n") ); |
---|
469 | _tprintf( _T("Local Variables And Parameters\r\n") ); |
---|
470 | |
---|
471 | trashableContext = *pCtx; |
---|
472 | WriteStackDetails( &trashableContext, true, NULL ); |
---|
473 | |
---|
474 | _tprintf( _T("========================\r\n") ); |
---|
475 | _tprintf( _T("Global Variables\r\n") ); |
---|
476 | |
---|
477 | SymEnumSymbols( GetCurrentProcess(), |
---|
478 | (DWORD64)GetModuleHandle(szFaultingModule), |
---|
479 | 0, EnumerateSymbolsCallback, 0 ); |
---|
480 | // #endif // X86 Only! |
---|
481 | |
---|
482 | SymCleanup( GetCurrentProcess() ); |
---|
483 | |
---|
484 | _tprintf( _T("\r\n") ); |
---|
485 | } |
---|
486 | |
---|
487 | //====================================================================== |
---|
488 | // Given an exception code, returns a pointer to a static string with a |
---|
489 | // description of the exception |
---|
490 | //====================================================================== |
---|
491 | LPTSTR WheatyExceptionReport::GetExceptionString( DWORD dwCode ) |
---|
492 | { |
---|
493 | #define EXCEPTION( x ) case EXCEPTION_##x: return _T(#x); |
---|
494 | |
---|
495 | switch ( dwCode ) |
---|
496 | { |
---|
497 | EXCEPTION( ACCESS_VIOLATION ) |
---|
498 | EXCEPTION( DATATYPE_MISALIGNMENT ) |
---|
499 | EXCEPTION( BREAKPOINT ) |
---|
500 | EXCEPTION( SINGLE_STEP ) |
---|
501 | EXCEPTION( ARRAY_BOUNDS_EXCEEDED ) |
---|
502 | EXCEPTION( FLT_DENORMAL_OPERAND ) |
---|
503 | EXCEPTION( FLT_DIVIDE_BY_ZERO ) |
---|
504 | EXCEPTION( FLT_INEXACT_RESULT ) |
---|
505 | EXCEPTION( FLT_INVALID_OPERATION ) |
---|
506 | EXCEPTION( FLT_OVERFLOW ) |
---|
507 | EXCEPTION( FLT_STACK_CHECK ) |
---|
508 | EXCEPTION( FLT_UNDERFLOW ) |
---|
509 | EXCEPTION( INT_DIVIDE_BY_ZERO ) |
---|
510 | EXCEPTION( INT_OVERFLOW ) |
---|
511 | EXCEPTION( PRIV_INSTRUCTION ) |
---|
512 | EXCEPTION( IN_PAGE_ERROR ) |
---|
513 | EXCEPTION( ILLEGAL_INSTRUCTION ) |
---|
514 | EXCEPTION( NONCONTINUABLE_EXCEPTION ) |
---|
515 | EXCEPTION( STACK_OVERFLOW ) |
---|
516 | EXCEPTION( INVALID_DISPOSITION ) |
---|
517 | EXCEPTION( GUARD_PAGE ) |
---|
518 | EXCEPTION( INVALID_HANDLE ) |
---|
519 | } |
---|
520 | |
---|
521 | // If not one of the "known" exceptions, try to get the string |
---|
522 | // from NTDLL.DLL's message table. |
---|
523 | |
---|
524 | static TCHAR szBuffer[512] = { 0 }; |
---|
525 | |
---|
526 | FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, |
---|
527 | GetModuleHandle( _T("NTDLL.DLL") ), |
---|
528 | dwCode, 0, szBuffer, sizeof( szBuffer ), 0 ); |
---|
529 | |
---|
530 | return szBuffer; |
---|
531 | } |
---|
532 | |
---|
533 | //============================================================================= |
---|
534 | // Given a linear address, locates the module, section, and offset containing |
---|
535 | // that address. |
---|
536 | // |
---|
537 | // Note: the szModule paramater buffer is an output buffer of length specified |
---|
538 | // by the len parameter (in characters!) |
---|
539 | //============================================================================= |
---|
540 | BOOL WheatyExceptionReport::GetLogicalAddress( |
---|
541 | PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset ) |
---|
542 | { |
---|
543 | MEMORY_BASIC_INFORMATION mbi; |
---|
544 | |
---|
545 | if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) ) |
---|
546 | return FALSE; |
---|
547 | |
---|
548 | DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase; |
---|
549 | |
---|
550 | if ( !GetModuleFileName( (HMODULE)hMod, szModule, len ) ) |
---|
551 | return FALSE; |
---|
552 | |
---|
553 | // Point to the DOS header in memory |
---|
554 | PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod; |
---|
555 | |
---|
556 | // From the DOS header, find the NT (PE) header |
---|
557 | PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew)); |
---|
558 | |
---|
559 | PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr ); |
---|
560 | |
---|
561 | DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address |
---|
562 | |
---|
563 | // Iterate through the section table, looking for the one that encompasses |
---|
564 | // the linear address. |
---|
565 | for ( unsigned i = 0; |
---|
566 | i < pNtHdr->FileHeader.NumberOfSections; |
---|
567 | i++, pSection++ ) |
---|
568 | { |
---|
569 | DWORD_PTR sectionStart = pSection->VirtualAddress; |
---|
570 | DWORD_PTR sectionEnd = sectionStart |
---|
571 | + DWORD_PTR(max(pSection->SizeOfRawData, pSection->Misc.VirtualSize)); |
---|
572 | |
---|
573 | // Is the address in this section??? |
---|
574 | if ( (rva >= sectionStart) && (rva <= sectionEnd) ) |
---|
575 | { |
---|
576 | // Yes, address is in the section. Calculate section and offset, |
---|
577 | // and store in the "section" & "offset" params, which were |
---|
578 | // passed by reference. |
---|
579 | section = i+1; |
---|
580 | offset = rva - sectionStart; |
---|
581 | return TRUE; |
---|
582 | } |
---|
583 | } |
---|
584 | |
---|
585 | return FALSE; // Should never get here! |
---|
586 | } |
---|
587 | |
---|
588 | // It contains SYMBOL_INFO structure plus additional |
---|
589 | // space for the name of the symbol |
---|
590 | struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE |
---|
591 | { |
---|
592 | CSymbolInfoPackage() |
---|
593 | { |
---|
594 | si.SizeOfStruct = sizeof(SYMBOL_INFO); |
---|
595 | si.MaxNameLen = sizeof(name); |
---|
596 | } |
---|
597 | }; |
---|
598 | |
---|
599 | //============================================================ |
---|
600 | // Walks the stack, and writes the results to the report file |
---|
601 | //============================================================ |
---|
602 | void WheatyExceptionReport::WriteStackDetails( |
---|
603 | PCONTEXT pContext, |
---|
604 | bool bWriteVariables, HANDLE pThreadHandle) // true if local/params should be output |
---|
605 | { |
---|
606 | _tprintf( _T("\r\nCall stack:\r\n") ); |
---|
607 | |
---|
608 | _tprintf( _T("Address Frame Function SourceFile\r\n") ); |
---|
609 | |
---|
610 | DWORD dwMachineType = 0; |
---|
611 | // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag |
---|
612 | |
---|
613 | STACKFRAME64 sf; |
---|
614 | memset( &sf, 0, sizeof(sf) ); |
---|
615 | |
---|
616 | #ifdef _M_IX86 |
---|
617 | // Initialize the STACKFRAME structure for the first call. This is only |
---|
618 | // necessary for Intel CPUs, and isn't mentioned in the documentation. |
---|
619 | sf.AddrPC.Offset = pContext->Eip; |
---|
620 | sf.AddrPC.Mode = AddrModeFlat; |
---|
621 | sf.AddrStack.Offset = pContext->Esp; |
---|
622 | sf.AddrStack.Mode = AddrModeFlat; |
---|
623 | sf.AddrFrame.Offset = pContext->Ebp; |
---|
624 | sf.AddrFrame.Mode = AddrModeFlat; |
---|
625 | |
---|
626 | dwMachineType = IMAGE_FILE_MACHINE_I386; |
---|
627 | #endif |
---|
628 | |
---|
629 | #ifdef _M_X64 |
---|
630 | sf.AddrPC.Offset = pContext->Rip; |
---|
631 | sf.AddrPC.Mode = AddrModeFlat; |
---|
632 | sf.AddrStack.Offset = pContext->Rsp; |
---|
633 | sf.AddrStack.Mode = AddrModeFlat; |
---|
634 | sf.AddrFrame.Offset = pContext->Rbp; |
---|
635 | sf.AddrFrame.Mode = AddrModeFlat; |
---|
636 | dwMachineType = IMAGE_FILE_MACHINE_AMD64; |
---|
637 | #endif |
---|
638 | |
---|
639 | while ( 1 ) |
---|
640 | { |
---|
641 | // Get the next stack frame |
---|
642 | if ( ! StackWalk64( dwMachineType, |
---|
643 | m_hProcess, |
---|
644 | pThreadHandle != NULL ? pThreadHandle : GetCurrentThread(), |
---|
645 | &sf, |
---|
646 | pContext, |
---|
647 | 0, |
---|
648 | SymFunctionTableAccess64, |
---|
649 | SymGetModuleBase64, |
---|
650 | 0 ) ) |
---|
651 | break; |
---|
652 | if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure |
---|
653 | break; // the frame is OK. Bail if not. |
---|
654 | #ifdef _M_IX86 |
---|
655 | _tprintf( _T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset ); |
---|
656 | #endif |
---|
657 | #ifdef _M_X64 |
---|
658 | _tprintf( _T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset ); |
---|
659 | #endif |
---|
660 | |
---|
661 | DWORD64 symDisplacement = 0; // Displacement of the input address, |
---|
662 | // relative to the start of the symbol |
---|
663 | |
---|
664 | // Get the name of the function for this stack frame entry |
---|
665 | CSymbolInfoPackage sip; |
---|
666 | if ( SymFromAddr( |
---|
667 | m_hProcess, // Process handle of the current process |
---|
668 | sf.AddrPC.Offset, // Symbol address |
---|
669 | &symDisplacement, // Address of the variable that will receive the displacement |
---|
670 | &sip.si // Address of the SYMBOL_INFO structure (inside "sip" object) |
---|
671 | )) |
---|
672 | { |
---|
673 | _tprintf( _T("%hs+%I64X"), sip.si.Name, symDisplacement ); |
---|
674 | |
---|
675 | } |
---|
676 | else // No symbol found. Print out the logical address instead. |
---|
677 | { |
---|
678 | TCHAR szModule[MAX_PATH] = _T(""); |
---|
679 | DWORD section = 0; |
---|
680 | DWORD_PTR offset = 0; |
---|
681 | |
---|
682 | GetLogicalAddress( (PVOID)sf.AddrPC.Offset, |
---|
683 | szModule, sizeof(szModule), section, offset ); |
---|
684 | #ifdef _M_IX86 |
---|
685 | _tprintf( _T("%04X:%08X %s"), section, offset, szModule ); |
---|
686 | #endif |
---|
687 | #ifdef _M_X64 |
---|
688 | _tprintf( _T("%04X:%016I64X %s"), section, offset, szModule ); |
---|
689 | #endif |
---|
690 | } |
---|
691 | |
---|
692 | // Get the source line for this stack frame entry |
---|
693 | IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE) }; |
---|
694 | DWORD dwLineDisplacement; |
---|
695 | if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset, |
---|
696 | &dwLineDisplacement, &lineInfo ) ) |
---|
697 | { |
---|
698 | _tprintf(_T(" %s line %u"),lineInfo.FileName,lineInfo.LineNumber); |
---|
699 | } |
---|
700 | |
---|
701 | _tprintf( _T("\r\n") ); |
---|
702 | |
---|
703 | // Write out the variables, if desired |
---|
704 | if ( bWriteVariables ) |
---|
705 | { |
---|
706 | // Use SymSetContext to get just the locals/params for this frame |
---|
707 | IMAGEHLP_STACK_FRAME imagehlpStackFrame; |
---|
708 | imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset; |
---|
709 | SymSetContext( m_hProcess, &imagehlpStackFrame, 0 ); |
---|
710 | |
---|
711 | // Enumerate the locals/parameters |
---|
712 | SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf ); |
---|
713 | |
---|
714 | _tprintf( _T("\r\n") ); |
---|
715 | } |
---|
716 | } |
---|
717 | |
---|
718 | } |
---|
719 | |
---|
720 | ////////////////////////////////////////////////////////////////////////////// |
---|
721 | // The function invoked by SymEnumSymbols |
---|
722 | ////////////////////////////////////////////////////////////////////////////// |
---|
723 | |
---|
724 | BOOL CALLBACK |
---|
725 | WheatyExceptionReport::EnumerateSymbolsCallback( |
---|
726 | PSYMBOL_INFO pSymInfo, |
---|
727 | ULONG SymbolSize, |
---|
728 | PVOID UserContext ) |
---|
729 | { |
---|
730 | |
---|
731 | char szBuffer[2048]; |
---|
732 | |
---|
733 | __try |
---|
734 | { |
---|
735 | if ( FormatSymbolValue( pSymInfo, (STACKFRAME*)UserContext, |
---|
736 | szBuffer, sizeof(szBuffer) ) ) |
---|
737 | _tprintf( _T("\t%s\r\n"), szBuffer ); |
---|
738 | } |
---|
739 | __except( 1 ) |
---|
740 | { |
---|
741 | _tprintf( _T("punting on symbol %s\r\n"), pSymInfo->Name ); |
---|
742 | } |
---|
743 | |
---|
744 | return TRUE; |
---|
745 | } |
---|
746 | |
---|
747 | ////////////////////////////////////////////////////////////////////////////// |
---|
748 | // Given a SYMBOL_INFO representing a particular variable, displays its |
---|
749 | // contents. If it's a user defined type, display the members and their |
---|
750 | // values. |
---|
751 | ////////////////////////////////////////////////////////////////////////////// |
---|
752 | bool WheatyExceptionReport::FormatSymbolValue( |
---|
753 | PSYMBOL_INFO pSym, |
---|
754 | STACKFRAME * sf, |
---|
755 | char * pszBuffer, |
---|
756 | unsigned cbBuffer ) |
---|
757 | { |
---|
758 | char * pszCurrBuffer = pszBuffer; |
---|
759 | |
---|
760 | // Indicate if the variable is a local or parameter |
---|
761 | if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER ) |
---|
762 | pszCurrBuffer += sprintf( pszCurrBuffer, "Parameter " ); |
---|
763 | else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL ) |
---|
764 | pszCurrBuffer += sprintf( pszCurrBuffer, "Local " ); |
---|
765 | |
---|
766 | // If it's a function, don't do anything. |
---|
767 | if ( pSym->Tag == 5 ) // SymTagFunction from CVCONST.H from the DIA SDK |
---|
768 | return false; |
---|
769 | |
---|
770 | DWORD_PTR pVariable = 0; // Will point to the variable's data in memory |
---|
771 | |
---|
772 | if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE ) |
---|
773 | { |
---|
774 | // if ( pSym->Register == 8 ) // EBP is the value 8 (in DBGHELP 5.1) |
---|
775 | { // This may change!!! |
---|
776 | pVariable = sf->AddrFrame.Offset; |
---|
777 | pVariable += (DWORD_PTR)pSym->Address; |
---|
778 | } |
---|
779 | // else |
---|
780 | // return false; |
---|
781 | } |
---|
782 | else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER ) |
---|
783 | { |
---|
784 | return false; // Don't try to report register variable |
---|
785 | } |
---|
786 | else |
---|
787 | { |
---|
788 | pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable |
---|
789 | } |
---|
790 | |
---|
791 | // Determine if the variable is a user defined type (UDT). IF so, bHandled |
---|
792 | // will return true. |
---|
793 | bool bHandled; |
---|
794 | pszCurrBuffer = DumpTypeIndex(pszCurrBuffer,pSym->ModBase, pSym->TypeIndex, |
---|
795 | 0, pVariable, bHandled, pSym->Name ); |
---|
796 | |
---|
797 | if ( !bHandled ) |
---|
798 | { |
---|
799 | // The symbol wasn't a UDT, so do basic, stupid formatting of the |
---|
800 | // variable. Based on the size, we're assuming it's a char, WORD, or |
---|
801 | // DWORD. |
---|
802 | BasicType basicType = GetBasicType( pSym->TypeIndex, pSym->ModBase ); |
---|
803 | pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]); |
---|
804 | |
---|
805 | // Emit the variable name |
---|
806 | pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", pSym->Name ); |
---|
807 | |
---|
808 | pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, pSym->Size, |
---|
809 | (PVOID)pVariable ); |
---|
810 | } |
---|
811 | |
---|
812 | return true; |
---|
813 | } |
---|
814 | |
---|
815 | ////////////////////////////////////////////////////////////////////////////// |
---|
816 | // If it's a user defined type (UDT), recurse through its members until we're |
---|
817 | // at fundamental types. When he hit fundamental types, return |
---|
818 | // bHandled = false, so that FormatSymbolValue() will format them. |
---|
819 | ////////////////////////////////////////////////////////////////////////////// |
---|
820 | char * WheatyExceptionReport::DumpTypeIndex( |
---|
821 | char * pszCurrBuffer, |
---|
822 | DWORD64 modBase, |
---|
823 | DWORD dwTypeIndex, |
---|
824 | unsigned nestingLevel, |
---|
825 | DWORD_PTR offset, |
---|
826 | bool & bHandled, |
---|
827 | char* Name) |
---|
828 | { |
---|
829 | bHandled = false; |
---|
830 | |
---|
831 | // Get the name of the symbol. This will either be a Type name (if a UDT), |
---|
832 | // or the structure member name. |
---|
833 | WCHAR * pwszTypeName; |
---|
834 | if ( SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME, |
---|
835 | &pwszTypeName ) ) |
---|
836 | { |
---|
837 | pszCurrBuffer += sprintf( pszCurrBuffer, " %ls", pwszTypeName ); |
---|
838 | LocalFree( pwszTypeName ); |
---|
839 | } |
---|
840 | |
---|
841 | // Determine how many children this type has. |
---|
842 | DWORD dwChildrenCount = 0; |
---|
843 | SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, |
---|
844 | &dwChildrenCount ); |
---|
845 | |
---|
846 | if ( !dwChildrenCount ) // If no children, we're done |
---|
847 | return pszCurrBuffer; |
---|
848 | |
---|
849 | // Prepare to get an array of "TypeIds", representing each of the children. |
---|
850 | // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a |
---|
851 | // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this. |
---|
852 | struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS |
---|
853 | { |
---|
854 | ULONG MoreChildIds[1024]; |
---|
855 | FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);} |
---|
856 | } children; |
---|
857 | |
---|
858 | children.Count = dwChildrenCount; |
---|
859 | children.Start= 0; |
---|
860 | |
---|
861 | // Get the array of TypeIds, one for each child type |
---|
862 | if ( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN, |
---|
863 | &children ) ) |
---|
864 | { |
---|
865 | return pszCurrBuffer; |
---|
866 | } |
---|
867 | |
---|
868 | // Append a line feed |
---|
869 | pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" ); |
---|
870 | |
---|
871 | // Iterate through each of the children |
---|
872 | for ( unsigned i = 0; i < dwChildrenCount; i++ ) |
---|
873 | { |
---|
874 | // Add appropriate indentation level (since this routine is recursive) |
---|
875 | for ( unsigned j = 0; j <= nestingLevel+1; j++ ) |
---|
876 | pszCurrBuffer += sprintf( pszCurrBuffer, "\t" ); |
---|
877 | |
---|
878 | // Recurse for each of the child types |
---|
879 | bool bHandled2; |
---|
880 | BasicType basicType = GetBasicType(children.ChildId[i], modBase ); |
---|
881 | pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]); |
---|
882 | |
---|
883 | pszCurrBuffer = DumpTypeIndex( pszCurrBuffer, modBase, |
---|
884 | children.ChildId[i], nestingLevel+1, |
---|
885 | offset, bHandled2, ""/*Name */); |
---|
886 | |
---|
887 | // If the child wasn't a UDT, format it appropriately |
---|
888 | if ( !bHandled2 ) |
---|
889 | { |
---|
890 | // Get the offset of the child member, relative to its parent |
---|
891 | DWORD dwMemberOffset; |
---|
892 | SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i], |
---|
893 | TI_GET_OFFSET, &dwMemberOffset ); |
---|
894 | |
---|
895 | // Get the real "TypeId" of the child. We need this for the |
---|
896 | // SymGetTypeInfo( TI_GET_TYPEID ) call below. |
---|
897 | DWORD typeId; |
---|
898 | SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i], |
---|
899 | TI_GET_TYPEID, &typeId ); |
---|
900 | |
---|
901 | // Get the size of the child member |
---|
902 | ULONG64 length; |
---|
903 | SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH,&length); |
---|
904 | |
---|
905 | // Calculate the address of the member |
---|
906 | DWORD_PTR dwFinalOffset = offset + dwMemberOffset; |
---|
907 | |
---|
908 | // BasicType basicType = GetBasicType(children.ChildId[i], modBase ); |
---|
909 | // |
---|
910 | // pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]); |
---|
911 | // |
---|
912 | // Emit the variable name |
---|
913 | // pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", Name ); |
---|
914 | |
---|
915 | pszCurrBuffer = FormatOutputValue( pszCurrBuffer, basicType, |
---|
916 | length, (PVOID)dwFinalOffset ); |
---|
917 | |
---|
918 | pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" ); |
---|
919 | } |
---|
920 | } |
---|
921 | |
---|
922 | bHandled = true; |
---|
923 | return pszCurrBuffer; |
---|
924 | } |
---|
925 | |
---|
926 | char * WheatyExceptionReport::FormatOutputValue( char * pszCurrBuffer, |
---|
927 | BasicType basicType, |
---|
928 | DWORD64 length, |
---|
929 | PVOID pAddress ) |
---|
930 | { |
---|
931 | // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!) |
---|
932 | if ( length == 1 ) |
---|
933 | pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PBYTE)pAddress ); |
---|
934 | else if ( length == 2 ) |
---|
935 | pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PWORD)pAddress ); |
---|
936 | else if ( length == 4 ) |
---|
937 | { |
---|
938 | if ( basicType == btFloat ) |
---|
939 | { |
---|
940 | pszCurrBuffer += sprintf(pszCurrBuffer," = %f", *(PFLOAT)pAddress); |
---|
941 | } |
---|
942 | else if ( basicType == btChar ) |
---|
943 | { |
---|
944 | if ( !IsBadStringPtr( *(PSTR*)pAddress, 32) ) |
---|
945 | { |
---|
946 | pszCurrBuffer += sprintf( pszCurrBuffer, " = \"%.31s\"", |
---|
947 | *(PDWORD)pAddress ); |
---|
948 | } |
---|
949 | else |
---|
950 | pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", |
---|
951 | *(PDWORD)pAddress ); |
---|
952 | } |
---|
953 | else |
---|
954 | pszCurrBuffer += sprintf(pszCurrBuffer," = %X", *(PDWORD)pAddress); |
---|
955 | } |
---|
956 | else if ( length == 8 ) |
---|
957 | { |
---|
958 | if ( basicType == btFloat ) |
---|
959 | { |
---|
960 | pszCurrBuffer += sprintf( pszCurrBuffer, " = %lf", |
---|
961 | *(double *)pAddress ); |
---|
962 | } |
---|
963 | else |
---|
964 | pszCurrBuffer += sprintf( pszCurrBuffer, " = %I64X", |
---|
965 | *(DWORD64*)pAddress ); |
---|
966 | } |
---|
967 | |
---|
968 | return pszCurrBuffer; |
---|
969 | } |
---|
970 | |
---|
971 | BasicType |
---|
972 | WheatyExceptionReport::GetBasicType( DWORD typeIndex, DWORD64 modBase ) |
---|
973 | { |
---|
974 | BasicType basicType; |
---|
975 | if ( SymGetTypeInfo( m_hProcess, modBase, typeIndex, |
---|
976 | TI_GET_BASETYPE, &basicType ) ) |
---|
977 | { |
---|
978 | return basicType; |
---|
979 | } |
---|
980 | |
---|
981 | // Get the real "TypeId" of the child. We need this for the |
---|
982 | // SymGetTypeInfo( TI_GET_TYPEID ) call below. |
---|
983 | DWORD typeId; |
---|
984 | if (SymGetTypeInfo(m_hProcess,modBase, typeIndex, TI_GET_TYPEID, &typeId)) |
---|
985 | { |
---|
986 | if ( SymGetTypeInfo( m_hProcess, modBase, typeId, TI_GET_BASETYPE, |
---|
987 | &basicType ) ) |
---|
988 | { |
---|
989 | return basicType; |
---|
990 | } |
---|
991 | } |
---|
992 | |
---|
993 | return btNoType; |
---|
994 | } |
---|
995 | |
---|
996 | //============================================================================ |
---|
997 | // Helper function that writes to the report file, and allows the user to use |
---|
998 | // printf style formating |
---|
999 | //============================================================================ |
---|
1000 | int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...) |
---|
1001 | { |
---|
1002 | TCHAR szBuff[1024]; |
---|
1003 | int retValue; |
---|
1004 | DWORD cbWritten; |
---|
1005 | va_list argptr; |
---|
1006 | |
---|
1007 | va_start( argptr, format ); |
---|
1008 | retValue = vsprintf( szBuff, format, argptr ); |
---|
1009 | va_end( argptr ); |
---|
1010 | |
---|
1011 | WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0 ); |
---|
1012 | |
---|
1013 | return retValue; |
---|
1014 | } |
---|