Pol  Revision:cb584c9
msjexhnd.cpp
Go to the documentation of this file.
1 
7 #pragma warning( disable : 4189 ) // local variable is initialized but not referenced
8 #pragma warning( disable : 4312 ) // cast trunc
9 #pragma warning( disable : 4311 ) // cast trunc
10 #pragma warning( disable : 4302 ) // cast trunc
11 
12 //==========================================
13 // Matt Pietrek
14 // Microsoft Systems Journal, May 1997
15 // FILE: MSJEXHND.CPP
16 //==========================================
17 
18 #include <tchar.h>
19 #include "msjexhnd.h"
20 #include <imagehlp.h>
21 #include <algorithm>
22 #include <stdio.h>
23 #include "logfacility.h"
24 
25 namespace Pol
26 {
27 namespace Clib
28 {
29 //============================== Global Variables =============================
30 
31 //
32 // Declare the static variables of the MSJExceptionHandler class
33 //
35 LPTOP_LEVEL_EXCEPTION_FILTER MSJExceptionHandler::m_previousFilter;
37 
38 MSJExceptionHandler::SYMINITIALIZEPROC MSJExceptionHandler::_SymInitialize = 0;
39 MSJExceptionHandler::SYMCLEANUPPROC MSJExceptionHandler::_SymCleanup = 0;
40 MSJExceptionHandler::STACKWALKPROC MSJExceptionHandler::_StackWalk = 0;
41 
42 MSJExceptionHandler::SYMFUNCTIONTABLEACCESSPROC MSJExceptionHandler::_SymFunctionTableAccess = 0;
43 
44 MSJExceptionHandler::SYMGETMODULEBASEPROC MSJExceptionHandler::_SymGetModuleBase = 0;
45 
46 MSJExceptionHandler::SYMGETSYMFROMADDRPROC MSJExceptionHandler::_SymGetSymFromAddr = 0;
47 
48 MSJExceptionHandler g_MSJExceptionHandler; // Declare global instance of class
49 
50 //============================== Class Methods =============================
51 
52 //=============
53 // Constructor
54 //=============
56 {
57  // Install the unhandled exception filter function
58 
59  // m_previousFilter = SetUnhandledExceptionFilter(MSJUnhandledExceptionFilter);
60 
61  // Figure out what the report file will be named, and store it away
62  GetModuleFileName( 0, m_szLogFileName, MAX_PATH );
63 
64  // Look for the '.' before the "EXE" extension. Replace the extension
65  // with "RPT"
66  PTSTR pszDot = _tcsrchr( m_szLogFileName, _T( '.' ) );
67  if ( pszDot )
68  {
69  pszDot++; // Advance past the '.'
70  if ( _tcslen( pszDot ) >= 3 )
71  _tcscpy( pszDot, _T( "RPT" ) ); // "RPT" -> "Report"
72  }
73 }
74 
75 //============
76 // Destructor
77 //============
79 {
80  // SetUnhandledExceptionFilter( m_previousFilter );
81 }
82 
83 //==============================================================
84 // Lets user change the name of the report file to be generated
85 //==============================================================
86 void MSJExceptionHandler::SetLogFileName( PTSTR pszLogFileName )
87 {
88  if ( _tcslen( pszLogFileName ) <= 260 )
89  {
90  _tcscpy( m_szLogFileName, pszLogFileName );
91  }
92 }
93 
94 //===========================================================
95 // Entry point where control comes on an unhandled exception
96 //===========================================================
97 LONG WINAPI MSJExceptionHandler::MSJUnhandledExceptionFilter( PEXCEPTION_POINTERS pExceptionInfo )
98 {
100  CreateFile( m_szLogFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 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_CONTINUE_SEARCH;
116 }
117 
118 //===========================================================================
119 // Open the report file, and write the desired information to it. Called by
120 // MSJUnhandledExceptionFilter
121 //===========================================================================
122 void MSJExceptionHandler::GenerateExceptionReport( PEXCEPTION_POINTERS pExceptionInfo )
123 {
124  // Start out with a banner
125  _tprintf( _T( "//=====================================================\n" ) );
126 
127  PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
128 
129  // First print information about the type of fault
130  _tprintf( _T( "Exception code: %08X %s\n" ), pExceptionRecord->ExceptionCode,
131  GetExceptionString( pExceptionRecord->ExceptionCode ) );
132 
133  // Now print information about where the fault occured
134  TCHAR szFaultingModule[MAX_PATH];
135  DWORD section, offset;
136  GetLogicalAddress( pExceptionRecord->ExceptionAddress, szFaultingModule,
137  sizeof( szFaultingModule ), section, offset );
138 
139  _tprintf( _T( "Fault address: %08X %02X:%08X %s\n" ), pExceptionRecord->ExceptionAddress,
140  section, offset, szFaultingModule );
141 
142  PCONTEXT pCtx = pExceptionInfo->ContextRecord;
143 
144 // Show the registers
145 #ifdef _M_IX86 // Intel Only!
146  _tprintf( _T("\nRegisters:\n") );
147 
148  _tprintf( _T("EAX:%08X\nEBX:%08X\nECX:%08X\nEDX:%08X\nESI:%08X\nEDI:%08X\n"), pCtx->Eax,
149  pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi );
150 
151  _tprintf( _T("CS:EIP:%04X:%08X\n"), pCtx->SegCs, pCtx->Eip );
152  _tprintf( _T("SS:ESP:%04X:%08X EBP:%08X\n"), pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
153  _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\n"), pCtx->SegDs, pCtx->SegEs, pCtx->SegFs,
154  pCtx->SegGs );
155  _tprintf( _T("Flags:%08X\n"), pCtx->EFlags );
156 
157 #endif
158 
159  if ( !InitImagehlpFunctions() )
160  {
161  OutputDebugString( _T( "IMAGEHLP.DLL or its exported procs not found" ) );
162 
163 #ifdef _M_IX86 // Intel Only!
164 #ifndef _M_X64
165  // Walk the stack using x86 specific code
166  IntelStackWalk( pCtx );
167 #endif
168 #endif
169 
170  return;
171  }
172 #ifndef _M_X64
173  ImagehlpStackWalk( pCtx );
174 #endif
175 
176  _SymCleanup( GetCurrentProcess() );
177 
178  _tprintf( _T( "\n" ) );
179 }
180 
181 //======================================================================
182 // Given an exception code, returns a pointer to a static string with a
183 // description of the exception
184 //======================================================================
186 {
187 #define EXCEPTION( x ) \
188  case EXCEPTION_##x: \
189  return _T( #x );
190 
191  switch ( dwCode )
192  {
193  EXCEPTION( ACCESS_VIOLATION )
194  EXCEPTION( DATATYPE_MISALIGNMENT )
195  EXCEPTION( BREAKPOINT )
196  EXCEPTION( SINGLE_STEP )
197  EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
198  EXCEPTION( FLT_DENORMAL_OPERAND )
199  EXCEPTION( FLT_DIVIDE_BY_ZERO )
200  EXCEPTION( FLT_INEXACT_RESULT )
201  EXCEPTION( FLT_INVALID_OPERATION )
202  EXCEPTION( FLT_OVERFLOW )
203  EXCEPTION( FLT_STACK_CHECK )
204  EXCEPTION( FLT_UNDERFLOW )
205  EXCEPTION( INT_DIVIDE_BY_ZERO )
206  EXCEPTION( INT_OVERFLOW )
207  EXCEPTION( PRIV_INSTRUCTION )
208  EXCEPTION( IN_PAGE_ERROR )
209  EXCEPTION( ILLEGAL_INSTRUCTION )
210  EXCEPTION( NONCONTINUABLE_EXCEPTION )
211  EXCEPTION( STACK_OVERFLOW )
212  EXCEPTION( INVALID_DISPOSITION )
213  EXCEPTION( GUARD_PAGE )
214  EXCEPTION( INVALID_HANDLE )
215  }
216 
217  // If not one of the "known" exceptions, try to get the string
218  // from NTDLL.DLL's message table.
219 
220  static TCHAR szBuffer[512] = {0};
221 
222  FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
223  GetModuleHandle( _T( "NTDLL.DLL" ) ), dwCode, 0, szBuffer, sizeof( szBuffer ), 0 );
224 
225  return szBuffer;
226 }
227 
228 //==============================================================================
229 // Given a linear address, locates the module, section, and offset containing
230 // that address.
231 //
232 // Note: the szModule paramater buffer is an output buffer of length specified
233 // by the len parameter (in characters!)
234 //==============================================================================
235 BOOL MSJExceptionHandler::GetLogicalAddress( PVOID addr, PTSTR szModule, DWORD len, DWORD& section,
236  DWORD& offset )
237 {
238  MEMORY_BASIC_INFORMATION mbi;
239 
240  if ( !VirtualQuery( addr, &mbi, sizeof( mbi ) ) )
241  return FALSE;
242 
243  DWORD hMod = (DWORD)mbi.AllocationBase;
244 
245  if ( !GetModuleFileName( (HMODULE)hMod, szModule, len ) )
246  return FALSE;
247 
248  // Point to the DOS header in memory
249  PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
250 
251  // From the DOS header, find the NT (PE) header
252  PIMAGE_NT_HEADERS pNtHdr = ( PIMAGE_NT_HEADERS )( hMod + pDosHdr->e_lfanew );
253 
254  PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
255 
256  DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address
257 
258  // Iterate through the section table, looking for the one that encompasses
259  // the linear address.
260  for ( unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++ )
261  {
262  DWORD sectionStart = pSection->VirtualAddress;
263  DWORD sectionEnd =
264  sectionStart + std::max( pSection->SizeOfRawData, pSection->Misc.VirtualSize );
265 
266  // Is the address in this section???
267  if ( ( rva >= sectionStart ) && ( rva <= sectionEnd ) )
268  {
269  // Yes, address is in the section. Calculate section and offset,
270  // and store in the "section" & "offset" params, which were
271  // passed by reference.
272  section = i + 1;
273  offset = rva - sectionStart;
274  return TRUE;
275  }
276  }
277 
278  return FALSE; // Should never get here!
279 }
280 
281 //============================================================
282 // Walks the stack, and writes the results to the report file
283 //============================================================
284 #ifndef _M_X64
285 void MSJExceptionHandler::IntelStackWalk( PCONTEXT pContext )
286 {
287  _tprintf( _T("\nCall stack:\n") );
288 
289  _tprintf( _T("Address Frame Logical addr Module\n") );
290 
291  DWORD pc = pContext->Eip;
292  PDWORD pFrame, pPrevFrame;
293 
294  pFrame = (PDWORD)pContext->Ebp;
295 
296  do
297  {
298  TCHAR szModule[MAX_PATH] = _T("");
299  DWORD section = 0, offset = 0;
300 
301  GetLogicalAddress( (PVOID)pc, szModule, sizeof( szModule ), section, offset );
302 
303  _tprintf( _T("%08X %08X %04X:%08X %s\n"), pc, pFrame, section, offset, szModule );
304 
305  pc = pFrame[1];
306 
307  pPrevFrame = pFrame;
308 
309  pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack
310 
311  if ( (DWORD)pFrame & 3 ) // Frame pointer must be aligned on a
312  break; // DWORD boundary. Bail if not so.
313 
314  if ( pFrame <= pPrevFrame )
315  break;
316 
317  // Can two DWORDs be read from the supposed frame address?
318  if ( IsBadWritePtr( pFrame, sizeof( PVOID ) * 2 ) )
319  break;
320 
321  } while ( 1 );
322 }
323 #endif
324 
325 //============================================================
326 // Walks the stack, and writes the results to the report file
327 //============================================================
328 #ifndef _M_X64
329 void MSJExceptionHandler::ImagehlpStackWalk( PCONTEXT pContext )
330 {
331  _tprintf( _T("\nCall stack:\n") );
332 
333  _tprintf( _T("Address Frame\n") );
334 
335  // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
336 
337  STACKFRAME sf;
338  memset( &sf, 0, sizeof( sf ) );
339 
340  // Initialize the STACKFRAME structure for the first call. This is only
341  // necessary for Intel CPUs, and isn't mentioned in the documentation.
342  sf.AddrPC.Offset = pContext->Eip;
343  sf.AddrPC.Mode = AddrModeFlat;
344  sf.AddrStack.Offset = pContext->Esp;
345  sf.AddrStack.Mode = AddrModeFlat;
346  sf.AddrFrame.Offset = pContext->Ebp;
347  sf.AddrFrame.Mode = AddrModeFlat;
348 
349  while ( 1 )
350  {
351  if ( !_StackWalk( IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &sf,
352  pContext, 0, _SymFunctionTableAccess, _SymGetModuleBase, 0 ) )
353  break;
354 
355  if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure
356  break; // the frame is OK. Bail if not.
357 
358  _tprintf( _T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset );
359 
360  // IMAGEHLP is wacky, and requires you to pass in a pointer to an
361  // IMAGEHLP_SYMBOL structure. The problem is that this structure is
362  // variable length. That is, you determine how big the structure is
363  // at runtime. This means that you can't use sizeof(struct).
364  // So...make a buffer that's big enough, and make a pointer
365  // to the buffer. We also need to initialize not one, but TWO
366  // members of the structure before it can be used.
367 
368  BYTE symbolBuffer[sizeof( IMAGEHLP_SYMBOL ) + 512];
369  PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
370  pSymbol->SizeOfStruct = sizeof( symbolBuffer );
371  pSymbol->MaxNameLength = 512;
372 
373  DWORD symDisplacement = 0; // Displacement of the input address,
374  // relative to the start of the symbol
375 
376  if ( _SymGetSymFromAddr( GetCurrentProcess(), sf.AddrPC.Offset, &symDisplacement, pSymbol ) )
377  {
378  _tprintf( _T("%hs+%X\n"), pSymbol->Name, symDisplacement );
379  }
380  else // No symbol found. Print out the logical address instead.
381  {
382  TCHAR szModule[MAX_PATH] = _T("");
383  DWORD section = 0, offset = 0;
384 
385  GetLogicalAddress( (PVOID)sf.AddrPC.Offset, szModule, sizeof( szModule ), section, offset );
386 
387  _tprintf( _T("%04X:%08X %s\n"), section, offset, szModule );
388  }
389  }
390 }
391 #endif
392 
393 //============================================================================
394 // Helper function that writes to the report file, and allows the user to use
395 // printf style formating
396 //============================================================================
397 int __cdecl MSJExceptionHandler::_tprintf( const TCHAR* format, ... )
398 {
399  TCHAR szBuff[1024];
400  int retValue;
401  // DWORD cbWritten;
402  va_list argptr;
403 
404  va_start( argptr, format );
405  retValue = wvsprintf( szBuff, format, argptr );
406  va_end( argptr );
407 
408  // WriteFile( m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0 );
409  POLLOG_INFO << szBuff;
410 
411  return retValue;
412 }
413 
414 
415 //=========================================================================
416 // Load IMAGEHLP.DLL and get the address of functions in it that we'll use
417 //=========================================================================
419 {
420  HMODULE hModImagehlp = LoadLibrary( _T( "IMAGEHLP.DLL" ) );
421  if ( !hModImagehlp )
422  return FALSE;
423 
424  _SymInitialize = (SYMINITIALIZEPROC)GetProcAddress( hModImagehlp, "SymInitialize" );
425  if ( !_SymInitialize )
426  {
427  FreeLibrary( hModImagehlp );
428  return FALSE;
429  }
430 
431 
432  _SymCleanup = (SYMCLEANUPPROC)GetProcAddress( hModImagehlp, "SymCleanup" );
433  if ( !_SymCleanup )
434  {
435  FreeLibrary( hModImagehlp );
436  return FALSE;
437  }
438 
439  _StackWalk = (STACKWALKPROC)GetProcAddress( hModImagehlp, "StackWalk" );
440  if ( !_StackWalk )
441  {
442  FreeLibrary( hModImagehlp );
443  return FALSE;
444  }
445 
447  (SYMFUNCTIONTABLEACCESSPROC)GetProcAddress( hModImagehlp, "SymFunctionTableAccess" );
448 
450  {
451  FreeLibrary( hModImagehlp );
452  return FALSE;
453  }
454 
455  _SymGetModuleBase = (SYMGETMODULEBASEPROC)GetProcAddress( hModImagehlp, "SymGetModuleBase" );
456  if ( !_SymGetModuleBase )
457  {
458  FreeLibrary( hModImagehlp );
459  return FALSE;
460  }
461 
462  _SymGetSymFromAddr = (SYMGETSYMFROMADDRPROC)GetProcAddress( hModImagehlp, "SymGetSymFromAddr" );
463  if ( !_SymGetSymFromAddr )
464  {
465  FreeLibrary( hModImagehlp );
466  return FALSE;
467  }
468 
469  if ( !_SymInitialize( GetCurrentProcess(), 0, TRUE ) )
470  {
471  FreeLibrary( hModImagehlp );
472  return FALSE;
473  }
474 
475  return TRUE;
476 }
477 }
478 }
static TCHAR m_szLogFileName[MAX_PATH]
Definition: msjexhnd.h:47
static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter
Definition: msjexhnd.h:48
static int __cdecl _tprintf(const TCHAR *format,...)
Definition: msjexhnd.cpp:397
static SYMFUNCTIONTABLEACCESSPROC _SymFunctionTableAccess
Definition: msjexhnd.h:71
MSJExceptionHandler g_MSJExceptionHandler
Definition: msjexhnd.cpp:48
#define POLLOG_INFO
Definition: logfacility.h:213
static LONG WINAPI MSJUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
Definition: msjexhnd.cpp:97
static SYMGETMODULEBASEPROC _SymGetModuleBase
Definition: msjexhnd.h:72
static LPTSTR GetExceptionString(DWORD dwCode)
Definition: msjexhnd.cpp:185
static STACKWALKPROC _StackWalk
Definition: msjexhnd.h:70
void SetLogFileName(PTSTR pszLogFileName)
Definition: msjexhnd.cpp:86
static HANDLE m_hReportFile
Definition: msjexhnd.h:49
static void IntelStackWalk(PCONTEXT pContext)
Definition: msjexhnd.cpp:285
static SYMGETSYMFROMADDRPROC _SymGetSymFromAddr
Definition: msjexhnd.h:73
static SYMINITIALIZEPROC _SymInitialize
Definition: msjexhnd.h:68
#define EXCEPTION(x)
static void ImagehlpStackWalk(PCONTEXT pContext)
Definition: msjexhnd.cpp:329
static void GenerateExceptionReport(PEXCEPTION_POINTERS pExceptionInfo)
Definition: msjexhnd.cpp:122
static BOOL InitImagehlpFunctions(void)
Definition: msjexhnd.cpp:418
Definition: berror.cpp:12
static BOOL GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len, DWORD &section, DWORD &offset)
Definition: msjexhnd.cpp:235
static SYMCLEANUPPROC _SymCleanup
Definition: msjexhnd.h:69