1 //==========================================
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)
16 #include "WheatyExceptionReport.h"
18 #include "revision_nr.h"
19 #define CrashFolder _T("Crashs")
20 //#pragma comment(linker, "/defaultlib:dbghelp.lib")
22 inline LPTSTR
ErrorMessage(DWORD dw
)
26 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
27 FORMAT_MESSAGE_FROM_SYSTEM
,
30 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
33 return (LPTSTR
)lpMsgBuf
;
36 //============================== Global Variables =============================
39 // Declare the static variables of the WheatyExceptionReport class
41 TCHAR
WheatyExceptionReport::m_szLogFileName
[MAX_PATH
];
42 LPTOP_LEVEL_EXCEPTION_FILTER
WheatyExceptionReport::m_previousFilter
;
43 HANDLE
WheatyExceptionReport::m_hReportFile
;
44 HANDLE
WheatyExceptionReport::m_hProcess
;
46 // Declare global instance of class
47 WheatyExceptionReport g_WheatyExceptionReport
;
49 //============================== Class Methods =============================
51 WheatyExceptionReport::WheatyExceptionReport( ) // Constructor
53 // Install the unhandled exception filter function
54 m_previousFilter
= SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter
);
55 m_hProcess
= GetCurrentProcess();
61 WheatyExceptionReport::~WheatyExceptionReport( )
64 SetUnhandledExceptionFilter( m_previousFilter
);
67 //===========================================================
68 // Entry point where control comes on an unhandled exception
69 //===========================================================
70 LONG WINAPI
WheatyExceptionReport::WheatyUnhandledExceptionFilter(
71 PEXCEPTION_POINTERS pExceptionInfo
)
73 TCHAR module_folder_name
[MAX_PATH
];
74 GetModuleFileName( 0, module_folder_name
, MAX_PATH
);
75 TCHAR
* pos
= _tcsrchr(module_folder_name
, '\\');
81 TCHAR crash_folder_path
[MAX_PATH
];
82 sprintf(crash_folder_path
, "%s\\%s", module_folder_name
, CrashFolder
);
83 if(!CreateDirectory(crash_folder_path
, NULL
))
85 if(GetLastError() != ERROR_ALREADY_EXISTS
)
90 GetLocalTime(&systime
);
91 sprintf(m_szLogFileName
, "%s\\%s_[%u-%u_%u-%u-%u].txt",
92 crash_folder_path
, pos
, systime
.wDay
, systime
.wMonth
, systime
.wHour
, systime
.wMinute
, systime
.wSecond
95 m_hReportFile
= CreateFile( m_szLogFileName
,
100 FILE_FLAG_WRITE_THROUGH
,
105 SetFilePointer( m_hReportFile
, 0, 0, FILE_END
);
107 GenerateExceptionReport( pExceptionInfo
);
109 CloseHandle( m_hReportFile
);
113 if ( m_previousFilter
)
114 return m_previousFilter( pExceptionInfo
);
116 return EXCEPTION_EXECUTE_HANDLER
/*EXCEPTION_CONTINUE_SEARCH*/;
119 BOOL
WheatyExceptionReport::_GetProcessorName(TCHAR
* sProcessorName
, DWORD maxcount
)
126 lRet
= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
127 0, KEY_QUERY_VALUE
, &hKey
);
128 if (lRet
!= ERROR_SUCCESS
)
131 DWORD cntBytes
= sizeof(szTmp
);
132 lRet
= ::RegQueryValueEx(hKey
, _T("ProcessorNameString"), NULL
, NULL
,
133 (LPBYTE
)szTmp
, &cntBytes
);
134 if (lRet
!= ERROR_SUCCESS
)
137 sProcessorName
[0] = '\0';
140 while (iswspace(*psz
))
142 _tcsncpy(sProcessorName
, psz
, maxcount
);
146 BOOL
WheatyExceptionReport::_GetWindowsVersion(TCHAR
* szVersion
, DWORD cntMax
)
148 // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
149 // If that fails, try using the OSVERSIONINFO structure.
150 OSVERSIONINFOEX osvi
= { 0 };
151 osvi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOEX
);
152 BOOL bOsVersionInfoEx
;
153 bOsVersionInfoEx
= ::GetVersionEx((LPOSVERSIONINFO
)(&osvi
));
154 if (!bOsVersionInfoEx
)
156 osvi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
157 if (!::GetVersionEx((OSVERSIONINFO
*)&osvi
))
160 *szVersion
= _T('\0');
162 switch (osvi
.dwPlatformId
)
164 // Windows NT product family.
165 case VER_PLATFORM_WIN32_NT
:
166 // Test for the specific product family.
167 if (osvi
.dwMajorVersion
== 6)
168 _tcsncat(szVersion
, _T("Windows Vista or Windows Server 2008 "), cntMax
);
169 if (osvi
.dwMajorVersion
== 5 && osvi
.dwMinorVersion
== 2)
170 _tcsncat(szVersion
, _T("Microsoft Windows Server 2003 "), cntMax
);
171 if (osvi
.dwMajorVersion
== 5 && osvi
.dwMinorVersion
== 1)
172 _tcsncat(szVersion
, _T("Microsoft Windows XP "), cntMax
);
173 if (osvi
.dwMajorVersion
== 5 && osvi
.dwMinorVersion
== 0)
174 _tcsncat(szVersion
, _T("Microsoft Windows 2000 "), cntMax
);
175 if (osvi
.dwMajorVersion
<= 4 )
176 _tcsncat(szVersion
, _T("Microsoft Windows NT "), cntMax
);
178 // Test for specific product on Windows NT 4.0 SP6 and later.
179 if (bOsVersionInfoEx
)
181 // Test for the workstation type.
183 if (osvi
.wReserved
[1] == VER_NT_WORKSTATION
)
185 if (osvi
.wProductType
== VER_NT_WORKSTATION
)
186 #endif // WINVER < 0x0500
188 if (osvi
.dwMajorVersion
== 4)
189 _tcsncat(szVersion
, _T("Workstation 4.0 "), cntMax
);
191 else if (osvi
.wReserved
[0] & VER_SUITE_PERSONAL
)
193 else if (osvi
.wSuiteMask
& VER_SUITE_PERSONAL
)
194 #endif // WINVER < 0x0500
195 _tcsncat(szVersion
, _T("Home Edition "), cntMax
);
197 else if (osvi
.wReserved
[0] & VER_SUITE_EMBEDDEDNT
)
199 else if (osvi
.wSuiteMask
& VER_SUITE_EMBEDDEDNT
)
200 #endif // WINVER < 0x0500
201 _tcsncat(szVersion
, _T("Embedded "), cntMax
);
203 _tcsncat(szVersion
, _T("Professional "), cntMax
);
205 // Test for the server type.
207 else if (osvi
.wReserved
[1] == VER_NT_SERVER
)
209 else if (osvi
.wProductType
== VER_NT_SERVER
)
210 #endif // WINVER < 0x0500
212 if (osvi
.dwMajorVersion
== 5 && osvi
.dwMinorVersion
== 2)
215 if (osvi
.wReserved
[0] & VER_SUITE_DATACENTER
)
217 if (osvi
.wSuiteMask
& VER_SUITE_DATACENTER
)
218 #endif // WINVER < 0x0500
219 _tcsncat(szVersion
, _T("Datacenter Edition "), cntMax
);
221 else if (osvi
.wReserved
[0] & VER_SUITE_ENTERPRISE
)
223 else if (osvi
.wSuiteMask
& VER_SUITE_ENTERPRISE
)
224 #endif // WINVER < 0x0500
225 _tcsncat(szVersion
, _T("Enterprise Edition "), cntMax
);
227 else if (osvi
.wReserved
[0] == VER_SUITE_BLADE
)
229 else if (osvi
.wSuiteMask
== VER_SUITE_BLADE
)
230 #endif // WINVER < 0x0500
231 _tcsncat(szVersion
, _T("Web Edition "), cntMax
);
233 _tcsncat(szVersion
, _T("Standard Edition "), cntMax
);
235 else if (osvi
.dwMajorVersion
== 5 && osvi
.dwMinorVersion
== 0)
238 if (osvi
.wReserved
[0] & VER_SUITE_DATACENTER
)
240 if (osvi
.wSuiteMask
& VER_SUITE_DATACENTER
)
241 #endif // WINVER < 0x0500
242 _tcsncat(szVersion
, _T("Datacenter Server "), cntMax
);
244 else if (osvi
.wReserved
[0] & VER_SUITE_ENTERPRISE
)
246 else if (osvi
.wSuiteMask
& VER_SUITE_ENTERPRISE
)
247 #endif // WINVER < 0x0500
248 _tcsncat(szVersion
, _T("Advanced Server "), cntMax
);
250 _tcsncat(szVersion
, _T("Server "), cntMax
);
252 else // Windows NT 4.0
255 if (osvi
.wReserved
[0] & VER_SUITE_ENTERPRISE
)
257 if (osvi
.wSuiteMask
& VER_SUITE_ENTERPRISE
)
258 #endif // WINVER < 0x0500
259 _tcsncat(szVersion
, _T("Server 4.0, Enterprise Edition "), cntMax
);
261 _tcsncat(szVersion
, _T("Server 4.0 "), cntMax
);
265 // Display service pack (if any) and build number.
266 if (osvi
.dwMajorVersion
== 4 && _tcsicmp(osvi
.szCSDVersion
, _T("Service Pack 6")) == 0)
271 // Test for SP6 versus SP6a.
272 lRet
= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE
, &hKey
);
273 if (lRet
== ERROR_SUCCESS
)
275 _stprintf(wszTmp
, _T("Service Pack 6a (Version %d.%d, Build %d)"),
276 osvi
.dwMajorVersion
, osvi
.dwMinorVersion
, osvi
.dwBuildNumber
& 0xFFFF);
277 _tcsncat(szVersion
, wszTmp
, cntMax
);
279 else // Windows NT 4.0 prior to SP6a
281 _stprintf(wszTmp
, _T("%s (Version %d.%d, Build %d)"),
282 osvi
.szCSDVersion
, osvi
.dwMajorVersion
, osvi
.dwMinorVersion
, osvi
.dwBuildNumber
& 0xFFFF);
283 _tcsncat(szVersion
, wszTmp
, cntMax
);
287 else // Windows NT 3.51 and earlier or Windows 2000 and later
289 if (!_tcslen(osvi
.szCSDVersion
))
290 _stprintf(wszTmp
, _T("(Version %d.%d, Build %d)"),
291 osvi
.dwMajorVersion
, osvi
.dwMinorVersion
, osvi
.dwBuildNumber
& 0xFFFF);
293 _stprintf(wszTmp
, _T("%s (Version %d.%d, Build %d)"),
294 osvi
.szCSDVersion
, osvi
.dwMajorVersion
, osvi
.dwMinorVersion
, osvi
.dwBuildNumber
& 0xFFFF);
295 _tcsncat(szVersion
, wszTmp
, cntMax
);
299 _stprintf(wszTmp
, _T("%s (Version %d.%d, Build %d)"),
300 osvi
.szCSDVersion
, osvi
.dwMajorVersion
, osvi
.dwMinorVersion
, osvi
.dwBuildNumber
& 0xFFFF);
301 _tcsncat(szVersion
, wszTmp
, cntMax
);
308 void WheatyExceptionReport::PrintSystemInfo()
310 SYSTEM_INFO SystemInfo
;
311 ::GetSystemInfo(&SystemInfo
);
313 MEMORYSTATUS MemoryStatus
;
314 MemoryStatus
.dwLength
= sizeof (MEMORYSTATUS
);
315 ::GlobalMemoryStatus(&MemoryStatus
);
317 _tprintf(_T("//=====================================================\r\n"));
318 if (_GetProcessorName(sString
, countof(sString
)))
319 _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"),
320 sString
, SystemInfo
.dwNumberOfProcessors
, MemoryStatus
.dwTotalPhys
/0x400, MemoryStatus
.dwAvailPhys
/0x400, MemoryStatus
.dwTotalPageFile
/0x400);
322 _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"),
323 SystemInfo
.dwNumberOfProcessors
, MemoryStatus
.dwTotalPhys
/0x400, MemoryStatus
.dwAvailPhys
/0x400, MemoryStatus
.dwTotalPageFile
/0x400);
325 if(_GetWindowsVersion(sString
, countof(sString
)))
326 _tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString
);
328 _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n"));
331 //===========================================================================
332 void WheatyExceptionReport::printTracesForAllThreads()
334 HANDLE hThreadSnap
= INVALID_HANDLE_VALUE
;
337 DWORD dwOwnerPID
= GetCurrentProcessId();
338 m_hProcess
= GetCurrentProcess();
339 // Take a snapshot of all running threads
340 hThreadSnap
= CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD
, 0 );
341 if( hThreadSnap
== INVALID_HANDLE_VALUE
)
344 // Fill in the size of the structure before using it.
345 te32
.dwSize
= sizeof(THREADENTRY32
);
347 // Retrieve information about the first thread,
348 // and exit if unsuccessful
349 if( !Thread32First( hThreadSnap
, &te32
) )
351 CloseHandle( hThreadSnap
); // Must clean up the
356 // Now walk the thread list of the system,
357 // and display information about each thread
358 // associated with the specified process
361 if( te32
.th32OwnerProcessID
== dwOwnerPID
)
364 context
.ContextFlags
= 0xffffffff;
365 HANDLE threadHandle
= OpenThread(THREAD_GET_CONTEXT
| THREAD_QUERY_INFORMATION
,false, te32
.th32ThreadID
);
366 if(threadHandle
&& GetThreadContext(threadHandle
, &context
))
368 WriteStackDetails( &context
, false, threadHandle
);
370 CloseHandle(threadHandle
);
372 } while( Thread32Next(hThreadSnap
, &te32
) );
374 // Don't forget to clean up the snapshot object.
375 CloseHandle( hThreadSnap
);
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
)
386 GetLocalTime(&systime
);
388 // Start out with a banner
389 _tprintf(_T("Revision: %s %s %s %s\r\n"), REVISION_DATE
, REVISION_TIME
, REVISION_NR
, REVISION_ID
);
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
;
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
) );
400 // Now print information about where the fault occured
401 TCHAR szFaultingModule
[MAX_PATH
];
404 GetLogicalAddress( pExceptionRecord
->ExceptionAddress
,
406 sizeof( szFaultingModule
),
410 _tprintf( _T("Fault address: %08X %02X:%08X %s\r\n"),
411 pExceptionRecord
->ExceptionAddress
,
412 section
, offset
, szFaultingModule
);
415 _tprintf( _T("Fault address: %016I64X %02X:%016I64X %s\r\n"),
416 pExceptionRecord
->ExceptionAddress
,
417 section
, offset
, szFaultingModule
);
420 PCONTEXT pCtx
= pExceptionInfo
->ContextRecord
;
422 // Show the registers
423 #ifdef _M_IX86 // X86 Only!
424 _tprintf( _T("\r\nRegisters:\r\n") );
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
);
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
);
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
);
452 SymSetOptions( SYMOPT_DEFERRED_LOADS
);
454 // Initialize DbgHelp
455 if ( !SymInitialize( GetCurrentProcess(), 0, TRUE
) )
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()));
461 CONTEXT trashableContext
= *pCtx
;
463 WriteStackDetails( &trashableContext
, false, NULL
);
464 printTracesForAllThreads();
466 // #ifdef _M_IX86 // X86 Only!
468 _tprintf( _T("========================\r\n") );
469 _tprintf( _T("Local Variables And Parameters\r\n") );
471 trashableContext
= *pCtx
;
472 WriteStackDetails( &trashableContext
, true, NULL
);
474 _tprintf( _T("========================\r\n") );
475 _tprintf( _T("Global Variables\r\n") );
477 SymEnumSymbols( GetCurrentProcess(),
478 (DWORD64
)GetModuleHandle(szFaultingModule
),
479 0, EnumerateSymbolsCallback
, 0 );
480 // #endif // X86 Only!
482 SymCleanup( GetCurrentProcess() );
484 _tprintf( _T("\r\n") );
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
)
493 #define EXCEPTION( x ) case EXCEPTION_##x: return _T(#x);
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
)
521 // If not one of the "known" exceptions, try to get the string
522 // from NTDLL.DLL's message table.
524 static TCHAR szBuffer
[512] = { 0 };
526 FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_FROM_HMODULE
,
527 GetModuleHandle( _T("NTDLL.DLL") ),
528 dwCode
, 0, szBuffer
, sizeof( szBuffer
), 0 );
533 //=============================================================================
534 // Given a linear address, locates the module, section, and offset containing
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
)
543 MEMORY_BASIC_INFORMATION mbi
;
545 if ( !VirtualQuery( addr
, &mbi
, sizeof(mbi
) ) )
548 DWORD_PTR hMod
= (DWORD_PTR
)mbi
.AllocationBase
;
550 if ( !GetModuleFileName( (HMODULE
)hMod
, szModule
, len
) )
553 // Point to the DOS header in memory
554 PIMAGE_DOS_HEADER pDosHdr
= (PIMAGE_DOS_HEADER
)hMod
;
556 // From the DOS header, find the NT (PE) header
557 PIMAGE_NT_HEADERS pNtHdr
= (PIMAGE_NT_HEADERS
)(hMod
+ DWORD_PTR(pDosHdr
->e_lfanew
));
559 PIMAGE_SECTION_HEADER pSection
= IMAGE_FIRST_SECTION( pNtHdr
);
561 DWORD_PTR rva
= (DWORD_PTR
)addr
- hMod
; // RVA is offset from module load address
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
;
569 DWORD_PTR sectionStart
= pSection
->VirtualAddress
;
570 DWORD_PTR sectionEnd
= sectionStart
571 + DWORD_PTR(max(pSection
->SizeOfRawData
, pSection
->Misc
.VirtualSize
));
573 // Is the address in this section???
574 if ( (rva
>= sectionStart
) && (rva
<= sectionEnd
) )
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.
580 offset
= rva
- sectionStart
;
585 return FALSE
; // Should never get here!
588 // It contains SYMBOL_INFO structure plus additional
589 // space for the name of the symbol
590 struct CSymbolInfoPackage
: public SYMBOL_INFO_PACKAGE
594 si
.SizeOfStruct
= sizeof(SYMBOL_INFO
);
595 si
.MaxNameLen
= sizeof(name
);
599 //============================================================
600 // Walks the stack, and writes the results to the report file
601 //============================================================
602 void WheatyExceptionReport::WriteStackDetails(
604 bool bWriteVariables
, HANDLE pThreadHandle
) // true if local/params should be output
606 _tprintf( _T("\r\nCall stack:\r\n") );
608 _tprintf( _T("Address Frame Function SourceFile\r\n") );
610 DWORD dwMachineType
= 0;
611 // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
614 memset( &sf
, 0, sizeof(sf
) );
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
;
626 dwMachineType
= IMAGE_FILE_MACHINE_I386
;
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
;
641 // Get the next stack frame
642 if ( ! StackWalk64( dwMachineType
,
644 pThreadHandle
!= NULL
? pThreadHandle
: GetCurrentThread(),
648 SymFunctionTableAccess64
,
652 if ( 0 == sf
.AddrFrame
.Offset
) // Basic sanity check to make sure
653 break; // the frame is OK. Bail if not.
655 _tprintf( _T("%08X %08X "), sf
.AddrPC
.Offset
, sf
.AddrFrame
.Offset
);
658 _tprintf( _T("%016I64X %016I64X "), sf
.AddrPC
.Offset
, sf
.AddrFrame
.Offset
);
661 DWORD64 symDisplacement
= 0; // Displacement of the input address,
662 // relative to the start of the symbol
664 // Get the name of the function for this stack frame entry
665 CSymbolInfoPackage sip
;
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)
673 _tprintf( _T("%hs+%I64X"), sip
.si
.Name
, symDisplacement
);
676 else // No symbol found. Print out the logical address instead.
678 TCHAR szModule
[MAX_PATH
] = _T("");
680 DWORD_PTR offset
= 0;
682 GetLogicalAddress( (PVOID
)sf
.AddrPC
.Offset
,
683 szModule
, sizeof(szModule
), section
, offset
);
685 _tprintf( _T("%04X:%08X %s"), section
, offset
, szModule
);
688 _tprintf( _T("%04X:%016I64X %s"), section
, offset
, szModule
);
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
) )
698 _tprintf(_T(" %s line %u"),lineInfo
.FileName
,lineInfo
.LineNumber
);
701 _tprintf( _T("\r\n") );
703 // Write out the variables, if desired
704 if ( bWriteVariables
)
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 );
711 // Enumerate the locals/parameters
712 SymEnumSymbols( m_hProcess
, 0, 0, EnumerateSymbolsCallback
, &sf
);
714 _tprintf( _T("\r\n") );
720 //////////////////////////////////////////////////////////////////////////////
721 // The function invoked by SymEnumSymbols
722 //////////////////////////////////////////////////////////////////////////////
725 WheatyExceptionReport::EnumerateSymbolsCallback(
726 PSYMBOL_INFO pSymInfo
,
735 if ( FormatSymbolValue( pSymInfo
, (STACKFRAME
*)UserContext
,
736 szBuffer
, sizeof(szBuffer
) ) )
737 _tprintf( _T("\t%s\r\n"), szBuffer
);
741 _tprintf( _T("punting on symbol %s\r\n"), pSymInfo
->Name
);
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
751 //////////////////////////////////////////////////////////////////////////////
752 bool WheatyExceptionReport::FormatSymbolValue(
758 char * pszCurrBuffer
= pszBuffer
;
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 " );
766 // If it's a function, don't do anything.
767 if ( pSym
->Tag
== 5 ) // SymTagFunction from CVCONST.H from the DIA SDK
770 DWORD_PTR pVariable
= 0; // Will point to the variable's data in memory
772 if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_REGRELATIVE
)
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
;
782 else if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_REGISTER
)
784 return false; // Don't try to report register variable
788 pVariable
= (DWORD_PTR
)pSym
->Address
; // It must be a global variable
791 // Determine if the variable is a user defined type (UDT). IF so, bHandled
794 pszCurrBuffer
= DumpTypeIndex(pszCurrBuffer
,pSym
->ModBase
, pSym
->TypeIndex
,
795 0, pVariable
, bHandled
, pSym
->Name
);
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
802 BasicType basicType
= GetBasicType( pSym
->TypeIndex
, pSym
->ModBase
);
803 pszCurrBuffer
+= sprintf( pszCurrBuffer
, rgBaseType
[basicType
]);
805 // Emit the variable name
806 pszCurrBuffer
+= sprintf( pszCurrBuffer
, "\'%s\'", pSym
->Name
);
808 pszCurrBuffer
= FormatOutputValue(pszCurrBuffer
, basicType
, pSym
->Size
,
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
,
824 unsigned nestingLevel
,
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
,
837 pszCurrBuffer
+= sprintf( pszCurrBuffer
, " %ls", pwszTypeName
);
838 LocalFree( pwszTypeName
);
841 // Determine how many children this type has.
842 DWORD dwChildrenCount
= 0;
843 SymGetTypeInfo( m_hProcess
, modBase
, dwTypeIndex
, TI_GET_CHILDRENCOUNT
,
846 if ( !dwChildrenCount
) // If no children, we're done
847 return pszCurrBuffer
;
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
854 ULONG MoreChildIds
[1024];
855 FINDCHILDREN(){Count
= sizeof(MoreChildIds
) / sizeof(MoreChildIds
[0]);}
858 children
.Count
= dwChildrenCount
;
861 // Get the array of TypeIds, one for each child type
862 if ( !SymGetTypeInfo( m_hProcess
, modBase
, dwTypeIndex
, TI_FINDCHILDREN
,
865 return pszCurrBuffer
;
868 // Append a line feed
869 pszCurrBuffer
+= sprintf( pszCurrBuffer
, "\r\n" );
871 // Iterate through each of the children
872 for ( unsigned i
= 0; i
< dwChildrenCount
; i
++ )
874 // Add appropriate indentation level (since this routine is recursive)
875 for ( unsigned j
= 0; j
<= nestingLevel
+1; j
++ )
876 pszCurrBuffer
+= sprintf( pszCurrBuffer
, "\t" );
878 // Recurse for each of the child types
880 BasicType basicType
= GetBasicType(children
.ChildId
[i
], modBase
);
881 pszCurrBuffer
+= sprintf( pszCurrBuffer
, rgBaseType
[basicType
]);
883 pszCurrBuffer
= DumpTypeIndex( pszCurrBuffer
, modBase
,
884 children
.ChildId
[i
], nestingLevel
+1,
885 offset
, bHandled2
, ""/*Name */);
887 // If the child wasn't a UDT, format it appropriately
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
);
895 // Get the real "TypeId" of the child. We need this for the
896 // SymGetTypeInfo( TI_GET_TYPEID ) call below.
898 SymGetTypeInfo( m_hProcess
, modBase
, children
.ChildId
[i
],
899 TI_GET_TYPEID
, &typeId
);
901 // Get the size of the child member
903 SymGetTypeInfo(m_hProcess
, modBase
, typeId
, TI_GET_LENGTH
,&length
);
905 // Calculate the address of the member
906 DWORD_PTR dwFinalOffset
= offset
+ dwMemberOffset
;
908 // BasicType basicType = GetBasicType(children.ChildId[i], modBase );
910 // pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]);
912 // Emit the variable name
913 // pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", Name );
915 pszCurrBuffer
= FormatOutputValue( pszCurrBuffer
, basicType
,
916 length
, (PVOID
)dwFinalOffset
);
918 pszCurrBuffer
+= sprintf( pszCurrBuffer
, "\r\n" );
923 return pszCurrBuffer
;
926 char * WheatyExceptionReport::FormatOutputValue( char * pszCurrBuffer
,
931 // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
933 pszCurrBuffer
+= sprintf( pszCurrBuffer
, " = %X", *(PBYTE
)pAddress
);
934 else if ( length
== 2 )
935 pszCurrBuffer
+= sprintf( pszCurrBuffer
, " = %X", *(PWORD
)pAddress
);
936 else if ( length
== 4 )
938 if ( basicType
== btFloat
)
940 pszCurrBuffer
+= sprintf(pszCurrBuffer
," = %f", *(PFLOAT
)pAddress
);
942 else if ( basicType
== btChar
)
944 if ( !IsBadStringPtr( *(PSTR
*)pAddress
, 32) )
946 pszCurrBuffer
+= sprintf( pszCurrBuffer
, " = \"%.31s\"",
950 pszCurrBuffer
+= sprintf( pszCurrBuffer
, " = %X",
954 pszCurrBuffer
+= sprintf(pszCurrBuffer
," = %X", *(PDWORD
)pAddress
);
956 else if ( length
== 8 )
958 if ( basicType
== btFloat
)
960 pszCurrBuffer
+= sprintf( pszCurrBuffer
, " = %lf",
961 *(double *)pAddress
);
964 pszCurrBuffer
+= sprintf( pszCurrBuffer
, " = %I64X",
965 *(DWORD64
*)pAddress
);
968 return pszCurrBuffer
;
972 WheatyExceptionReport::GetBasicType( DWORD typeIndex
, DWORD64 modBase
)
975 if ( SymGetTypeInfo( m_hProcess
, modBase
, typeIndex
,
976 TI_GET_BASETYPE
, &basicType
) )
981 // Get the real "TypeId" of the child. We need this for the
982 // SymGetTypeInfo( TI_GET_TYPEID ) call below.
984 if (SymGetTypeInfo(m_hProcess
,modBase
, typeIndex
, TI_GET_TYPEID
, &typeId
))
986 if ( SymGetTypeInfo( m_hProcess
, modBase
, typeId
, TI_GET_BASETYPE
,
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
, ...)
1007 va_start( argptr
, format
);
1008 retValue
= vsprintf( szBuff
, format
, argptr
);
1011 WriteFile(m_hReportFile
, szBuff
, retValue
* sizeof(TCHAR
), &cbWritten
, 0 );