2003-02-16 04:05:10 +00:00
#include "global.h"
2003-01-24 22:23:09 +00:00
// from VirtualDub
// Copyright (C) 1998-2001 Avery Lee
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// DO NOT USE stdio.h! printf() calls malloc()!
//#include <stdio.h>
#include <stdarg.h>
#include <crtdbg.h>
#include <windows.h>
#include <tlhelp32.h>
#include "resource.h"
#include "crash.h"
2003-08-19 02:30:10 +00:00
#include "ProductInfo.h"
2003-01-24 22:23:09 +00:00
2003-08-26 08:18:49 +00:00
#include "RageLog.h" /* for RageLog::GetAdditionalLog and Flush */
2003-08-05 01:29:36 +00:00
#include "RageThreads.h" /* for GetCheckpointLogs */
2003-10-16 09:00:20 +00:00
#include "PrefsManager.h" /* for g_bAutoRestart */
#include "RestartProgram.h"
2003-07-23 21:47:41 +00:00
2003-03-24 04:20:19 +00:00
#include "GotoURL.h"
2003-01-24 22:23:09 +00:00
2003-09-01 02:47:24 +00:00
static HFONT hFontMono = NULL ;
2003-01-24 22:23:09 +00:00
static void DoSave ( const EXCEPTION_POINTERS * pExc );
///////////////////////////////////////////////////////////////////////////
#define CODE_WINDOW (256)
///////////////////////////////////////////////////////////////////////////
extern HINSTANCE g_hInstance ;
extern unsigned long version_num ;
2003-09-01 02:47:24 +00:00
extern HINSTANCE g_hInstance ;
// WARNING: This is called from crash-time conditions! No malloc() or new!!!
#define malloc not_allowed_here
#define new not_allowed_here
static void SpliceProgramPath ( char * buf , int bufsiz , const char * fn ) {
char tbuf [ MAX_PATH ];
char * pszFile ;
GetModuleFileName ( NULL , tbuf , sizeof tbuf );
GetFullPathName ( tbuf , bufsiz , buf , & pszFile );
strcpy ( pszFile , fn );
}
2003-01-24 22:23:09 +00:00
struct VDDebugInfoContext {
void * pRawBlock ;
int nBuildNumber ;
const unsigned char * pRVAHeap ;
unsigned nFirstRVA ;
const char * pFuncNameHeap ;
const unsigned long ( * pSegments )[ 2 ];
int nSegments ;
};
static VDDebugInfoContext g_debugInfo ;
BOOL APIENTRY CrashDlgProc ( HWND hDlg , UINT msg , WPARAM wParam , LPARAM lParam );
///////////////////////////////////////////////////////////////////////////
static const struct ExceptionLookup {
DWORD code ;
const char * name ;
} exceptions [] = {
{ EXCEPTION_ACCESS_VIOLATION , "Access Violation" },
{ EXCEPTION_BREAKPOINT , "Breakpoint" },
{ EXCEPTION_FLT_DENORMAL_OPERAND , "FP Denormal Operand" },
{ EXCEPTION_FLT_DIVIDE_BY_ZERO , "FP Divide-by-Zero" },
{ EXCEPTION_FLT_INEXACT_RESULT , "FP Inexact Result" },
{ EXCEPTION_FLT_INVALID_OPERATION , "FP Invalid Operation" },
{ EXCEPTION_FLT_OVERFLOW , "FP Overflow" , },
{ EXCEPTION_FLT_STACK_CHECK , "FP Stack Check" , },
{ EXCEPTION_FLT_UNDERFLOW , "FP Underflow" , },
{ EXCEPTION_INT_DIVIDE_BY_ZERO , "Integer Divide-by-Zero" , },
{ EXCEPTION_INT_OVERFLOW , "Integer Overflow" , },
{ EXCEPTION_PRIV_INSTRUCTION , "Privileged Instruction" , },
{ EXCEPTION_ILLEGAL_INSTRUCTION , "Illegal instruction" },
{ EXCEPTION_INVALID_HANDLE , "Invalid handle" },
{ 0xe06d7363 , "Unhandled Microsoft C++ Exception" , },
// hmm... '_msc'... gee, who would have thought?
{ NULL },
};
long VDDebugInfoLookupRVA ( VDDebugInfoContext * pctx , unsigned rva , char * buf , int buflen );
bool VDDebugInfoInitFromMemory ( VDDebugInfoContext * pctx , const void * _src );
bool VDDebugInfoInitFromFile ( VDDebugInfoContext * pctx , const char * pszFilename );
void VDDebugInfoDeinit ( VDDebugInfoContext * pctx );
extern HWND g_hWndMain ;
2003-07-30 01:15:56 +00:00
long __stdcall CrashHandler ( EXCEPTION_POINTERS * pExc )
{
2003-08-26 08:18:49 +00:00
/* Flush the log it isn't cut off at the end. */
2003-09-05 19:44:10 +00:00
/* 1. We can't do regular file access in the crash handler.
* 2. We can't access LOG itself at all, since it may not be set up or the pointer might
* be munged. We must only ever use the RageLog:: methods that access static data, that
* we're being very careful to null-terminate as needed.
*
* Logs are rarely important, anyway. Only info.txt and crashinfo.txt are needed 99%
* of the time. */
// LOG->Flush();
2003-08-26 08:18:49 +00:00
2003-07-30 01:15:56 +00:00
/* We aren't supposed to receive these exceptions. For example, if you do
* a floating point divide by zero, you should receive a result of #INF. Only
* if the floating point exception for _EM_ZERODIVIDE is unmasked does this
* exception occur, and we never unmask it.
*
* However, once in a while some driver or library turns evil and unmasks an
* exception flag on us. If this happens, re-mask it and continue execution. */
switch ( pExc -> ExceptionRecord -> ExceptionCode )
{
case EXCEPTION_FLT_INVALID_OPERATION :
case EXCEPTION_FLT_DENORMAL_OPERAND :
case EXCEPTION_FLT_DIVIDE_BY_ZERO :
case EXCEPTION_FLT_OVERFLOW :
case EXCEPTION_FLT_UNDERFLOW :
case EXCEPTION_FLT_INEXACT_RESULT :
pExc -> ContextRecord -> FloatSave . ControlWord |= 0x3F ;
return EXCEPTION_CONTINUE_EXECUTION ;
}
2003-01-24 22:23:09 +00:00
/* If we're fullscreen, the fullscreen d3d window will obscure
* the crash dialog. Do this before we suspend threads, or it'll
* deadlock if the thread controlling this window has been stopped. */
/* XXX problem: If the sound thread crashes at the same time the main
* thread tells it to shut down, the main thread will be blocking waiting
* for the sound thread to finish; we'll come here and deadlock waiting for
* it to hide. Hmmmmm. */
ShowWindow ( g_hWndMain , SW_HIDE );
2003-10-01 08:21:11 +00:00
RageThread :: HaltAllThreads ( false );
2003-01-24 22:23:09 +00:00
static bool InHere = false ;
if ( InHere )
{
/* If we get here, then we've been called recursively, which means
2003-09-01 02:47:24 +00:00
* we crashed.
*/
2003-01-24 22:23:09 +00:00
SetUnhandledExceptionFilter ( NULL );
MessageBox ( NULL ,
2003-09-01 02:47:24 +00:00
"The error reporting interface has crashed. \n " ,
"Fatal Error" , MB_OK );
2003-02-16 04:07:39 +00:00
#ifdef DEBUG
2003-01-24 22:23:09 +00:00
DebugBreak ();
#endif
return EXCEPTION_EXECUTE_HANDLER ;
}
InHere = true ;
/////////////////////////
2003-09-01 02:47:24 +00:00
hFontMono = CreateFont (
10 , // nHeight
0 , // nWidth
0 , // nEscapement
0 , // nOrientation
FW_DONTCARE , // fnWeight
FALSE , // fdwItalic
FALSE , // fdwUnderline
FALSE , // fdwStrikeOut
ANSI_CHARSET , // fdwCharSet
OUT_DEFAULT_PRECIS , // fdwOutputPrecision
CLIP_DEFAULT_PRECIS , // fdwClipPrecision
DEFAULT_QUALITY , // fdwQuality
DEFAULT_PITCH | FF_DONTCARE , // fdwPitchAndFamily
"Lucida Console"
);
2003-01-24 22:23:09 +00:00
2003-09-01 02:47:24 +00:00
if ( ! hFontMono )
hFontMono = ( HFONT ) GetStockObject ( ANSI_FIXED_FONT );
2003-01-24 22:23:09 +00:00
// Attempt to read debug file.
2003-09-01 02:47:24 +00:00
char buf [ 1024 ];
SpliceProgramPath ( buf , sizeof buf , "StepMania.vdi" );
VDDebugInfoInitFromFile ( & g_debugInfo , buf );
2003-01-24 22:23:09 +00:00
/* In case something goes amiss before the user can view the crash
* dump, save it now. */
DoSave ( pExc );
2003-10-16 09:00:20 +00:00
if ( g_bAutoRestart )
Win32RestartProgram ();
2003-01-24 22:23:09 +00:00
/* Little trick to get an HINSTANCE of ourself without having access to the hwnd ... */
{
TCHAR szFullAppPath [ MAX_PATH ];
GetModuleFileName ( NULL , szFullAppPath , MAX_PATH );
HINSTANCE handle = LoadLibrary ( szFullAppPath );
2003-09-22 00:10:04 +00:00
DialogBoxParam ( handle , MAKEINTRESOURCE ( IDD_DISASM_CRASH ), NULL ,
2003-01-24 22:23:09 +00:00
CrashDlgProc , ( LPARAM ) pExc );
}
VDDebugInfoDeinit ( & g_debugInfo );
InHere = false ;
SetUnhandledExceptionFilter ( NULL );
return EXCEPTION_EXECUTE_HANDLER ;
}
static void Report ( HWND hwndList , HANDLE hFile , const char * format , ...) {
char buf [ 10240 ];
va_list val ;
int ch ;
va_start ( val , format );
ch = wvsprintf ( buf , format , val );
va_end ( val );
if ( hwndList )
SendMessage ( hwndList , LB_ADDSTRING , 0 , ( LPARAM ) buf );
if ( hFile ) {
DWORD dwActual ;
buf [ ch ] = '\r' ;
buf [ ch + 1 ] = '\n' ;
WriteFile ( hFile , buf , ch + 2 , & dwActual , NULL );
}
}
2003-10-01 20:54:37 +00:00
static void ReportReason ( HWND hwndReason , HANDLE hFile , const EXCEPTION_POINTERS * const pExc )
2003-01-24 22:23:09 +00:00
{
const EXCEPTION_RECORD * const pRecord = ( const EXCEPTION_RECORD * ) pExc -> ExceptionRecord ;
// fill out bomb reason
2003-03-24 04:20:19 +00:00
2003-01-24 22:23:09 +00:00
const struct ExceptionLookup * pel = exceptions ;
while ( pel -> code ) {
if ( pel -> code == pRecord -> ExceptionCode )
break ;
++ pel ;
}
// Unfortunately, EXCEPTION_ACCESS_VIOLATION doesn't seem to provide
// us with the read/write flag and virtual address as the docs say...
// *sigh*
2003-10-01 20:54:37 +00:00
char buf [ 256 ] = "" ;
2003-01-24 22:23:09 +00:00
2003-10-01 20:54:37 +00:00
if ( ! pel -> code )
wsprintf ( buf , "Crash reason: unknown exception 0x%08lx" , pRecord -> ExceptionCode );
else
{
strcpy ( buf , "Crash reason: " );
strcat ( buf , pel -> name );
2003-01-24 22:23:09 +00:00
}
2003-10-01 20:54:37 +00:00
if ( hwndReason )
SetWindowText ( hwndReason , buf );
if ( hFile )
Report ( NULL , hFile , buf );
2003-01-24 22:23:09 +00:00
}
static const char * GetNameFromHeap ( const char * heap , int idx ) {
while ( idx -- )
while ( * heap ++ );
return heap ;
}
//////////////////////////////////////////////////////////////////////////////
static bool IsValidCall ( char * buf , int len ) {
// Permissible CALL sequences that we care about:
//
// E8 xx xx xx xx CALL near relative
// FF (group 2) CALL near absolute indirect
//
// Minimum sequence is 2 bytes (call eax).
// Maximum sequence is 7 bytes (call dword ptr [eax+disp32]).
2003-08-03 02:47:43 +00:00
if ( len >= 5 && buf [ - 5 ] == '\xe8' )
2003-01-24 22:23:09 +00:00
return true ;
// FF 14 xx CALL [reg32+reg32*scale]
2003-08-03 02:47:43 +00:00
if ( len >= 3 && buf [ - 3 ] == '\xff' && buf [ - 2 ] == '\x14' )
2003-01-24 22:23:09 +00:00
return true ;
// FF 15 xx xx xx xx CALL disp32
2003-08-03 02:47:43 +00:00
if ( len >= 6 && buf [ - 6 ] == '\xff' && buf [ - 5 ] == '\x15' )
2003-01-24 22:23:09 +00:00
return true ;
// FF 00-3F(!14/15) CALL [reg32]
2003-08-03 02:47:43 +00:00
if ( len >= 2 && buf [ - 2 ] == '\xff' && ( unsigned char ) buf [ - 1 ] < '\x40' )
2003-01-24 22:23:09 +00:00
return true ;
// FF D0-D7 CALL reg32
2003-08-03 02:47:43 +00:00
if ( len >= 2 && buf [ - 2 ] == '\xff' && ( buf [ - 1 ] & 0xF8 ) == '\xd0' )
2003-01-24 22:23:09 +00:00
return true ;
// FF 50-57 xx CALL [reg32+reg32*scale+disp8]
2003-08-03 02:47:43 +00:00
if ( len >= 3 && buf [ - 3 ] == '\xff' && ( buf [ - 2 ] & 0xF8 ) == '\x50' )
2003-01-24 22:23:09 +00:00
return true ;
// FF 90-97 xx xx xx xx xx CALL [reg32+reg32*scale+disp32]
2003-08-03 02:47:43 +00:00
if ( len >= 7 && buf [ - 7 ] == '\xff' && ( buf [ - 6 ] & 0xF8 ) == '\x90' )
2003-01-24 22:23:09 +00:00
return true ;
return false ;
}
///////////////////////////////////////////////////////////////////////////
static bool IsExecutableProtection ( DWORD dwProtect ) {
MEMORY_BASIC_INFORMATION meminfo ;
// Windows NT/2000 allows Execute permissions, but Win9x seems to
// rip it off. So we query the permissions on our own code block,
// and use it to determine if READONLY/READWRITE should be
// considered 'executable.'
VirtualQuery ( IsExecutableProtection , & meminfo , sizeof meminfo );
switch (( unsigned char ) dwProtect ) {
case PAGE_READONLY : // *sigh* Win9x...
case PAGE_READWRITE : // *sigh*
return meminfo . Protect == PAGE_READONLY || meminfo . Protect == PAGE_READWRITE ;
case PAGE_EXECUTE :
case PAGE_EXECUTE_READ :
case PAGE_EXECUTE_READWRITE :
case PAGE_EXECUTE_WRITECOPY :
return true ;
}
return false ;
}
static const char * CrashGetModuleBaseName ( HMODULE hmod , char * pszBaseName ) {
char szPath1 [ MAX_PATH ];
char szPath2 [ MAX_PATH ];
__try {
DWORD dw ;
char * pszFile , * period = NULL ;
if ( ! GetModuleFileName ( hmod , szPath1 , sizeof szPath1 ))
return NULL ;
dw = GetFullPathName ( szPath1 , sizeof szPath2 , szPath2 , & pszFile );
if ( ! dw || dw > sizeof szPath2 )
return NULL ;
strcpy ( pszBaseName , pszFile );
pszFile = pszBaseName ;
while ( * pszFile ++ )
if ( pszFile [ - 1 ] == '.' )
period = pszFile - 1 ;
if ( period )
* period = 0 ;
} __except ( 1 ) {
return NULL ;
}
return pszBaseName ;
}
///////////////////////////////////////////////////////////////////////////
bool VDDebugInfoInitFromMemory ( VDDebugInfoContext * pctx , const void * _src ) {
const unsigned char * src = ( const unsigned char * ) _src ;
pctx -> pRVAHeap = NULL ;
2003-10-01 20:54:37 +00:00
if ( memcmp (( char * ) src , "StepMania symbolic debug information" , 36 ))
2003-01-24 22:23:09 +00:00
return false ;
// Extract fields
src += 64 ;
pctx -> nBuildNumber = * ( int * ) src ;
2003-10-01 20:54:37 +00:00
pctx -> pRVAHeap = ( const unsigned char * )( src + 20 );
pctx -> nFirstRVA = * ( const long * )( src + 16 );
pctx -> pFuncNameHeap = ( const char * ) pctx -> pRVAHeap - 4 + * ( const long * )( src + 4 );
pctx -> pSegments = ( unsigned long ( * )[ 2 ])( pctx -> pFuncNameHeap + * ( const long * )( src + 8 ));
pctx -> nSegments = * ( const long * )( src + 12 );
2003-01-24 22:23:09 +00:00
return true ;
}
void VDDebugInfoDeinit ( VDDebugInfoContext * pctx ) {
if ( pctx -> pRawBlock ) {
VirtualFree ( pctx -> pRawBlock , 0 , MEM_RELEASE );
pctx -> pRawBlock = NULL ;
}
}
2003-09-01 02:47:24 +00:00
bool VDDebugInfoInitFromFile ( VDDebugInfoContext * pctx , const char * pszFilename )
{
2003-01-24 22:23:09 +00:00
pctx -> pRawBlock = NULL ;
pctx -> pRVAHeap = NULL ;
HANDLE h = CreateFile ( pszFilename , GENERIC_READ , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL );
2003-10-01 08:21:11 +00:00
if ( INVALID_HANDLE_VALUE == h )
2003-01-24 22:23:09 +00:00
return false ;
do {
DWORD dwFileSize = GetFileSize ( h , NULL );
if ( dwFileSize == 0xFFFFFFFF )
break ;
pctx -> pRawBlock = VirtualAlloc ( NULL , dwFileSize , MEM_COMMIT , PAGE_READWRITE );
if ( ! pctx -> pRawBlock )
break ;
DWORD dwActual ;
if ( ! ReadFile ( h , pctx -> pRawBlock , dwFileSize , & dwActual , NULL ) || dwActual != dwFileSize )
break ;
if ( VDDebugInfoInitFromMemory ( pctx , pctx -> pRawBlock )) {
CloseHandle ( h );
return true ;
}
2003-03-24 04:20:19 +00:00
VirtualFree ( pctx -> pRawBlock , 0 , MEM_RELEASE );
2003-01-24 22:23:09 +00:00
} while ( false );
VDDebugInfoDeinit ( pctx );
CloseHandle ( h );
return false ;
}
2003-10-01 20:54:37 +00:00
static bool PointerIsInAnySegment ( const VDDebugInfoContext * pctx , unsigned rva )
{
for ( int i = 0 ; i < pctx -> nSegments ; ++ i )
{
2003-01-24 22:23:09 +00:00
if ( rva >= pctx -> pSegments [ i ][ 0 ] && rva < pctx -> pSegments [ i ][ 0 ] + pctx -> pSegments [ i ][ 1 ])
2003-10-01 20:54:37 +00:00
return true ;
2003-01-24 22:23:09 +00:00
}
2003-10-01 20:54:37 +00:00
return false ;
}
long VDDebugInfoLookupRVA ( VDDebugInfoContext * pctx , unsigned rva , char * buf , int buflen )
{
if ( ! PointerIsInAnySegment ( pctx , rva ) )
2003-01-24 22:23:09 +00:00
return - 1 ;
const unsigned char * pr = pctx -> pRVAHeap ;
2003-10-01 20:54:37 +00:00
const unsigned char * pr_limit = ( const unsigned char * ) pctx -> pFuncNameHeap ;
2003-01-24 22:23:09 +00:00
int idx = 0 ;
// Linearly unpack RVA deltas and find lower_bound
rva -= pctx -> nFirstRVA ;
if (( signed ) rva < 0 )
return - 1 ;
while ( pr < pr_limit ) {
unsigned char c ;
unsigned diff = 0 ;
do {
c = * pr ++ ;
diff = ( diff << 7 ) | ( c & 0x7f );
} while ( c & 0x80 );
rva -= diff ;
if (( signed ) rva < 0 ) {
rva += diff ;
break ;
}
++ idx ;
}
2003-10-01 20:54:37 +00:00
if ( pr >= pr_limit )
return - 1 ;
2003-01-24 22:23:09 +00:00
// Decompress name for RVA
2003-10-01 20:54:37 +00:00
const char * fn_name = GetNameFromHeap ( pctx -> pFuncNameHeap , idx );
2003-01-24 22:23:09 +00:00
2003-10-01 20:54:37 +00:00
if ( !* fn_name )
fn_name = "(special)" ;
2003-01-24 22:23:09 +00:00
2003-10-01 20:54:37 +00:00
strncpy ( buf , fn_name , buflen );
buf [ buflen - 1 ] = 0 ;
2003-01-24 22:23:09 +00:00
2003-10-01 20:54:37 +00:00
return rva ;
2003-01-24 22:23:09 +00:00
}
///////////////////////////////////////////////////////////////////////////
2003-09-22 00:10:04 +00:00
#include "ddk/dbghelp.h"
#pragma comment(lib, "ddk/dbghelp.lib")
2003-09-19 06:22:17 +00:00
2003-09-19 07:19:25 +00:00
static bool InitDbghelp ()
2003-09-19 06:22:17 +00:00
{
static bool initted = false ;
if ( ! initted )
{
SymSetOptions ( SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS );
if ( ! SymInitialize ( GetCurrentProcess (), NULL , TRUE ))
2003-09-19 07:19:25 +00:00
return false ;
2003-09-19 06:22:17 +00:00
initted = true ;
}
2003-09-19 07:19:25 +00:00
return true ;
}
2003-09-19 06:22:17 +00:00
2003-09-19 07:19:25 +00:00
static SYMBOL_INFO * GetSym ( unsigned long ptr , DWORD64 & disp )
{
InitDbghelp ();
static BYTE buffer [ 1024 ];
SYMBOL_INFO * pSymbol = ( PSYMBOL_INFO ) buffer ;
pSymbol -> SizeOfStruct = sizeof ( SYMBOL_INFO );
pSymbol -> MaxNameLen = sizeof ( buffer ) - sizeof ( SYMBOL_INFO ) + 1 ;
if ( ! SymFromAddr ( GetCurrentProcess (), ptr , & disp , pSymbol ))
return NULL ;
return pSymbol ;
}
static const char * Demangle ( const char * buf )
{
if ( ! InitDbghelp () )
return buf ;
static char obuf [ 1024 ];
2003-09-19 06:22:17 +00:00
if ( ! UnDecorateSymbolName ( buf , obuf , sizeof ( obuf ),
UNDNAME_COMPLETE
| UNDNAME_NO_CV_THISTYPE
| UNDNAME_NO_ALLOCATION_MODEL
| UNDNAME_NO_ACCESS_SPECIFIERS // no public:
| UNDNAME_NO_MS_KEYWORDS // no __cdecl
) )
{
return buf ;
}
2003-09-19 07:19:25 +00:00
if ( obuf [ 0 ] == '_' )
{
strcat ( obuf , "()" ); /* _main -> _main() */
return obuf + 1 ; /* _main -> main */
}
2003-09-19 06:22:17 +00:00
return obuf ;
}
2003-01-24 22:23:09 +00:00
2003-10-01 20:54:37 +00:00
static bool PointsToValidCall ( unsigned long ptr )
{
char buf [ 7 ];
int len = 7 ;
memset ( buf , 0 , sizeof ( buf ) );
while ( len > 0 && ! ReadProcessMemory ( GetCurrentProcess (), ( void * )( ptr - len ), buf + 7 - len , len , NULL ))
-- len ;
return IsValidCall ( buf + 7 , len );
}
2003-09-01 02:47:24 +00:00
static bool ReportCrashCallStack ( HWND hwnd , HANDLE hFile , const EXCEPTION_POINTERS * const pExc )
2003-08-05 01:29:36 +00:00
{
2003-01-24 22:23:09 +00:00
if ( ! g_debugInfo . pRVAHeap ) {
Report ( hwnd , hFile , "Could not open debug resource file (StepMania.vdi)." );
return false ;
}
if ( g_debugInfo . nBuildNumber != int ( version_num )) {
Report ( hwnd , hFile , "Incorrect StepMania.vdi file (build %d, expected %d) for this version of StepMania -- call stack unavailable." , g_debugInfo . nBuildNumber , int ( version_num ));
return false ;
}
// Retrieve stack pointers.
// Not sure if NtCurrentTeb() is available on Win95....
NT_TIB * pTib ;
__asm {
mov eax , fs :[ 0 ] _NT_TIB . Self
mov pTib , eax
}
char * pStackBase = ( char * ) pTib -> StackBase ;
2003-10-01 20:54:37 +00:00
const CONTEXT * const pContext = ( const CONTEXT * ) pExc -> ContextRecord ;
const HANDLE hprMe = GetCurrentProcess ();
const char * lpAddr = ( const char * ) pContext -> Esp ;
int limit = 100 ;
2003-01-24 22:23:09 +00:00
// Walk up the stack. Hopefully it wasn't fscked.
2003-10-01 20:54:37 +00:00
unsigned long data = pContext -> Eip ;
2003-01-24 22:23:09 +00:00
do {
bool fValid = true ;
MEMORY_BASIC_INFORMATION meminfo ;
VirtualQuery (( void * ) data , & meminfo , sizeof meminfo );
2003-10-01 20:54:37 +00:00
if ( ! IsExecutableProtection ( meminfo . Protect ) || meminfo . State != MEM_COMMIT )
2003-01-24 22:23:09 +00:00
fValid = false ;
2003-10-01 20:54:37 +00:00
if ( data != pContext -> Eip && ! PointsToValidCall ( data ) )
fValid = false ;
2003-01-24 22:23:09 +00:00
if ( fValid ) {
2003-10-01 20:54:37 +00:00
char buf [ 512 ];
2003-01-24 22:23:09 +00:00
if ( VDDebugInfoLookupRVA ( & g_debugInfo , data , buf , sizeof buf ) >= 0 ) {
2003-09-19 06:22:17 +00:00
Report ( hwnd , hFile , "%08lx: %s" , data , Demangle ( buf ));
2003-01-24 22:23:09 +00:00
-- limit ;
} else {
2003-09-19 07:19:25 +00:00
char szName [ MAX_PATH ];
if ( ! CrashGetModuleBaseName (( HMODULE ) meminfo . AllocationBase , szName ) )
strcpy ( szName , "???" );
DWORD64 disp ;
SYMBOL_INFO * pSymbol = GetSym ( data , disp );
if ( pSymbol )
{
Report ( hwnd , hFile , "%08lx: %s!%s [%08lx+%lx+%lx]" ,
( unsigned long ) data , szName , pSymbol -> Name ,
( unsigned long ) meminfo . AllocationBase ,
( unsigned long ) ( pSymbol -> Address ) - ( unsigned long ) ( meminfo . AllocationBase ),
( unsigned long ) disp );
} else {
Report ( hwnd , hFile , "%08lx: %s!%08lx" ,
( unsigned long ) data , szName ,
( unsigned long ) meminfo . AllocationBase );
}
2003-01-24 22:23:09 +00:00
-- limit ;
}
}
if ( lpAddr >= pStackBase )
break ;
lpAddr += 4 ;
} while ( limit > 0 && ReadProcessMemory ( hprMe , lpAddr - 4 , & data , 4 , NULL ));
return true ;
}
2003-09-01 19:49:49 +00:00
void WriteBuf ( HANDLE hFile , const char * buf )
{
DWORD dwActual ;
WriteFile ( hFile , buf , strlen ( buf ), & dwActual , NULL );
}
2003-09-01 02:47:24 +00:00
static void DoSave ( const EXCEPTION_POINTERS * pExc )
2003-01-24 22:23:09 +00:00
{
char szModName2 [ MAX_PATH ];
SpliceProgramPath ( szModName2 , sizeof szModName2 , "crashinfo.txt" );
2003-09-01 02:47:24 +00:00
HANDLE hFile = CreateFile ( szModName2 , GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL );
2003-01-24 22:23:09 +00:00
if ( INVALID_HANDLE_VALUE == hFile )
return ;
Report ( NULL , hFile ,
2003-08-19 02:30:10 +00:00
"%s crash report (build %d) \r\n "
2003-01-24 22:23:09 +00:00
"--------------------------------------"
2003-08-19 02:30:10 +00:00
" \r\n " , PRODUCT_NAME_VER , version_num );
2003-01-24 22:23:09 +00:00
2003-10-01 20:54:37 +00:00
ReportReason ( NULL , hFile , pExc );
2003-01-24 22:23:09 +00:00
Report ( NULL , hFile , "" );
// Dump thread stacks
2003-09-02 06:01:28 +00:00
WriteBuf ( hFile , GetCheckpointLogs ( " \r\n " ) );
2003-01-24 22:23:09 +00:00
Report ( NULL , hFile , "" );
2003-09-01 02:47:24 +00:00
ReportCrashCallStack ( NULL , hFile , pExc );
2003-01-24 22:23:09 +00:00
Report ( NULL , hFile , "" );
2003-09-01 02:47:24 +00:00
Report ( NULL , hFile , "Static log:" );
2003-09-01 19:49:49 +00:00
WriteBuf ( hFile , RageLog :: GetInfo () );
WriteBuf ( hFile , RageLog :: GetAdditionalLog () );
2003-01-24 22:23:09 +00:00
Report ( NULL , hFile , "" );
2003-09-01 02:47:24 +00:00
Report ( NULL , hFile , "Partial log:" );
int i = 0 ;
while ( const char * p = RageLog :: GetRecentLog ( i ++ ) )
Report ( NULL , hFile , "%s" , p );
2003-01-24 22:23:09 +00:00
Report ( NULL , hFile , "" );
Report ( NULL , hFile , "-- End of report" );
CloseHandle ( hFile );
}
void ViewWithNotepad ( const char * str )
{
char buf [ 256 ] = "" ;
strcat ( buf , "notepad.exe " );
strcat ( buf , str );
char cwd [ MAX_PATH ];
SpliceProgramPath ( cwd , MAX_PATH , "" );
PROCESS_INFORMATION pi ;
STARTUPINFO si ;
ZeroMemory ( & si , sizeof ( si ) );
CreateProcess (
NULL , // pointer to name of executable module
buf , // pointer to command line string
NULL , // process security attributes
NULL , // thread security attributes
false , // handle inheritance flag
0 , // creation flags
NULL , // pointer to new environment block
cwd , // pointer to current directory name
& si , // pointer to STARTUPINFO
& pi // pointer to PROCESS_INFORMATION
);
}
BOOL APIENTRY CrashDlgProc ( HWND hDlg , UINT msg , WPARAM wParam , LPARAM lParam ) {
static const EXCEPTION_POINTERS * s_pExc ;
static bool s_bHaveCallstack ;
switch ( msg ) {
case WM_INITDIALOG :
{
2003-09-01 02:47:24 +00:00
HWND hwndList = GetDlgItem ( hDlg , IDC_CALL_STACK );
2003-01-24 22:23:09 +00:00
HWND hwndReason = GetDlgItem ( hDlg , IDC_STATIC_BOMBREASON );
const EXCEPTION_POINTERS * const pExc = ( const EXCEPTION_POINTERS * ) lParam ;
s_pExc = pExc ;
2003-09-01 02:47:24 +00:00
if ( hFontMono )
SendMessage ( hwndList , WM_SETFONT , ( WPARAM ) hFontMono , MAKELPARAM ( TRUE , 0 ));
2003-01-24 22:23:09 +00:00
2003-10-01 20:54:37 +00:00
ReportReason ( hwndReason , NULL , pExc );
2003-09-01 02:47:24 +00:00
s_bHaveCallstack = ReportCrashCallStack ( hwndList , NULL , pExc );
2003-01-24 22:23:09 +00:00
}
return TRUE ;
case WM_COMMAND :
switch ( LOWORD ( wParam )) {
case IDC_BUTTON_CLOSE :
EndDialog ( hDlg , FALSE );
return TRUE ;
case IDOK :
EndDialog ( hDlg , TRUE );
return TRUE ;
case IDC_VIEW_LOG :
ViewWithNotepad ( "log.txt" );
break ;
case IDC_CRASH_SAVE :
if ( ! s_bHaveCallstack )
if ( IDOK != MessageBox ( hDlg ,
2003-08-19 02:30:10 +00:00
PRODUCT_NAME " cannot load its crash resource file, and thus the crash dump will be "
2003-01-24 22:23:09 +00:00
"missing the most important part, the call stack. Crash dumps are much less useful "
"without the call stack." ,
2003-08-19 02:30:10 +00:00
PRODUCT_NAME " warning" , MB_OK | MB_ICONEXCLAMATION ))
2003-01-24 22:23:09 +00:00
return TRUE ;
ViewWithNotepad ( "crashinfo.txt" );
return TRUE ;
case IDC_BUTTON_RESTART :
{
char cwd [ MAX_PATH ];
SpliceProgramPath ( cwd , MAX_PATH , "" );
TCHAR szFullAppPath [ MAX_PATH ];
GetModuleFileName ( NULL , szFullAppPath , MAX_PATH );
// Launch StepMania
PROCESS_INFORMATION pi ;
STARTUPINFO si ;
ZeroMemory ( & si , sizeof ( si ) );
CreateProcess (
NULL , // pointer to name of executable module
szFullAppPath , // pointer to command line string
NULL , // process security attributes
NULL , // thread security attributes
false , // handle inheritance flag
0 , // creation flags
NULL , // pointer to new environment block
cwd , // pointer to current directory name
& si , // pointer to STARTUPINFO
& pi // pointer to PROCESS_INFORMATION
);
}
EndDialog ( hDlg , FALSE );
break ;
case IDC_BUTTON_REPORT :
GotoURL ( "http://sourceforge.net/tracker/?func=add&group_id=37892&atid=421366" );
break ;
}
break ;
}
return FALSE ;
}
void crash () {
__try {
__asm xor ebx , ebx
__asm mov eax , dword ptr [ ebx ]
// __asm mov dword ptr [ebx],eax
// __asm lock add dword ptr cs:[00000000h], 12345678h
} __except ( CrashHandler (( EXCEPTION_POINTERS * ) _exception_info ())) {
}
}