StackDump.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. #include "stdh.h"
  13. #include <Engine/Base/Console_Internal.h>
  14. #include <Engine/Build.h>
  15. extern ULONG _ulEngineBuildMajor;
  16. extern ULONG _ulEngineBuildMinor;
  17. //==========================================
  18. // Matt Pietrek
  19. // Microsoft Systems Journal, May 1997
  20. // FILE: MSJEXHND.CPP
  21. class MSJExceptionHandler
  22. {
  23. public:
  24. MSJExceptionHandler( );
  25. ~MSJExceptionHandler( );
  26. void SetLogFileName(const char* pszLogFileName );
  27. private:
  28. // entry point where control comes on an unhandled exception
  29. static LONG WINAPI MSJUnhandledExceptionFilter(
  30. PEXCEPTION_POINTERS pExceptionInfo );
  31. // where report info is extracted and generated
  32. static void GenerateExceptionReport( PEXCEPTION_POINTERS pExceptionInfo );
  33. // Helper functions
  34. static const char* GetExceptionString( DWORD dwCode );
  35. static BOOL GetLogicalAddress(PVOID addr, char* szModule, DWORD len,
  36. DWORD& section, DWORD& offset );
  37. static void IntelStackWalk( PCONTEXT pContext );
  38. static int __cdecl _tprintf(const char * format, ...);
  39. // Variables used by the class
  40. static char m_szLogFileName[MAX_PATH];
  41. static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter;
  42. static HANDLE m_hReportFile;
  43. };
  44. //extern MSJExceptionHandler g_MSJExceptionHandler; // global instance of class
  45. MSJExceptionHandler g_MSJExceptionHandler; // Declare global instance of class
  46. //============================== Global Variables =============================
  47. //
  48. // Declare the static variables of the MSJExceptionHandler class
  49. //
  50. char MSJExceptionHandler::m_szLogFileName[MAX_PATH];
  51. LPTOP_LEVEL_EXCEPTION_FILTER MSJExceptionHandler::m_previousFilter;
  52. HANDLE MSJExceptionHandler::m_hReportFile;
  53. //============================== Class Methods =============================
  54. //=============
  55. // Constructor
  56. //=============
  57. MSJExceptionHandler::MSJExceptionHandler( )
  58. {
  59. // Install the unhandled exception filter function
  60. m_previousFilter = SetUnhandledExceptionFilter(MSJUnhandledExceptionFilter);
  61. // Figure out what the report file will be named, and store it away
  62. GetModuleFileNameA( 0, m_szLogFileName, MAX_PATH );
  63. // Look for the '.' before the "EXE" extension. Replace the extension
  64. // with "RPT"
  65. char* pszDot = strrchr( m_szLogFileName, '.' );
  66. if ( pszDot )
  67. {
  68. pszDot++; // Advance past the '.'
  69. if ( strlen(pszDot) >= 3 )
  70. strcpy( pszDot, "RPT" ); // "RPT" -> "Report"
  71. }
  72. }
  73. //============
  74. // Destructor
  75. //============
  76. MSJExceptionHandler::~MSJExceptionHandler( )
  77. {
  78. SetUnhandledExceptionFilter( m_previousFilter );
  79. }
  80. //==============================================================
  81. // Lets user change the name of the report file to be generated
  82. //==============================================================
  83. void MSJExceptionHandler::SetLogFileName(const char* pszLogFileName )
  84. {
  85. strcpy( m_szLogFileName, pszLogFileName );
  86. }
  87. //===========================================================
  88. // Entry point where control comes on an unhandled exception
  89. //===========================================================
  90. LONG WINAPI MSJExceptionHandler::MSJUnhandledExceptionFilter(
  91. PEXCEPTION_POINTERS pExceptionInfo )
  92. {
  93. m_hReportFile = CreateFileA( m_szLogFileName,
  94. GENERIC_WRITE,
  95. 0,
  96. 0,
  97. OPEN_ALWAYS,
  98. FILE_FLAG_WRITE_THROUGH,
  99. 0 );
  100. if ( m_hReportFile )
  101. {
  102. SetFilePointer( m_hReportFile, 0, 0, FILE_END );
  103. GenerateExceptionReport( pExceptionInfo );
  104. CloseHandle( m_hReportFile );
  105. m_hReportFile = 0;
  106. }
  107. // make sure the console log was written safely
  108. if (_pConsole!=NULL) {
  109. _pConsole->CloseLog();
  110. }
  111. extern void EnableWindowsKeys(void);
  112. EnableWindowsKeys();
  113. if ( m_previousFilter )
  114. return m_previousFilter( pExceptionInfo );
  115. else
  116. return EXCEPTION_CONTINUE_SEARCH;
  117. }
  118. //===========================================================================
  119. // Open the report file, and write the desired information to it. Called by
  120. // MSJUnhandledExceptionFilter
  121. //===========================================================================
  122. void MSJExceptionHandler::GenerateExceptionReport(
  123. PEXCEPTION_POINTERS pExceptionInfo )
  124. {
  125. // Start out with a banner
  126. _tprintf( "//=====================================================\n" );
  127. char strTime[80];
  128. _strtime(strTime);
  129. char strDate[80];
  130. _strdate(strDate);
  131. _tprintf( "Crashed at: %s %s\n", strDate, strTime);
  132. _tprintf( "Version: %d.%d%s%s\n", _ulEngineBuildMajor, _ulEngineBuildMinor, _SE_BUILD_EXTRA, _SE_DEMO?"-demo":"");
  133. PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
  134. // First print information about the type of fault
  135. _tprintf( "Exception code: %08X %s\n",
  136. pExceptionRecord->ExceptionCode,
  137. GetExceptionString(pExceptionRecord->ExceptionCode) );
  138. // Now print information about where the fault occured
  139. char szFaultingModule[MAX_PATH];
  140. DWORD section, offset;
  141. GetLogicalAddress( pExceptionRecord->ExceptionAddress,
  142. szFaultingModule,
  143. sizeof( szFaultingModule ),
  144. section, offset );
  145. _tprintf( "Fault address: %08X %02X:%08X %s\n",
  146. pExceptionRecord->ExceptionAddress,
  147. section, offset, szFaultingModule );
  148. PCONTEXT pCtx = pExceptionInfo->ContextRecord;
  149. // Show the registers
  150. #ifdef _M_IX86 // Intel Only!
  151. _tprintf( "\nRegisters:\n" );
  152. _tprintf("EAX:%08X\nEBX:%08X\nECX:%08X\nEDX:%08X\nESI:%08X\nEDI:%08X\n",
  153. pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi );
  154. _tprintf( "CS:EIP:%04X:%08X\n", pCtx->SegCs, pCtx->Eip );
  155. _tprintf( "SS:ESP:%04X:%08X EBP:%08X\n",
  156. pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
  157. _tprintf( "DS:%04X ES:%04X FS:%04X GS:%04X\n",
  158. pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
  159. _tprintf( "Flags:%08X\n", pCtx->EFlags );
  160. #endif
  161. #ifdef _M_IX86 // Intel Only!
  162. // Walk the stack using x86 specific code
  163. IntelStackWalk( pCtx );
  164. #endif
  165. _tprintf( "\n" );
  166. }
  167. //======================================================================
  168. // Given an exception code, returns a pointer to a static string with a
  169. // description of the exception
  170. //======================================================================
  171. const char* MSJExceptionHandler::GetExceptionString( DWORD dwCode )
  172. {
  173. #define EXCEPTION( x ) case EXCEPTION_##x: return #x;
  174. switch ( dwCode )
  175. {
  176. EXCEPTION( ACCESS_VIOLATION )
  177. EXCEPTION( DATATYPE_MISALIGNMENT )
  178. case EXCEPTION_BREAKPOINT: return "BREAKPOINT";
  179. //EXCEPTION( BREAKPOINT )
  180. EXCEPTION( SINGLE_STEP )
  181. EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
  182. EXCEPTION( FLT_DENORMAL_OPERAND )
  183. EXCEPTION( FLT_DIVIDE_BY_ZERO )
  184. EXCEPTION( FLT_INEXACT_RESULT )
  185. EXCEPTION( FLT_INVALID_OPERATION )
  186. EXCEPTION( FLT_OVERFLOW )
  187. EXCEPTION( FLT_STACK_CHECK )
  188. EXCEPTION( FLT_UNDERFLOW )
  189. EXCEPTION( INT_DIVIDE_BY_ZERO )
  190. EXCEPTION( INT_OVERFLOW )
  191. EXCEPTION( PRIV_INSTRUCTION )
  192. EXCEPTION( IN_PAGE_ERROR )
  193. EXCEPTION( ILLEGAL_INSTRUCTION )
  194. EXCEPTION( NONCONTINUABLE_EXCEPTION )
  195. EXCEPTION( STACK_OVERFLOW )
  196. EXCEPTION( INVALID_DISPOSITION )
  197. EXCEPTION( GUARD_PAGE )
  198. EXCEPTION( INVALID_HANDLE )
  199. }
  200. // If not one of the "known" exceptions, try to get the string
  201. // from NTDLL.DLL's message table.
  202. static char szBuffer[512] = { 0 };
  203. FormatMessageA( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
  204. GetModuleHandleA( "NTDLL.DLL" ),
  205. dwCode, 0, szBuffer, sizeof( szBuffer ), 0 );
  206. return szBuffer;
  207. }
  208. //==============================================================================
  209. // Given a linear address, locates the module, section, and offset containing
  210. // that address.
  211. //
  212. // Note: the szModule paramater buffer is an output buffer of length specified
  213. // by the len parameter (in characters!)
  214. //==============================================================================
  215. BOOL MSJExceptionHandler::GetLogicalAddress(
  216. PVOID addr, char* szModule, DWORD len, DWORD& section, DWORD& offset )
  217. {
  218. MEMORY_BASIC_INFORMATION mbi;
  219. if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) )
  220. return FALSE;
  221. DWORD hMod = (DWORD)mbi.AllocationBase;
  222. if ( !GetModuleFileNameA( (HMODULE)hMod, szModule, len ) )
  223. return FALSE;
  224. // Point to the DOS header in memory
  225. PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
  226. // From the DOS header, find the NT (PE) header
  227. PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
  228. PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
  229. DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address
  230. // Iterate through the section table, looking for the one that encompasses
  231. // the linear address.
  232. for ( unsigned i = 0;
  233. i < pNtHdr->FileHeader.NumberOfSections;
  234. i++, pSection++ )
  235. {
  236. DWORD sectionStart = pSection->VirtualAddress;
  237. DWORD sectionEnd = sectionStart
  238. + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
  239. // Is the address in this section???
  240. if ( (rva >= sectionStart) && (rva <= sectionEnd) )
  241. {
  242. // Yes, address is in the section. Calculate section and offset,
  243. // and store in the "section" & "offset" params, which were
  244. // passed by reference.
  245. section = i+1;
  246. offset = rva - sectionStart;
  247. return TRUE;
  248. }
  249. }
  250. return FALSE; // Should never get here!
  251. }
  252. //============================================================
  253. // Walks the stack, and writes the results to the report file
  254. //============================================================
  255. void MSJExceptionHandler::IntelStackWalk( PCONTEXT pContext )
  256. {
  257. _tprintf( "\nmanual stack frame walk begin:\n" );
  258. _tprintf( "Address Frame Logical addr Module\n" );
  259. DWORD pc = pContext->Eip;
  260. PDWORD pFrame, pPrevFrame;
  261. pFrame = (PDWORD)pContext->Ebp;
  262. for(INDEX iDepth=0; iDepth<100; iDepth++)
  263. {
  264. char szModule[MAX_PATH] = "";
  265. DWORD section = 0, offset = 0;
  266. _tprintf( "%08X %08X ", pc, pFrame);
  267. DWORD symDisplacement = 0; // Displacement of the input address,
  268. // relative to the start of the symbol
  269. GetLogicalAddress((PVOID)pc, szModule,sizeof(szModule),section,offset );
  270. _tprintf( "$adr: %s %04X:%08X", szModule, section, offset);
  271. _tprintf( "\n");
  272. pc = pFrame[1];
  273. pPrevFrame = pFrame;
  274. pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack
  275. if ( (DWORD)pFrame & 3 ) // Frame pointer must be aligned on a
  276. break; // DWORD boundary. Bail if not so.
  277. if ( pFrame <= pPrevFrame )
  278. break;
  279. // Can two DWORDs be read from the supposed frame address?
  280. if ( IsBadWritePtr(pFrame, sizeof(PVOID)*2) )
  281. break;
  282. };
  283. _tprintf( "\nmanual stack frame walk end:\n" );
  284. }
  285. //============================================================================
  286. // Helper function that writes to the report file, and allows the user to use
  287. // printf style formating
  288. //============================================================================
  289. int __cdecl MSJExceptionHandler::_tprintf(const char * format, ...)
  290. {
  291. char szBuff[1024];
  292. int retValue;
  293. DWORD cbWritten;
  294. va_list argptr;
  295. va_start( argptr, format );
  296. retValue = wvsprintfA( szBuff, format, argptr );
  297. va_end( argptr );
  298. WriteFile( m_hReportFile, szBuff, retValue * sizeof(char), &cbWritten, 0 );
  299. return retValue;
  300. }