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 ;
2004-02-25 21:08:10 +00:00
static void DoSave ();
2003-01-24 22:23:09 +00:00
///////////////////////////////////////////////////////////////////////////
#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 ;
2004-02-25 21:08:10 +00:00
#define BACKTRACE_MAX_SIZE 100
2003-09-01 02:47:24 +00:00
// 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 );
}
2004-02-25 21:08:10 +00:00
struct VDDebugInfoContext
{
VDDebugInfoContext () { pRVAHeap = NULL ; }
bool Loaded () const { return pRVAHeap != NULL ; }
2003-01-24 22:23:09 +00:00
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 },
};
2004-02-25 21:08:10 +00:00
static const char * LookupException ( DWORD code )
{
for ( int i = 0 ; exceptions [ i ]. code ; ++ i )
if ( exceptions [ i ]. code == code )
return exceptions [ i ]. name ;
return NULL ;
}
struct CrashInfo
{
char m_CrashReason [ 256 ];
const void * m_BacktracePointers [ BACKTRACE_MAX_SIZE ];
const void * m_AlternateThreadBacktrace [ BACKTRACE_MAX_SIZE ];
CrashInfo ()
{
m_CrashReason [ 0 ] = 0 ;
m_BacktracePointers [ 0 ] = m_AlternateThreadBacktrace [ 0 ] = NULL ;
}
};
static CrashInfo g_CrashInfo ;
static void GetReason ( const EXCEPTION_RECORD * pRecord , CrashInfo * crash )
{
// fill out bomb reason
const char * reason = LookupException ( pRecord -> ExceptionCode );
if ( reason == NULL )
wsprintf ( crash -> m_CrashReason , "Crash reason: unknown exception 0x%08lx" , pRecord -> ExceptionCode );
else
{
strcpy ( crash -> m_CrashReason , "Crash reason: " );
strcat ( crash -> m_CrashReason , reason );
}
}
2003-01-24 22:23:09 +00:00
long VDDebugInfoLookupRVA ( VDDebugInfoContext * pctx , unsigned rva , char * buf , int buflen );
bool VDDebugInfoInitFromMemory ( VDDebugInfoContext * pctx , const void * _src );
2004-02-25 21:08:10 +00:00
bool VDDebugInfoInitFromFile ( VDDebugInfoContext * pctx );
2003-01-24 22:23:09 +00:00
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-10-01 08:21:11 +00:00
RageThread :: HaltAllThreads ( false );
2003-01-24 22:23:09 +00:00
2004-01-14 22:18:27 +00:00
static int InHere = 0 ;
if ( InHere > 0 )
2003-01-24 22:23:09 +00:00
{
2004-01-14 22:18:27 +00:00
/* If we get here, then we've been called recursively, which means we crashed.
* If InHere is greater than 1, then we crashed after writing the crash dump;
* say so. */
2003-01-24 22:23:09 +00:00
SetUnhandledExceptionFilter ( NULL );
MessageBox ( NULL ,
2004-01-14 22:18:27 +00:00
InHere == 1 ?
"The error reporting interface has crashed. \n " :
"The error reporting interface has crashed. However, crashinfo.txt was"
"written successfully to the program directory. \n " ,
2003-09-01 02:47:24 +00:00
"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 ;
}
2004-01-14 22:18:27 +00:00
++ InHere ;
2003-01-24 22:23:09 +00:00
/////////////////////////
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.
2004-02-25 21:08:10 +00:00
VDDebugInfoInitFromFile ( & g_debugInfo );
2003-01-24 22:23:09 +00:00
/* In case something goes amiss before the user can view the crash
* dump, save it now. */
2004-02-25 21:08:10 +00:00
if ( ! g_CrashInfo . m_CrashReason [ 0 ] )
GetReason ( pExc -> ExceptionRecord , & g_CrashInfo );
do_backtrace ( g_CrashInfo . m_BacktracePointers , BACKTRACE_MAX_SIZE , GetCurrentProcess (), GetCurrentThread (), pExc -> ContextRecord );
DoSave ();
2003-01-24 22:23:09 +00:00
2004-01-14 22:18:27 +00:00
++ InHere ;
2004-01-12 01:37:00 +00:00
/* Now things get more risky. If we're fullscreen, the window will obscure the
2004-01-17 00:34:34 +00:00
* crash dialog. Try to hide the window. Things might blow up here; do this
* after DoSave, so we always write a crash dump. */
if ( GetWindowThreadProcessId ( g_hWndMain , NULL ) == GetCurrentThreadId () )
{
/* The thread that crashed was the thread that created the main window. Hide
* the window. This will also restore the video mode, if necessary. */
ShowWindow ( g_hWndMain , SW_HIDE );
} else {
/* A different thread crashed. Simply kill all other windows. We can't safely
* call ShowWindow; the main thread might be deadlocked. */
RageThread :: HaltAllThreads ( true );
ChangeDisplaySettings ( NULL , 0 );
}
2004-01-12 01:37:00 +00:00
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 );
2004-02-25 21:08:10 +00:00
DialogBoxParam ( handle , MAKEINTRESOURCE ( IDD_DISASM_CRASH ), NULL , CrashDlgProc , ( LPARAM ) pExc );
2003-01-24 22:23:09 +00:00
}
VDDebugInfoDeinit ( & g_debugInfo );
InHere = false ;
SetUnhandledExceptionFilter ( NULL );
2004-02-23 00:47:18 +00:00
/* Forcibly terminate; if we keep going, we'll try to shut down threads and do other
* things that may deadlock, which is confusing for users. */
TerminateProcess ( GetCurrentProcess (), 0 );
2003-01-24 22:23:09 +00:00
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 );
}
}
2004-02-25 21:08:10 +00:00
static void ReportReason ( HWND hwndReason , HANDLE hFile , const CrashInfo * pCrash )
2003-01-24 22:23:09 +00:00
{
2003-10-01 20:54:37 +00:00
if ( hwndReason )
2004-02-25 21:08:10 +00:00
SetWindowText ( hwndReason , pCrash -> m_CrashReason );
2003-10-01 20:54:37 +00:00
if ( hFile )
2004-02-25 21:08:10 +00:00
Report ( NULL , hFile , pCrash -> m_CrashReason );
2003-01-24 22:23:09 +00:00
}
2004-02-25 21:08:10 +00:00
static const char * GetNameFromHeap ( const char * heap , int idx )
{
2003-01-24 22:23:09 +00:00
while ( idx -- )
while ( * heap ++ );
return heap ;
}
//////////////////////////////////////////////////////////////////////////////
2004-02-25 21:08:10 +00:00
static bool IsValidCall ( char * buf , int len )
{
2003-01-24 22:23:09 +00:00
// 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 ;
}
}
2004-02-25 21:08:10 +00:00
bool VDDebugInfoInitFromFile ( VDDebugInfoContext * pctx )
2003-09-01 02:47:24 +00:00
{
2004-02-25 21:08:10 +00:00
if ( pctx -> Loaded () )
return true ;
char pszFilename [ 1024 ];
SpliceProgramPath ( pszFilename , sizeof ( pszFilename ), "StepMania.vdi" );
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 );
}
2004-02-25 21:08:10 +00:00
void do_backtrace ( const void ** buf , size_t size ,
2004-02-25 08:34:26 +00:00
HANDLE hProcess , HANDLE hThread , const CONTEXT * pContext )
2003-08-05 01:29:36 +00:00
{
2003-01-24 22:23:09 +00:00
// Retrieve stack pointers.
2004-02-25 08:34:26 +00:00
const char * pStackBase ;
{
LDT_ENTRY sel ;
2004-02-25 21:08:10 +00:00
if ( ! GetThreadSelectorEntry ( hThread , pContext -> SegFs , & sel ) )
{
buf [ 0 ] = NULL ;
return ;
}
2004-02-25 08:34:26 +00:00
const NT_TIB * tib = ( NT_TIB * ) (( sel . HighWord . Bits . BaseHi << 24 ) + ( sel . HighWord . Bits . BaseMid << 16 ) + sel . BaseLow );
const NT_TIB * pTib = tib -> Self ;
pStackBase = ( char * ) pTib -> StackBase ;
2003-01-24 22:23:09 +00:00
}
2004-02-25 08:34:26 +00:00
// Walk up the stack.
2003-10-01 20:54:37 +00:00
const char * lpAddr = ( const char * ) pContext -> Esp ;
2004-02-25 08:34:26 +00:00
const void * data = ( void * ) pContext -> Eip ;
size_t i = 0 ;
2003-01-24 22:23:09 +00:00
do {
2004-02-25 08:34:26 +00:00
if ( i + 1 >= size )
break ;
2003-01-24 22:23:09 +00:00
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 ;
2004-02-25 08:34:26 +00:00
if ( data != ( void * ) pContext -> Eip && ! PointsToValidCall (( unsigned long ) data ) )
2003-10-01 20:54:37 +00:00
fValid = false ;
2003-01-24 22:23:09 +00:00
2004-02-25 08:34:26 +00:00
if ( fValid )
buf [ i ++ ] = data ;
2003-01-24 22:23:09 +00:00
if ( lpAddr >= pStackBase )
break ;
lpAddr += 4 ;
2004-02-25 08:34:26 +00:00
} while ( ReadProcessMemory ( hProcess , lpAddr - 4 , & data , 4 , NULL ));
buf [ i ++ ] = NULL ;
}
2004-02-25 21:08:10 +00:00
void SymLookup ( const void * ptr , char * buf )
2004-02-25 08:34:26 +00:00
{
2004-02-25 21:08:10 +00:00
VDDebugInfoInitFromFile ( & g_debugInfo );
if ( ! g_debugInfo . Loaded () )
{
strcpy ( buf , "error" );
return ;
}
MEMORY_BASIC_INFORMATION meminfo ;
VirtualQuery ( ptr , & meminfo , sizeof meminfo );
char tmp [ 512 ];
if ( VDDebugInfoLookupRVA ( & g_debugInfo , ( unsigned int ) ptr , tmp , sizeof ( tmp )) >= 0 )
{
2004-04-05 05:34:38 +00:00
wsprintf ( buf , "%08x: %s" , ptr , Demangle ( tmp ) );
2004-02-25 21:08:10 +00:00
return ;
}
char szName [ MAX_PATH ];
if ( ! CrashGetModuleBaseName (( HMODULE ) meminfo . AllocationBase , szName ) )
strcpy ( szName , "???" );
DWORD64 disp ;
SYMBOL_INFO * pSymbol = GetSym ( ( unsigned int ) ptr , disp );
if ( pSymbol )
{
wsprintf ( buf , "%08lx: %s!%s [%08lx+%lx+%lx]" ,
( unsigned long ) ptr , szName , pSymbol -> Name ,
( unsigned long ) meminfo . AllocationBase ,
( unsigned long ) ( pSymbol -> Address ) - ( unsigned long ) ( meminfo . AllocationBase ),
( unsigned long ) disp );
return ;
}
wsprintf ( buf , "%08lx: %s!%08lx" ,
( unsigned long ) ptr , szName ,
( unsigned long ) meminfo . AllocationBase );
}
static bool ReportCallStack ( HWND hwnd , HANDLE hFile , const void ** Backtrace )
{
VDDebugInfoInitFromFile ( & g_debugInfo );
if ( ! g_debugInfo . Loaded () )
2004-02-25 08:34:26 +00:00
{
Report ( hwnd , hFile , "Could not open debug resource file (StepMania.vdi)." );
return false ;
}
if ( g_debugInfo . nBuildNumber != int ( version_num ) )
{
2004-02-25 21:08:10 +00:00
Report ( hwnd , hFile , "Incorrect StepMania.vdi file (build %d, expected %d) for this version of " PRODUCT_NAME " -- call stack unavailable." , g_debugInfo . nBuildNumber , int ( version_num ));
2004-02-25 08:34:26 +00:00
return false ;
}
2004-02-25 21:08:10 +00:00
for ( int i = 0 ; Backtrace [ i ]; ++ i )
2004-02-25 08:34:26 +00:00
{
2004-02-25 21:08:10 +00:00
char buf [ 10240 ];
SymLookup ( Backtrace [ i ], buf );
Report ( hwnd , hFile , "%s" , buf );
2004-02-25 08:34:26 +00:00
}
2003-01-24 22:23:09 +00:00
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 );
}
2004-02-25 21:08:10 +00:00
static void DoSave ()
2003-01-24 22:23:09 +00:00
{
char szModName2 [ MAX_PATH ];
2003-12-12 09:14:09 +00:00
SpliceProgramPath ( szModName2 , sizeof szModName2 , "../crashinfo.txt" );
2003-01-24 22:23:09 +00:00
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
2004-02-25 21:08:10 +00:00
ReportReason ( NULL , hFile , & g_CrashInfo );
2003-01-24 22:23:09 +00:00
Report ( NULL , hFile , "" );
// Dump thread stacks
2003-11-24 00:23:18 +00:00
WriteBuf ( hFile , Checkpoints :: GetLogs ( " \r\n " ) );
2003-01-24 22:23:09 +00:00
Report ( NULL , hFile , "" );
2004-02-25 21:08:10 +00:00
ReportCallStack ( NULL , hFile , g_CrashInfo . m_BacktracePointers );
2003-01-24 22:23:09 +00:00
Report ( NULL , hFile , "" );
2004-02-25 21:08:10 +00:00
if ( g_CrashInfo . m_AlternateThreadBacktrace [ 0 ] )
{
Report ( NULL , hFile , "Deadlocked with:" );
Report ( NULL , hFile , "" );
ReportCallStack ( NULL , hFile , g_CrashInfo . m_AlternateThreadBacktrace );
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
);
}
2004-02-25 21:08:10 +00:00
BOOL APIENTRY CrashDlgProc ( HWND hDlg , UINT msg , WPARAM wParam , LPARAM lParam )
{
2003-01-24 22:23:09 +00:00
static bool s_bHaveCallstack ;
2004-02-25 21:08:10 +00:00
switch ( msg )
{
case WM_INITDIALOG :
{
HWND hwndList = GetDlgItem ( hDlg , IDC_CALL_STACK );
HWND hwndReason = GetDlgItem ( hDlg , IDC_STATIC_BOMBREASON );
2003-01-24 22:23:09 +00:00
2004-02-25 21:08:10 +00:00
if ( hFontMono )
SendMessage ( hwndList , WM_SETFONT , ( WPARAM ) hFontMono , MAKELPARAM ( TRUE , 0 ));
2003-01-24 22:23:09 +00:00
2004-02-25 21:08:10 +00:00
ReportReason ( hwndReason , NULL , & g_CrashInfo );
s_bHaveCallstack = ReportCallStack ( hwndList , NULL , g_CrashInfo . m_BacktracePointers );
}
return TRUE ;
2003-01-24 22:23:09 +00:00
2004-02-25 21:08:10 +00:00
case WM_COMMAND :
switch ( LOWORD ( wParam )) {
case IDC_BUTTON_CLOSE :
EndDialog ( hDlg , FALSE );
2003-01-24 22:23:09 +00:00
return TRUE ;
2004-02-25 21:08:10 +00:00
case IDOK :
// EndDialog(hDlg, TRUE); /* don't always exit on ENTER */
return TRUE ;
case IDC_VIEW_LOG :
ViewWithNotepad ( "../log.txt" );
2003-01-24 22:23:09 +00:00
break ;
2004-02-25 21:08:10 +00:00
case IDC_CRASH_SAVE :
if ( ! s_bHaveCallstack )
if ( IDOK != MessageBox ( hDlg ,
PRODUCT_NAME " cannot load its crash resource file, and thus the crash dump will be "
"missing the most important part, the call stack. Crash dumps are much less useful "
"without the call stack." ,
PRODUCT_NAME " warning" , MB_OK | MB_ICONEXCLAMATION ))
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 ;
2003-01-24 22:23:09 +00:00
}
return FALSE ;
}
2004-02-25 21:08:10 +00:00
void NORETURN debug_crash ()
{
2003-01-24 22:23:09 +00:00
__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 ())) {
}
}
2004-02-25 21:08:10 +00:00
/* Get a stack trace of the current thread and the specified thread. */
void NORETURN Crash_BacktraceThread ( HANDLE hThread )
{
2004-02-25 23:14:28 +00:00
if ( hThread == NULL )
{
strcpy ( g_CrashInfo . m_CrashReason , "Crash reason: Thread deadlock (with NULL?)" );
debug_crash ();
}
#if 0
/* Ugh. GetThreadId is XP-only or 2003 Server-only or something. How can we do this? */
2004-02-25 21:08:10 +00:00
if( GetThreadId( hThread ) == GetCurrentThreadId() )
{
/* hThread is the current thread. This shouldn't happen. */
strcpy( g_CrashInfo.m_CrashReason, "Crash reason: Thread deadlock (with the current thread?)" );
debug_crash();
}
2004-02-25 23:14:28 +00:00
#endif
2004-02-25 21:08:10 +00:00
/* Suspend the other thread we're going to backtrace. (We need to at least suspend
* hThread, for GetThreadContext to work.) */
RageThread :: HaltAllThreads ( false );
// SuspendThread( hThread );
CONTEXT context ;
context . ContextFlags = CONTEXT_FULL ;
if ( ! GetThreadContext ( hThread , & context ) )
{
strcpy ( g_CrashInfo . m_CrashReason , "Crash reason: Thread deadlock (GetThreadContext failed)" );
debug_crash ();
}
static const void * BacktracePointers [ BACKTRACE_MAX_SIZE ];
do_backtrace ( g_CrashInfo . m_AlternateThreadBacktrace , BACKTRACE_MAX_SIZE , GetCurrentProcess (), hThread , & context );
strcpy ( g_CrashInfo . m_CrashReason , "Crash reason: Thread deadlock" );
debug_crash ();
}
2004-04-12 01:46:14 +00:00
void ForceCrashHandler ( const char * reason )
{
strncpy ( g_CrashInfo . m_CrashReason , reason , sizeof ( g_CrashInfo . m_CrashReason ) );
g_CrashInfo . m_CrashReason [ sizeof ( g_CrashInfo . m_CrashReason ) - 1 ] = 0 ;
debug_crash ();
}