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

Revision 2, 35.3 kB (checked in by yumileroy, 17 years ago)

[svn] * Proper SVN structure

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