root/trunk/src/shared/WheatyExceptionReport.cpp @ 37

Revision 37, 36.9 kB (checked in by yumileroy, 17 years ago)

[svn] * svn:eol-style native set on all files that need it

Original author: Neo2003
Date: 2008-10-11 14:16:25-05:00

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