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