[7297] Fixed profession spells sorting in trainer spell list at client.
[getmangos.git] / src / shared / WheatyExceptionReport.cpp
blobd37019e08c34fc478b839d52cee7ae40767e19fa
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 "revision.h"
18 #include "revision_nr.h"
19 #define CrashFolder _T("Crashs")
20 //#pragma comment(linker, "/defaultlib:dbghelp.lib")
22 inline LPTSTR ErrorMessage(DWORD dw)
24 LPVOID lpMsgBuf;
25 FormatMessage(
26 FORMAT_MESSAGE_ALLOCATE_BUFFER |
27 FORMAT_MESSAGE_FROM_SYSTEM,
28 NULL,
29 dw,
30 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
31 (LPTSTR) &lpMsgBuf,
32 0, NULL );
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();
58 //============
59 // Destructor
60 //============
61 WheatyExceptionReport::~WheatyExceptionReport( )
63 if(m_previousFilter)
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, '\\');
76 if(!pos)
77 return 0;
78 pos[0] = '\0';
79 ++pos;
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)
86 return 0;
89 SYSTEMTIME systime;
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,
96 GENERIC_WRITE,
99 OPEN_ALWAYS,
100 FILE_FLAG_WRITE_THROUGH,
101 0 );
103 if ( m_hReportFile )
105 SetFilePointer( m_hReportFile, 0, 0, FILE_END );
107 GenerateExceptionReport( pExceptionInfo );
109 CloseHandle( m_hReportFile );
110 m_hReportFile = 0;
113 if ( m_previousFilter )
114 return m_previousFilter( pExceptionInfo );
115 else
116 return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/;
119 BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount)
121 if(!sProcessorName)
122 return FALSE;
124 HKEY hKey;
125 LONG lRet;
126 lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
127 0, KEY_QUERY_VALUE, &hKey);
128 if (lRet != ERROR_SUCCESS)
129 return FALSE;
130 TCHAR szTmp[2048];
131 DWORD cntBytes = sizeof(szTmp);
132 lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), NULL, NULL,
133 (LPBYTE)szTmp, &cntBytes);
134 if (lRet != ERROR_SUCCESS)
135 return FALSE;
136 ::RegCloseKey(hKey);
137 sProcessorName[0] = '\0';
138 // Skip spaces
139 TCHAR* psz = szTmp;
140 while (iswspace(*psz))
141 ++psz;
142 _tcsncpy(sProcessorName, psz, maxcount);
143 return TRUE;
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))
158 return FALSE;
160 *szVersion = _T('\0');
161 TCHAR wszTmp[128];
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.
182 #if WINVER < 0x0500
183 if (osvi.wReserved[1] == VER_NT_WORKSTATION)
184 #else
185 if (osvi.wProductType == VER_NT_WORKSTATION)
186 #endif // WINVER < 0x0500
188 if (osvi.dwMajorVersion == 4)
189 _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax);
190 #if WINVER < 0x0500
191 else if (osvi.wReserved[0] & VER_SUITE_PERSONAL)
192 #else
193 else if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
194 #endif // WINVER < 0x0500
195 _tcsncat(szVersion, _T("Home Edition "), cntMax);
196 #if WINVER < 0x0500
197 else if (osvi.wReserved[0] & VER_SUITE_EMBEDDEDNT)
198 #else
199 else if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT)
200 #endif // WINVER < 0x0500
201 _tcsncat(szVersion, _T("Embedded "), cntMax);
202 else
203 _tcsncat(szVersion, _T("Professional "), cntMax);
205 // Test for the server type.
206 #if WINVER < 0x0500
207 else if (osvi.wReserved[1] == VER_NT_SERVER)
208 #else
209 else if (osvi.wProductType == VER_NT_SERVER)
210 #endif // WINVER < 0x0500
212 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
214 #if WINVER < 0x0500
215 if (osvi.wReserved[0] & VER_SUITE_DATACENTER)
216 #else
217 if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
218 #endif // WINVER < 0x0500
219 _tcsncat(szVersion, _T("Datacenter Edition "), cntMax);
220 #if WINVER < 0x0500
221 else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
222 #else
223 else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
224 #endif // WINVER < 0x0500
225 _tcsncat(szVersion, _T("Enterprise Edition "), cntMax);
226 #if WINVER < 0x0500
227 else if (osvi.wReserved[0] == VER_SUITE_BLADE)
228 #else
229 else if (osvi.wSuiteMask == VER_SUITE_BLADE)
230 #endif // WINVER < 0x0500
231 _tcsncat(szVersion, _T("Web Edition "), cntMax);
232 else
233 _tcsncat(szVersion, _T("Standard Edition "), cntMax);
235 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
237 #if WINVER < 0x0500
238 if (osvi.wReserved[0] & VER_SUITE_DATACENTER)
239 #else
240 if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
241 #endif // WINVER < 0x0500
242 _tcsncat(szVersion, _T("Datacenter Server "), cntMax);
243 #if WINVER < 0x0500
244 else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE )
245 #else
246 else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
247 #endif // WINVER < 0x0500
248 _tcsncat(szVersion, _T("Advanced Server "), cntMax);
249 else
250 _tcsncat(szVersion, _T("Server "), cntMax);
252 else // Windows NT 4.0
254 #if WINVER < 0x0500
255 if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
256 #else
257 if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
258 #endif // WINVER < 0x0500
259 _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax);
260 else
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)
268 HKEY hKey;
269 LONG lRet;
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);
285 ::RegCloseKey(hKey);
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);
292 else
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);
297 break;
298 default:
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);
302 break;
305 return TRUE;
308 void WheatyExceptionReport::PrintSystemInfo()
310 SYSTEM_INFO SystemInfo;
311 ::GetSystemInfo(&SystemInfo);
313 MEMORYSTATUS MemoryStatus;
314 MemoryStatus.dwLength = sizeof (MEMORYSTATUS);
315 ::GlobalMemoryStatus(&MemoryStatus);
316 TCHAR sString[1024];
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);
321 else
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);
327 else
328 _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n"));
331 //===========================================================================
332 void WheatyExceptionReport::printTracesForAllThreads()
334 HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
335 THREADENTRY32 te32;
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 )
342 return;
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
352 // snapshot object!
353 return;
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 )
363 CONTEXT context;
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 )
385 SYSTEMTIME systime;
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;
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) );
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 );
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
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 );
436 #endif
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
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);
495 switch ( dwCode )
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 );
530 return szBuffer;
533 //=============================================================================
534 // Given a linear address, locates the module, section, and offset containing
535 // that address.
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) ) )
546 return FALSE;
548 DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase;
550 if ( !GetModuleFileName( (HMODULE)hMod, szModule, len ) )
551 return FALSE;
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;
567 i++, pSection++ )
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.
579 section = i+1;
580 offset = rva - sectionStart;
581 return TRUE;
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
592 CSymbolInfoPackage()
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(
603 PCONTEXT pContext,
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
613 STACKFRAME64 sf;
614 memset( &sf, 0, sizeof(sf) );
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;
626 dwMachineType = IMAGE_FILE_MACHINE_I386;
627 #endif
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
639 while ( 1 )
641 // Get the next stack frame
642 if ( ! StackWalk64( dwMachineType,
643 m_hProcess,
644 pThreadHandle != NULL ? pThreadHandle : GetCurrentThread(),
645 &sf,
646 pContext,
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
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;
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)
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("");
679 DWORD section = 0;
680 DWORD_PTR offset = 0;
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
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 //////////////////////////////////////////////////////////////////////////////
724 BOOL CALLBACK
725 WheatyExceptionReport::EnumerateSymbolsCallback(
726 PSYMBOL_INFO pSymInfo,
727 ULONG SymbolSize,
728 PVOID UserContext )
731 char szBuffer[2048];
733 __try
735 if ( FormatSymbolValue( pSymInfo, (STACKFRAME*)UserContext,
736 szBuffer, sizeof(szBuffer) ) )
737 _tprintf( _T("\t%s\r\n"), szBuffer );
739 __except( 1 )
741 _tprintf( _T("punting on symbol %s\r\n"), pSymInfo->Name );
744 return TRUE;
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 )
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
768 return false;
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;
779 // else
780 // return false;
782 else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER )
784 return false; // Don't try to report register variable
786 else
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
792 // will return true.
793 bool bHandled;
794 pszCurrBuffer = DumpTypeIndex(pszCurrBuffer,pSym->ModBase, pSym->TypeIndex,
795 0, pVariable, bHandled, pSym->Name );
797 if ( !bHandled )
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]);
805 // Emit the variable name
806 pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", pSym->Name );
808 pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, pSym->Size,
809 (PVOID)pVariable );
812 return true;
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)
829 bHandled = false;
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 ) )
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,
844 &dwChildrenCount );
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]);}
856 } children;
858 children.Count = dwChildrenCount;
859 children.Start= 0;
861 // Get the array of TypeIds, one for each child type
862 if ( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
863 &children ) )
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
879 bool bHandled2;
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
888 if ( !bHandled2 )
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.
897 DWORD typeId;
898 SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i],
899 TI_GET_TYPEID, &typeId );
901 // Get the size of the child member
902 ULONG64 length;
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" );
922 bHandled = true;
923 return pszCurrBuffer;
926 char * WheatyExceptionReport::FormatOutputValue( char * pszCurrBuffer,
927 BasicType basicType,
928 DWORD64 length,
929 PVOID pAddress )
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 )
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\"",
947 *(PDWORD)pAddress );
949 else
950 pszCurrBuffer += sprintf( pszCurrBuffer, " = %X",
951 *(PDWORD)pAddress );
953 else
954 pszCurrBuffer += sprintf(pszCurrBuffer," = %X", *(PDWORD)pAddress);
956 else if ( length == 8 )
958 if ( basicType == btFloat )
960 pszCurrBuffer += sprintf( pszCurrBuffer, " = %lf",
961 *(double *)pAddress );
963 else
964 pszCurrBuffer += sprintf( pszCurrBuffer, " = %I64X",
965 *(DWORD64*)pAddress );
968 return pszCurrBuffer;
971 BasicType
972 WheatyExceptionReport::GetBasicType( DWORD typeIndex, DWORD64 modBase )
974 BasicType basicType;
975 if ( SymGetTypeInfo( m_hProcess, modBase, typeIndex,
976 TI_GET_BASETYPE, &basicType ) )
978 return basicType;
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))
986 if ( SymGetTypeInfo( m_hProcess, modBase, typeId, TI_GET_BASETYPE,
987 &basicType ) )
989 return basicType;
993 return btNoType;
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, ...)
1002 TCHAR szBuff[1024];
1003 int retValue;
1004 DWORD cbWritten;
1005 va_list argptr;
1007 va_start( argptr, format );
1008 retValue = vsprintf( szBuff, format, argptr );
1009 va_end( argptr );
1011 WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0 );
1013 return retValue;