2002-09-03 05:55:12 +00:00
# include "stdafx.h"
2002-08-27 22:02:30 +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"
# include "disasm.h"
# include "CrashList.h"
// GotoURL pulls in MFC stuff that we really shouldn't have here.
// #include "RageUtil.h" // for GotoURL
HINSTANCE GotoURL ( const char * url ) ;
2002-10-20 07:14:23 +00:00
static void DoSave ( const EXCEPTION_POINTERS * pExc ) ;
2002-08-27 22:02:30 +00:00
2002-10-20 03:53:45 +00:00
/* in StepMania.cpp */
void CleanupForRestart ( ) ;
2002-08-27 22:02:30 +00:00
///////////////////////////////////////////////////////////////////////////
# define CODE_WINDOW (256)
///////////////////////////////////////////////////////////////////////////
extern HINSTANCE g_hInstance ;
extern unsigned long version_num ;
static CodeDisassemblyWindow * g_pcdw ;
struct VDDebugInfoContext {
void * pRawBlock ;
int nBuildNumber ;
const unsigned char * pRVAHeap ;
unsigned nFirstRVA ;
const char * pClassNameHeap ;
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 void ReportCrashLog ( HWND hwnd , HANDLE hFile ) ;
///////////////////////////////////////////////////////////////////////////
# ifdef _DEBUG
/*
void checkfpustack(const char *file, const int line) throw() {
static const char szFPUProblemCaption[]="FPU/MMX internal problem";
static const char szFPUProblemMessage[]="The FPU stack wasn't empty! Tagword = %04x\nFile: %s, line %d";
static bool seenmsg=false;
char buf[128];
unsigned short tagword;
if (seenmsg)
return;
__asm fnstenv buf
tagword = *(unsigned short *)(buf + 8);
if (tagword != 0xffff) {
wsprintf(buf, szFPUProblemMessage, tagword, file, line);
MessageBox(NULL, buf, szFPUProblemCaption, MB_OK);
seenmsg=true;
}
}
static void __declspec(naked) *operator new(size_t bytes) {
static const char fname[]="stack trace";
__asm {
push ebp
mov ebp,esp
push [ebp+4] ;return address
push offset fname ;'filename'
push _NORMAL_BLOCK ;block type
push [ebp+8] ;allocation size
call _malloc_dbg
add esp,16
pop ebp
ret
}
}
*/
# endif
#if 0
void __declspec(naked) stackcheck(void *&sp) {
static const char g_szStackHemorrhage[]="WARNING: Thread is hemorrhaging stack space!\n";
__asm {
mov eax,[esp+4]
mov ecx,[eax]
or ecx,ecx
jnz started
mov [eax],esp
ret
started:
sub ecx,esp
mov eax,ecx
sar ecx,31
xor eax,ecx
sub eax,ecx
cmp eax,128
jb ok
push offset g_szStackHemorrhage
call dword ptr [OutputDebugString]
int 3
ok:
ret
}
}
#endif
///////////////////////////////////////////////////////////////////////////
//
// Nina's per-thread debug logs are really handy, so I back-ported
// them to 1.x. These are lightweight in comparison, however.
//
VirtualDubThreadState __declspec ( thread ) g_PerThreadState ;
__declspec ( thread )
struct {
ListNode node ;
VirtualDubThreadState * pState ;
} g_PerThreadStateNode ;
class VirtualDubThreadStateNode : public ListNode2 < VirtualDubThreadStateNode > {
public :
VirtualDubThreadState * pState ;
} ;
static CRITICAL_SECTION g_csPerThreadState ;
static List2 < VirtualDubThreadStateNode > g_listPerThreadState ;
static LONG g_nThreadsTrackedMinusOne = - 1 ;
void VirtualDubInitializeThread ( const char * pszName ) {
DWORD dwThreadId = GetCurrentThreadId ( ) ;
if ( ! InterlockedIncrement ( & g_nThreadsTrackedMinusOne ) ) {
InitializeCriticalSection ( & g_csPerThreadState ) ;
}
EnterCriticalSection ( & g_csPerThreadState ) ;
if ( DuplicateHandle ( GetCurrentProcess ( ) , GetCurrentThread ( ) , GetCurrentProcess ( ) , ( HANDLE * ) & g_PerThreadState . hThread , NULL , FALSE , DUPLICATE_SAME_ACCESS ) ) {
g_PerThreadState . pszThreadName = pszName ;
g_PerThreadState . dwThreadId = dwThreadId ;
g_PerThreadStateNode . pState = & g_PerThreadState ;
g_listPerThreadState . AddTail ( ( ListNode2 < VirtualDubThreadStateNode > * ) & g_PerThreadStateNode ) ;
}
LeaveCriticalSection ( & g_csPerThreadState ) ;
}
void VirtualDubDeinitializeThread ( ) {
EnterCriticalSection ( & g_csPerThreadState ) ;
( ( ListNode2 < VirtualDubThreadStateNode > * ) & g_PerThreadStateNode ) - > Remove ( ) ;
LeaveCriticalSection ( & g_csPerThreadState ) ;
if ( g_PerThreadState . pszThreadName )
CloseHandle ( ( HANDLE ) g_PerThreadState . hThread ) ;
InterlockedDecrement ( & g_nThreadsTrackedMinusOne ) ;
}
///////////////////////////////////////////////////////////////////////////
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 ) ;
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 ) ;
}
long CrashSymLookup ( VDDisassemblyContext * pctx , unsigned long virtAddr , char * buf , int buf_len ) {
if ( ! g_debugInfo . pRVAHeap )
return - 1 ;
return VDDebugInfoLookupRVA ( & g_debugInfo , virtAddr , buf , buf_len ) ;
}
2002-08-29 08:58:40 +00:00
static char szEmergencyDumpName [ MAX_PATH ] ;
static void DoSaveEmergencyDump ( const char * buf , int siz ) {
HANDLE hFile ;
SpliceProgramPath ( szEmergencyDumpName , sizeof szEmergencyDumpName , " crashdump.dat " ) ;
hFile = CreateFile ( szEmergencyDumpName , GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( INVALID_HANDLE_VALUE = = hFile )
return ;
DWORD dwActual ;
WriteFile ( hFile , buf , siz , & dwActual , NULL ) ;
FlushFileBuffers ( hFile ) ;
CloseHandle ( hFile ) ;
}
static void DoEraseEmergencyDump ( )
{
DeleteFile ( szEmergencyDumpName ) ;
}
2002-09-03 05:55:12 +00:00
# include "RageSound.h"
2002-10-20 07:14:23 +00:00
extern HWND g_hWndMain ;
2002-08-27 22:02:30 +00:00
long __stdcall CrashHandler ( EXCEPTION_POINTERS * pExc ) {
/////////////////////////
//
// QUICKLY: SUSPEND ALL THREADS THAT AREN'T US.
EnterCriticalSection ( & g_csPerThreadState ) ;
try {
DWORD dwCurrentThread = GetCurrentThreadId ( ) ;
for ( List2 < VirtualDubThreadStateNode > : : fwit it = g_listPerThreadState . begin ( ) ; it ; + + it ) {
const VirtualDubThreadState * pState = it - > pState ;
if ( pState - > dwThreadId & & pState - > dwThreadId ! = dwCurrentThread ) {
SuspendThread ( ( HANDLE ) pState - > hThread ) ;
}
}
} catch ( . . . ) {
}
LeaveCriticalSection ( & g_csPerThreadState ) ;
2002-08-29 08:58:40 +00:00
static bool InHere = false ;
if ( InHere )
{
/* If we get here, then we've been called recursively, which means
* we (the disassembler or related code) crashed. If this happens,
* we're not in very good shape. We've left the emergency dump;
* that's all we have to go by. Tell this to the user, and then die.
*
* We've already blown up twice, and this is our last line of defense.
* If this dies, we have nothing, so keep it simple. */
SetUnhandledExceptionFilter ( NULL ) ;
MessageBox ( NULL ,
" The error reporting interface has crashed. \n "
" Please report a bug and attach the file \" crashdump.dat \" \n "
" to the report. " , " Fatal Error " , MB_OK ) ;
# ifdef _DEBUG
DebugBreak ( ) ;
# endif
return EXCEPTION_EXECUTE_HANDLER ;
}
InHere = true ;
2002-08-27 22:02:30 +00:00
/////////////////////////
static char buf [ CODE_WINDOW + 16 ] ;
HANDLE hprMe = GetCurrentProcess ( ) ;
void * lpBaseAddress = pExc - > ExceptionRecord - > ExceptionAddress ;
char * lpAddr = ( char * ) ( ( long ) lpBaseAddress & - 32 ) ;
memset ( buf , 0 , sizeof buf ) ;
if ( ( unsigned long ) lpAddr > CODE_WINDOW / 2 )
lpAddr - = CODE_WINDOW / 2 ;
else
lpAddr = NULL ;
if ( ! ReadProcessMemory ( hprMe , lpAddr , buf , CODE_WINDOW , NULL ) ) {
int i ;
for ( i = 0 ; i < CODE_WINDOW ; i + = 32 )
if ( ! ReadProcessMemory ( hprMe , lpAddr + i , buf + i , 32 , NULL ) )
memset ( buf + i , 0 , 32 ) ;
}
2002-08-29 08:58:40 +00:00
DoSaveEmergencyDump ( buf , CODE_WINDOW ) ;
2002-08-27 22:02:30 +00:00
CodeDisassemblyWindow cdw ( buf , CODE_WINDOW , ( char * ) ( buf - lpAddr ) , lpAddr ) ;
g_pcdw = & cdw ;
cdw . setFaultAddress ( lpBaseAddress ) ;
// Attempt to read debug file.
2002-08-27 23:16:52 +00:00
bool bSuccess = 0 ;
2002-08-27 22:02:30 +00:00
if ( cdw . vdc . pExtraData ) {
bSuccess = VDDebugInfoInitFromMemory ( & g_debugInfo , cdw . vdc . pExtraData ) ;
} else {
SpliceProgramPath ( buf , sizeof buf , " StepMania.vdi " ) ;
bSuccess = VDDebugInfoInitFromFile ( & g_debugInfo , buf ) ;
if ( ! bSuccess ) {
SpliceProgramPath ( buf , sizeof buf , " StepMani.vdi " ) ;
bSuccess = VDDebugInfoInitFromFile ( & g_debugInfo , buf ) ;
}
}
cdw . vdc . pSymLookup = CrashSymLookup ;
cdw . parse ( ) ;
2002-10-20 07:14:23 +00:00
/* In case something goes amiss before the user can view the crash
* dump, save it now. */
DoSave ( pExc ) ;
/* If we're fullscreen, the fullscreen d3d window will obscure
* the crash dialog. */
ShowWindow ( g_hWndMain , SW_HIDE ) ;
2002-08-27 22:02:30 +00:00
int ret = DialogBoxParam ( g_hInstance , MAKEINTRESOURCE ( IDD_DISASM_CRASH ) , NULL , CrashDlgProc , ( LPARAM ) pExc ) ;
/* Returns TRUE if we want to pop up the standard crash box;
* FALSE if we should just die. */
VDDebugInfoDeinit ( & g_debugInfo ) ;
2002-08-28 07:39:17 +00:00
/* Major hack:
* Bass crashes if we close without freeing it, even if it's by
* crashing ourselves. Now, it's far from safe to call these functions
* after a crash, but if we don't it'll crash anyway, so we have
* nothing to lose. */
2002-09-03 05:55:12 +00:00
delete SOUND ;
SOUND = NULL ;
2002-08-28 07:39:17 +00:00
2002-08-29 08:58:40 +00:00
/* We've made it. Delete the emergency dump. */
DoEraseEmergencyDump ( ) ;
InHere = false ;
SetUnhandledExceptionFilter ( NULL ) ;
2002-08-27 22:02:30 +00:00
if ( ret )
UnhandledExceptionFilter ( pExc ) ;
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 ) ;
}
}
static void SetWindowTitlef ( HWND hwnd , const char * format , . . . ) {
char buf [ 256 ] ;
va_list val ;
va_start ( val , format ) ;
wvsprintf ( buf , format , val ) ;
va_end ( val ) ;
SetWindowText ( hwnd , buf ) ;
}
2002-09-12 22:56:02 +00:00
static void ReportReason ( HWND hwnd , HWND hwndReason , HANDLE hFile , const EXCEPTION_POINTERS * const pExc )
{
2002-08-27 22:02:30 +00:00
const EXCEPTION_RECORD * const pRecord = ( const EXCEPTION_RECORD * ) pExc - > ExceptionRecord ;
// fill out bomb reason
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*
if ( ! pel - > code ) {
if ( hwndReason )
SetWindowTitlef ( hwndReason , " Crash reason: unknown exception 0x%08lx " , pRecord - > ExceptionCode ) ;
if ( hFile )
2002-09-12 22:56:02 +00:00
Report ( hwnd , hFile , " Crash reason: unknown exception 0x%08lx " , pRecord - > ExceptionCode ) ;
2002-08-27 22:02:30 +00:00
} else {
if ( hwndReason )
SetWindowTitlef ( hwndReason , " Crash reason: %s " , pel - > name ) ;
if ( hFile )
2002-09-12 22:56:02 +00:00
Report ( hwnd , hFile , " Crash reason: %s " , pel - > name ) ;
2002-08-27 22:02:30 +00:00
}
2002-09-12 22:56:02 +00:00
}
2002-08-27 22:02:30 +00:00
2002-09-12 22:56:02 +00:00
static void ReportThreadStacks ( HWND hwnd , HANDLE hFile , const EXCEPTION_POINTERS * const pExc )
{
2002-08-27 22:02:30 +00:00
EnterCriticalSection ( & g_csPerThreadState ) ;
try {
for ( List2 < VirtualDubThreadStateNode > : : fwit it = g_listPerThreadState . begin ( ) ; it ; + + it ) {
const VirtualDubThreadState * pState = it - > pState ;
2002-09-12 22:56:02 +00:00
Report ( hwnd , hFile , " Thread %08lx (%s) " , pState - > dwThreadId , pState - > pszThreadName ? pState - > pszThreadName : " unknown " ) ;
2002-08-27 22:02:30 +00:00
for ( int i = 0 ; i < CHECKPOINT_COUNT ; + + i ) {
const VirtualDubCheckpoint & cp = pState - > cp [ ( pState - > nNextCP + i ) & ( CHECKPOINT_COUNT - 1 ) ] ;
2002-09-07 20:59:16 +00:00
if ( cp . file ) {
2002-09-12 22:56:02 +00:00
Report ( hwnd , hFile , " \t %s:%d %s " , cp . file , cp . line , cp . message ? cp . message : " " ) ;
2002-09-07 20:59:16 +00:00
}
2002-08-27 22:02:30 +00:00
}
}
} catch ( . . . ) {
}
LeaveCriticalSection ( & g_csPerThreadState ) ;
2002-09-12 22:56:02 +00:00
}
2002-08-27 22:02:30 +00:00
2002-09-12 22:56:02 +00:00
static void ReportCrashData ( HWND hwnd , HANDLE hFile , const EXCEPTION_POINTERS * const pExc ) {
2002-10-24 07:05:15 +00:00
// const EXCEPTION_RECORD *const pRecord = (const EXCEPTION_RECORD *)pExc->ExceptionRecord;
2002-09-12 22:56:02 +00:00
const CONTEXT * const pContext = ( const CONTEXT * ) pExc - > ContextRecord ;
int i , tos ;
Report ( hwnd , hFile , " EAX = %08lx " , pContext - > Eax ) ;
Report ( hwnd , hFile , " EBX = %08lx " , pContext - > Ebx ) ;
Report ( hwnd , hFile , " ECX = %08lx " , pContext - > Ecx ) ;
Report ( hwnd , hFile , " EDX = %08lx " , pContext - > Edx ) ;
Report ( hwnd , hFile , " EBP = %08lx " , pContext - > Ebp ) ;
Report ( hwnd , hFile , " DS:ESI = %04x:%08lx " , pContext - > SegDs , pContext - > Esi ) ;
Report ( hwnd , hFile , " ES:EDI = %04x:%08lx " , pContext - > SegEs , pContext - > Edi ) ;
Report ( hwnd , hFile , " SS:ESP = %04x:%08lx " , pContext - > SegSs , pContext - > Esp ) ;
Report ( hwnd , hFile , " CS:EIP = %04x:%08lx " , pContext - > SegCs , pContext - > Eip ) ;
Report ( hwnd , hFile , " FS = %04x " , pContext - > SegFs ) ;
Report ( hwnd , hFile , " GS = %04x " , pContext - > SegGs ) ;
Report ( hwnd , hFile , " EFLAGS = %08lx " , pContext - > EFlags ) ;
Report ( hwnd , hFile , " " ) ;
// extract out MMX registers
tos = ( pContext - > FloatSave . StatusWord & 0x3800 ) > > 11 ;
for ( i = 0 ; i < 8 ; i + + ) {
long * pReg = ( long * ) ( pContext - > FloatSave . RegisterArea + 10 * ( ( i - tos ) & 7 ) ) ;
Report ( hwnd , hFile , " MM%c = %08lx%08lx " , i + ' 0 ' , pReg [ 1 ] , pReg [ 0 ] ) ;
}
2002-08-27 22:02:30 +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]).
if ( len > = 5 & & buf [ - 5 ] = = ( char ) 0xE8 )
return true ;
// FF 14 xx CALL [reg32+reg32*scale]
if ( len > = 3 & & buf [ - 3 ] = = ( char ) 0xFF & & buf [ - 2 ] = = 0x14 )
return true ;
// FF 15 xx xx xx xx CALL disp32
if ( len > = 6 & & buf [ - 6 ] = = ( char ) 0xFF & & buf [ - 5 ] = = 0x15 )
return true ;
// FF 00-3F(!14/15) CALL [reg32]
if ( len > = 2 & & buf [ - 2 ] = = ( char ) 0xFF & & ( unsigned char ) buf [ - 1 ] < 0x40 )
return true ;
// FF D0-D7 CALL reg32
if ( len > = 2 & & buf [ - 2 ] = = ( char ) 0xFF & & ( buf [ - 1 ] & 0xF8 ) = = 0xD0 )
return true ;
// FF 50-57 xx CALL [reg32+reg32*scale+disp8]
if ( len > = 3 & & buf [ - 3 ] = = ( char ) 0xFF & & ( buf [ - 2 ] & 0xF8 ) = = 0x50 )
return true ;
// FF 90-97 xx xx xx xx xx CALL [reg32+reg32*scale+disp32]
if ( len > = 7 & & buf [ - 7 ] = = ( char ) 0xFF & & ( buf [ - 6 ] & 0xF8 ) = = 0x90 )
return true ;
return false ;
}
//////////////////////////////////////////////////////////////////////////////
struct ModuleInfo {
const char * name ;
unsigned long base , size ;
} ;
// ARRGH. Where's psapi.h?!?
struct Win32ModuleInfo {
DWORD base , size , entry ;
} ;
typedef BOOL ( __stdcall * PENUMPROCESSMODULES ) ( HANDLE , HMODULE * , DWORD , LPDWORD ) ;
typedef DWORD ( __stdcall * PGETMODULEBASENAME ) ( HANDLE , HMODULE , LPTSTR , DWORD ) ;
typedef BOOL ( __stdcall * PGETMODULEINFORMATION ) ( HANDLE , HMODULE , Win32ModuleInfo * , DWORD ) ;
typedef HANDLE ( __stdcall * PCREATETOOLHELP32SNAPSHOT ) ( DWORD , DWORD ) ;
typedef BOOL ( WINAPI * PMODULE32FIRST ) ( HANDLE , LPMODULEENTRY32 ) ;
typedef BOOL ( WINAPI * PMODULE32NEXT ) ( HANDLE , LPMODULEENTRY32 ) ;
static ModuleInfo * CrashGetModules ( void * & ptr ) {
void * pMem = VirtualAlloc ( NULL , 65536 , MEM_COMMIT , PAGE_READWRITE ) ;
if ( ! pMem ) {
ptr = NULL ;
return NULL ;
}
// This sucks. If we're running under Windows 9x, we must use
// TOOLHELP.DLL to get the module list. Under Windows NT, we must
// use PSAPI.DLL. With Windows 2000, we can use both (but prefer
// PSAPI.DLL).
HMODULE hmodPSAPI = LoadLibrary ( " psapi.dll " ) ;
if ( hmodPSAPI ) {
// Using PSAPI.DLL. Call EnumProcessModules(), then GetModuleFileNameEx()
// and GetModuleInformation().
PENUMPROCESSMODULES pEnumProcessModules = ( PENUMPROCESSMODULES ) GetProcAddress ( hmodPSAPI , " EnumProcessModules " ) ;
PGETMODULEBASENAME pGetModuleBaseName = ( PGETMODULEBASENAME ) GetProcAddress ( hmodPSAPI , " GetModuleBaseNameA " ) ;
PGETMODULEINFORMATION pGetModuleInformation = ( PGETMODULEINFORMATION ) GetProcAddress ( hmodPSAPI , " GetModuleInformation " ) ;
HMODULE * pModules , * pModules0 = ( HMODULE * ) ( ( char * ) pMem + 0xF000 ) ;
DWORD cbNeeded ;
if ( pEnumProcessModules & & pGetModuleBaseName & & pGetModuleInformation
& & pEnumProcessModules ( GetCurrentProcess ( ) , pModules0 , 0x1000 , & cbNeeded ) ) {
ModuleInfo * pMod , * pMod0 ;
char * pszHeap = ( char * ) pMem , * pszHeapLimit ;
if ( cbNeeded > 0x1000 ) cbNeeded = 0x1000 ;
pModules = ( HMODULE * ) ( ( char * ) pMem + 0x10000 - cbNeeded ) ;
memmove ( pModules , pModules0 , cbNeeded ) ;
pMod = pMod0 = ( ModuleInfo * ) ( ( char * ) pMem + 0x10000 - sizeof ( ModuleInfo ) * ( cbNeeded / sizeof ( HMODULE ) + 1 ) ) ;
pszHeapLimit = ( char * ) pMod ;
do {
HMODULE hCurMod = * pModules + + ;
Win32ModuleInfo mi ;
if ( pGetModuleBaseName ( GetCurrentProcess ( ) , hCurMod , pszHeap , pszHeapLimit - pszHeap )
& & pGetModuleInformation ( GetCurrentProcess ( ) , hCurMod , & mi , sizeof mi ) ) {
char * period = NULL ;
pMod - > name = pszHeap ;
while ( * pszHeap + + )
if ( pszHeap [ - 1 ] = = ' . ' )
period = pszHeap - 1 ;
if ( period ) {
* period = 0 ;
pszHeap = period + 1 ;
}
pMod - > base = mi . base ;
pMod - > size = mi . size ;
+ + pMod ;
}
} while ( ( cbNeeded - = sizeof ( HMODULE * ) ) > 0 ) ;
pMod - > name = NULL ;
FreeLibrary ( hmodPSAPI ) ;
ptr = pMem ;
return pMod0 ;
}
FreeLibrary ( hmodPSAPI ) ;
} else {
// No PSAPI. Use the ToolHelp functions in KERNEL.
HMODULE hmodKERNEL32 = LoadLibrary ( " kernel32.dll " ) ;
PCREATETOOLHELP32SNAPSHOT pCreateToolhelp32Snapshot = ( PCREATETOOLHELP32SNAPSHOT ) GetProcAddress ( hmodKERNEL32 , " CreateToolhelp32Snapshot " ) ;
PMODULE32FIRST pModule32First = ( PMODULE32FIRST ) GetProcAddress ( hmodKERNEL32 , " Module32First " ) ;
PMODULE32NEXT pModule32Next = ( PMODULE32NEXT ) GetProcAddress ( hmodKERNEL32 , " Module32Next " ) ;
HANDLE hSnap ;
if ( pCreateToolhelp32Snapshot & & pModule32First & & pModule32Next ) {
if ( ( HANDLE ) - 1 ! = ( hSnap = pCreateToolhelp32Snapshot ( TH32CS_SNAPMODULE , 0 ) ) ) {
ModuleInfo * pModInfo = ( ModuleInfo * ) ( ( char * ) pMem + 65536 ) ;
char * pszHeap = ( char * ) pMem ;
MODULEENTRY32 me ;
- - pModInfo ;
pModInfo - > name = NULL ;
me . dwSize = sizeof ( MODULEENTRY32 ) ;
if ( pModule32First ( hSnap , & me ) )
do {
if ( pszHeap + strlen ( me . szModule ) > = ( char * ) ( pModInfo - 1 ) )
break ;
strcpy ( pszHeap , me . szModule ) ;
- - pModInfo ;
pModInfo - > name = pszHeap ;
char * period = NULL ;
while ( * pszHeap + + ) ;
if ( pszHeap [ - 1 ] = = ' . ' )
period = pszHeap - 1 ;
if ( period ) {
* period = 0 ;
pszHeap = period + 1 ;
}
pModInfo - > base = ( unsigned long ) me . modBaseAddr ;
pModInfo - > size = me . modBaseSize ;
} while ( pModule32Next ( hSnap , & me ) ) ;
CloseHandle ( hSnap ) ;
FreeLibrary ( hmodKERNEL32 ) ;
ptr = pMem ;
return pModInfo ;
}
}
FreeLibrary ( hmodKERNEL32 ) ;
}
VirtualFree ( pMem , 0 , MEM_RELEASE ) ;
ptr = NULL ;
return NULL ;
}
///////////////////////////////////////////////////////////////////////////
//
// info from Portable Executable/Common Object File Format (PE/COFF) spec
typedef unsigned short ushort ;
typedef unsigned long ulong ;
struct PEHeader {
ulong signature ;
ushort machine ;
ushort sections ;
ulong timestamp ;
ulong symbol_table ;
ulong symbols ;
ushort opthdr_size ;
ushort characteristics ;
} ;
struct PESectionHeader {
char name [ 8 ] ;
ulong virtsize ;
ulong virtaddr ;
ulong rawsize ;
ulong rawptr ;
ulong relocptr ;
ulong linenoptr ;
ushort reloc_cnt ;
ushort lineno_cnt ;
ulong characteristics ;
} ;
struct PEExportDirectory {
ulong flags ;
ulong timestamp ;
ushort major ;
ushort minor ;
ulong nameptr ;
ulong ordbase ;
ulong addrtbl_cnt ;
ulong nametbl_cnt ;
ulong addrtbl_ptr ;
ulong nametbl_ptr ;
ulong ordtbl_ptr ;
} ;
struct PE32OptionalHeader {
ushort magic ; // 0
char major_linker_ver ; // 2
char minor_linker_ver ; // 3
ulong codesize ; // 4
ulong idatasize ; // 8
ulong udatasize ; // 12
ulong entrypoint ; // 16
ulong codebase ; // 20
ulong database ; // 24
ulong imagebase ; // 28
ulong section_align ; // 32
ulong file_align ; // 36
ushort majoros ; // 40
ushort minoros ; // 42
ushort majorimage ; // 44
ushort minorimage ; // 46
ushort majorsubsys ; // 48
ushort minorsubsys ; // 50
ulong reserved ; // 52
ulong imagesize ; // 56
ulong hdrsize ; // 60
ulong checksum ; // 64
ushort subsystem ; // 68
ushort characteristics ; // 70
ulong stackreserve ; // 72
ulong stackcommit ; // 76
ulong heapreserve ; // 80
ulong heapcommit ; // 84
ulong loaderflags ; // 88
ulong dictentries ; // 92
// Not part of header, but it's convienent here
ulong export_RVA ; // 96
ulong export_size ; // 100
} ;
struct PE32PlusOptionalHeader {
ushort magic ; // 0
char major_linker_ver ; // 2
char minor_linker_ver ; // 3
ulong codesize ; // 4
ulong idatasize ; // 8
ulong udatasize ; // 12
ulong entrypoint ; // 16
ulong codebase ; // 20
__int64 imagebase ; // 24
ulong section_align ; // 32
ulong file_align ; // 36
ushort majoros ; // 40
ushort minoros ; // 42
ushort majorimage ; // 44
ushort minorimage ; // 46
ushort majorsubsys ; // 48
ushort minorsubsys ; // 50
ulong reserved ; // 52
ulong imagesize ; // 56
ulong hdrsize ; // 60
ulong checksum ; // 64
ushort subsystem ; // 68
ushort characteristics ; // 70
__int64 stackreserve ; // 72
__int64 stackcommit ; // 80
__int64 heapreserve ; // 88
__int64 heapcommit ; // 96
ulong loaderflags ; // 104
ulong dictentries ; // 108
// Not part of header, but it's convienent here
ulong export_RVA ; // 112
ulong export_size ; // 116
} ;
static const char * CrashLookupExport ( HMODULE hmod , unsigned long addr , unsigned long & fnbase ) {
char * pBase = ( char * ) hmod ;
// The PEheader offset is at hmod+0x3c. Add the size of the optional header
// to step to the section headers.
PEHeader * pHeader = ( PEHeader * ) ( pBase + ( ( long * ) hmod ) [ 15 ] ) ;
if ( pHeader - > signature ! = ' EP ' )
return NULL ;
#if 0
PESectionHeader *pSHdrs = (PESectionHeader *)((char *)pHeader + sizeof(PEHeader) + pHeader->opthdr_size);
// Scan down the section headers and look for ".edata"
int i;
for(i=0; i<pHeader->sections; i++) {
MessageBox(NULL, pSHdrs[i].name, "section", MB_OK);
if (!memcmp(pSHdrs[i].name, ".edata", 6))
break;
}
if (i >= pHeader->sections)
return NULL;
#endif
// Verify the optional structure.
PEExportDirectory * pExportDir ;
if ( pHeader - > opthdr_size < 104 )
return NULL ;
switch ( * ( short * ) ( ( char * ) pHeader + sizeof ( PEHeader ) ) ) {
case 0x10b : // PE32
{
PE32OptionalHeader * pOpt = ( PE32OptionalHeader * ) ( ( char * ) pHeader + sizeof ( PEHeader ) ) ;
if ( pOpt - > dictentries < 1 )
return NULL ;
pExportDir = ( PEExportDirectory * ) ( pBase + pOpt - > export_RVA ) ;
}
break ;
case 0x20b : // PE32+
{
PE32PlusOptionalHeader * pOpt = ( PE32PlusOptionalHeader * ) ( ( char * ) pHeader + sizeof ( PEHeader ) ) ;
if ( pOpt - > dictentries < 1 )
return NULL ;
pExportDir = ( PEExportDirectory * ) ( pBase + pOpt - > export_RVA ) ;
}
break ;
default :
return NULL ;
}
// Hmmm... no exports?
if ( ( char * ) pExportDir = = pBase )
return NULL ;
// Find the location of the export information.
ulong * pNameTbl = ( ulong * ) ( pBase + pExportDir - > nametbl_ptr ) ;
ulong * pAddrTbl = ( ulong * ) ( pBase + pExportDir - > addrtbl_ptr ) ;
ushort * pOrdTbl = ( ushort * ) ( pBase + pExportDir - > ordtbl_ptr ) ;
// Scan exports.
const char * pszName = NULL ;
ulong bestdelta = 0xFFFFFFFF ;
unsigned i ;
addr - = ( ulong ) pBase ;
for ( i = 0 ; i < pExportDir - > nametbl_cnt ; i + + ) {
ulong fnaddr ;
int idx ;
idx = pOrdTbl [ i ] ;
fnaddr = pAddrTbl [ idx ] ;
if ( addr > = fnaddr ) {
ulong delta = addr - fnaddr ;
if ( delta < bestdelta ) {
bestdelta = delta ;
fnbase = fnaddr ;
if ( pNameTbl [ i ] )
pszName = pBase + pNameTbl [ i ] ;
else {
static char buf [ 8 ] ;
wsprintf ( buf , " ord%d " , pOrdTbl [ i ] ) ;
pszName = buf ;
}
}
}
}
return pszName ;
}
///////////////////////////////////////////////////////////////////////////
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 ;
// Check type string
2002-08-27 23:16:52 +00:00
if ( ! memcmp ( ( char * ) src + 6 , " ] StepMania disasm " , 18 ) )
2002-08-27 22:02:30 +00:00
src + = * ( long * ) ( src + 64 ) + 72 ;
if ( src [ 0 ] ! = ' [ ' | | src [ 3 ] ! = ' | ' )
return false ;
2002-08-27 23:16:52 +00:00
if ( memcmp ( ( char * ) src + 6 , " ] StepMania symbolic debug information " , 38 ) )
2002-08-27 22:02:30 +00:00
return false ;
// Check version number
2002-10-24 07:05:15 +00:00
// int write_version = (src[1]-'0')*10 + (src[2] - '0');
2002-08-27 22:02:30 +00:00
int compat_version = ( src [ 4 ] - ' 0 ' ) * 10 + ( src [ 5 ] - ' 0 ' ) ;
if ( compat_version > 1 )
return false ; // resource is too new for us to load
// Extract fields
src + = 64 ;
pctx - > nBuildNumber = * ( int * ) src ;
pctx - > pRVAHeap = ( const unsigned char * ) ( src + 24 ) ;
pctx - > nFirstRVA = * ( const long * ) ( src + 20 ) ;
pctx - > pClassNameHeap = ( const char * ) pctx - > pRVAHeap - 4 + * ( const long * ) ( src + 4 ) ;
pctx - > pFuncNameHeap = pctx - > pClassNameHeap + * ( const long * ) ( src + 8 ) ;
pctx - > pSegments = ( unsigned long ( * ) [ 2 ] ) ( pctx - > pFuncNameHeap + * ( const long * ) ( src + 12 ) ) ;
pctx - > nSegments = * ( const long * ) ( src + 16 ) ;
return true ;
}
void VDDebugInfoDeinit ( VDDebugInfoContext * pctx ) {
if ( pctx - > pRawBlock ) {
VirtualFree ( pctx - > pRawBlock , 0 , MEM_RELEASE ) ;
pctx - > pRawBlock = NULL ;
}
}
bool VDDebugInfoInitFromFile ( VDDebugInfoContext * pctx , const char * pszFilename ) {
pctx - > pRawBlock = NULL ;
pctx - > pRVAHeap = NULL ;
HANDLE h = CreateFile ( pszFilename , GENERIC_READ , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( INVALID_HANDLE_VALUE = = h ) {
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 ;
}
} while ( false ) ;
VDDebugInfoDeinit ( pctx ) ;
CloseHandle ( h ) ;
return false ;
}
long VDDebugInfoLookupRVA ( VDDebugInfoContext * pctx , unsigned rva , char * buf , int buflen ) {
int i ;
for ( i = 0 ; i < pctx - > nSegments ; + + i ) {
if ( rva > = pctx - > pSegments [ i ] [ 0 ] & & rva < pctx - > pSegments [ i ] [ 0 ] + pctx - > pSegments [ i ] [ 1 ] )
break ;
}
if ( i > = pctx - > nSegments )
return - 1 ;
const unsigned char * pr = pctx - > pRVAHeap ;
const unsigned char * pr_limit = ( const unsigned char * ) pctx - > pClassNameHeap ;
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 ;
}
// Decompress name for RVA
if ( pr < pr_limit ) {
const char * fn_name = GetNameFromHeap ( pctx - > pFuncNameHeap , idx ) ;
const char * class_name = NULL ;
const char * prefix = " " ;
if ( * fn_name = = 0 )
return - 1 ;
if ( * fn_name < 32 ) {
int class_idx ;
class_idx = ( ( unsigned ) fn_name [ 0 ] - 1 ) * 128 + ( ( unsigned ) fn_name [ 1 ] - 1 ) ;
class_name = GetNameFromHeap ( pctx - > pClassNameHeap , class_idx ) ;
fn_name + = 2 ;
if ( * fn_name = = 1 ) {
fn_name = class_name ;
} else if ( * fn_name = = 2 ) {
fn_name = class_name ;
prefix = " ~ " ;
} else if ( * fn_name < 32 )
fn_name = " (special) " ;
}
// ehh... where's my wsnprintf? _snprintf() might allocate memory or locks....
return wsprintf ( buf , " %s%s%s%s " , class_name ? class_name : " " , class_name ? " :: " : " " , prefix , fn_name ) > = 0
? rva
: - 1 ;
}
return - 1 ;
}
///////////////////////////////////////////////////////////////////////////
static bool ReportCrashCallStack ( HWND hwnd , HANDLE hFile , const EXCEPTION_POINTERS * const pExc , const void * pDebugSrc ) {
const CONTEXT * const pContext = ( const CONTEXT * ) pExc - > ContextRecord ;
HANDLE hprMe = GetCurrentProcess ( ) ;
char * lpAddr = ( char * ) pContext - > Esp ;
int limit = 100 ;
unsigned long data ;
char buf [ 512 ] ;
if ( ! g_debugInfo . pRVAHeap ) {
2002-08-27 23:16:52 +00:00
Report ( hwnd , hFile , " Could not open debug resource file (StepMania.vdi). " ) ;
2002-08-27 22:02:30 +00:00
return false ;
}
if ( g_debugInfo . nBuildNumber ! = int ( version_num ) ) {
2002-08-27 23:16:52 +00:00
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 ) ) ;
2002-08-27 22:02:30 +00:00
return false ;
}
// Get some module names.
void * pModuleMem ;
ModuleInfo * pModules = CrashGetModules ( pModuleMem ) ;
// 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 ;
// Walk up the stack. Hopefully it wasn't fscked.
data = pContext - > Eip ;
do {
bool fValid = true ;
int len ;
MEMORY_BASIC_INFORMATION meminfo ;
VirtualQuery ( ( void * ) data , & meminfo , sizeof meminfo ) ;
if ( ! IsExecutableProtection ( meminfo . Protect ) | | meminfo . State ! = MEM_COMMIT ) {
// Report(hwnd, hFile, "Rejected: %08lx (%08lx)", data, meminfo.Protect);
fValid = false ;
}
if ( data ! = pContext - > Eip ) {
len = 7 ;
* ( long * ) ( buf + 0 ) = * ( long * ) ( buf + 4 ) = 0 ;
while ( len > 0 & & ! ReadProcessMemory ( GetCurrentProcess ( ) , ( void * ) ( data - len ) , buf + 7 - len , len , NULL ) )
- - len ;
fValid & = IsValidCall ( buf + 7 , len ) ;
}
if ( fValid ) {
if ( VDDebugInfoLookupRVA ( & g_debugInfo , data , buf , sizeof buf ) > = 0 ) {
Report ( hwnd , hFile , " %08lx: %s() " , data , buf ) ;
- - limit ;
} else {
ModuleInfo * pMods = pModules ;
ModuleInfo mi ;
char szName [ MAX_PATH ] ;
mi . name = NULL ;
if ( pMods ) {
while ( pMods - > name ) {
if ( data > = pMods - > base & & ( data - pMods - > base ) < pMods - > size )
break ;
+ + pMods ;
}
mi = * pMods ;
} else {
// Well, something failed, or we didn't have either PSAPI.DLL or ToolHelp
// to play with. So we'll use a nastier method instead.
mi . base = ( unsigned long ) meminfo . AllocationBase ;
mi . name = CrashGetModuleBaseName ( ( HMODULE ) mi . base , szName ) ;
}
if ( mi . name ) {
unsigned long fnbase ;
const char * pExportName = CrashLookupExport ( ( HMODULE ) mi . base , data , fnbase ) ;
if ( pExportName )
Report ( hwnd , hFile , " %08lx: %s!%s [%08lx+%lx+%lx] " , data , mi . name , pExportName , mi . base , fnbase , ( data - mi . base - fnbase ) ) ;
else
Report ( hwnd , hFile , " %08lx: %s!%08lx " , data , mi . name , data - mi . base ) ;
} else
Report ( hwnd , hFile , " %08lx: %08lx " , data , data ) ;
- - limit ;
}
}
if ( lpAddr > = pStackBase )
break ;
lpAddr + = 4 ;
} while ( limit > 0 & & ReadProcessMemory ( hprMe , lpAddr - 4 , & data , 4 , NULL ) ) ;
// All done, close up shop and exit.
if ( pModuleMem )
VirtualFree ( pModuleMem , 0 , MEM_RELEASE ) ;
return true ;
}
2002-09-12 22:56:02 +00:00
static void ReportDisasm ( HWND hwnd , HANDLE hFile , const EXCEPTION_POINTERS * const pExc )
{
char tbuf [ 2048 ] ;
long idx = 0 ;
Report ( hwnd , hFile , " Disassembly: " ) ;
while ( idx = g_pcdw - > getInstruction ( tbuf , idx ) ) {
Report ( hwnd , hFile , " %s " , tbuf ) ;
}
FlushFileBuffers ( hFile ) ;
}
2002-08-29 08:58:40 +00:00
static void DoSave ( const EXCEPTION_POINTERS * pExc ) {
2002-08-27 22:02:30 +00:00
HANDLE hFile ;
char szModName2 [ MAX_PATH ] ;
SpliceProgramPath ( szModName2 , sizeof szModName2 , " crashinfo.txt " ) ;
hFile = CreateFile ( szModName2 , GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( INVALID_HANDLE_VALUE = = hFile )
return ;
Report ( NULL , hFile ,
2002-08-27 23:16:52 +00:00
" StepMania crash report -- build %d \r \n "
2002-09-12 22:56:02 +00:00
" -------------------------------------- "
" \r \n " , version_num ) ;
2002-08-27 22:02:30 +00:00
// Detect operating system.
OSVERSIONINFO ovi ;
ovi . dwOSVersionInfoSize = sizeof ( OSVERSIONINFO ) ;
if ( GetVersionEx ( & ovi ) ) {
2002-10-20 03:56:09 +00:00
Report ( NULL , hFile , " Windows %d.%d (Win%s build %d) [%s] " ,
ovi . dwMajorVersion ,
ovi . dwMinorVersion ,
ovi . dwPlatformId = = VER_PLATFORM_WIN32_WINDOWS
2002-08-27 22:02:30 +00:00
? ( ovi . dwMinorVersion > 0 ? " 98 " : " 95 " )
: ovi . dwPlatformId = = VER_PLATFORM_WIN32_NT
? ( ovi . dwMajorVersion > = 5 ? " 2000 " : " NT " )
2002-10-20 03:56:09 +00:00
: " ? " ,
ovi . dwBuildNumber & 0xffff ,
ovi . szCSDVersion ) ;
2002-08-27 22:02:30 +00:00
}
Report ( NULL , hFile , " " ) ;
2002-09-12 22:56:02 +00:00
ReportReason ( NULL , NULL , hFile , pExc ) ;
Report ( NULL , hFile , " " ) ;
2002-08-27 22:02:30 +00:00
2002-09-12 22:56:02 +00:00
// Dump thread stacks
ReportThreadStacks ( NULL , hFile , pExc ) ;
2002-08-27 22:02:30 +00:00
Report ( NULL , hFile , " " ) ;
ReportCrashCallStack ( NULL , hFile , pExc , g_pcdw - > vdc . pExtraData ) ;
Report ( NULL , hFile , " " ) ;
ReportCrashLog ( NULL , hFile ) ;
2002-09-12 22:56:02 +00:00
Report ( NULL , hFile , " " ) ;
2002-08-27 22:02:30 +00:00
2002-09-12 22:56:02 +00:00
/* We can do without these for now. People keep saying "here's a bug,
* tell me if you want the crash dump" which wastes time (of course we
* do!). Let's make the dump smaller to encourage just sending it, which
* is what people should be doing. (If we can use the SF BTS more
* consistently, instead of forums, this will be less of an issue.) */
/* ReportDisasm(NULL, hFile, pExc);
Report(NULL, hFile, "");
2002-08-27 22:02:30 +00:00
2002-09-12 22:56:02 +00:00
ReportCrashData(NULL, hFile, pExc);
Report(NULL, hFile, "");
*/
Report ( NULL , hFile , " -- End of report " ) ;
2002-08-27 22:02:30 +00:00
CloseHandle ( hFile ) ;
}
2002-09-07 20:59:16 +00:00
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
) ;
}
2002-08-27 22:02:30 +00:00
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 :
{
HWND hwndList1 = GetDlgItem ( hDlg , IDC_ASMBOX ) ;
HWND hwndList2 = GetDlgItem ( hDlg , IDC_REGDUMP ) ;
HWND hwndList3 = GetDlgItem ( hDlg , IDC_CALL_STACK ) ;
HWND hwndReason = GetDlgItem ( hDlg , IDC_STATIC_BOMBREASON ) ;
const EXCEPTION_POINTERS * const pExc = ( const EXCEPTION_POINTERS * ) lParam ;
s_pExc = pExc ;
g_pcdw - > DoInitListbox ( hwndList1 ) ;
SendMessage ( hwndList2 , WM_SETFONT , SendMessage ( hwndList1 , WM_GETFONT , 0 , 0 ) , MAKELPARAM ( TRUE , 0 ) ) ;
SendMessage ( hwndList3 , WM_SETFONT , SendMessage ( hwndList1 , WM_GETFONT , 0 , 0 ) , MAKELPARAM ( TRUE , 0 ) ) ;
2002-09-12 22:56:02 +00:00
ReportReason ( NULL , hwndReason , NULL , pExc ) ;
ReportCrashData ( hwndList2 , NULL , pExc ) ;
2002-08-27 22:02:30 +00:00
s_bHaveCallstack = ReportCrashCallStack ( hwndList3 , NULL , pExc , g_pcdw - > vdc . pExtraData ) ;
}
return TRUE ;
case WM_COMMAND :
switch ( LOWORD ( wParam ) ) {
2002-08-27 23:16:52 +00:00
case IDC_BUTTON_CLOSE :
2002-08-27 22:02:30 +00:00
EndDialog ( hDlg , FALSE ) ;
return TRUE ;
2002-08-27 23:16:52 +00:00
case IDOK :
EndDialog ( hDlg , TRUE ) ;
return TRUE ;
2002-09-07 20:59:16 +00:00
case IDC_VIEW_LOG :
ViewWithNotepad ( " log.txt " ) ;
break ;
2002-08-27 22:02:30 +00:00
case IDC_CRASH_SAVE :
if ( ! s_bHaveCallstack )
if ( IDOK ! = MessageBox ( hDlg ,
2002-08-27 23:16:52 +00:00
" StepMania cannot load its crash resource file, and thus the crash dump will be "
2002-08-27 22:02:30 +00:00
" missing the most important part, the call stack. Crash dumps are much less useful "
" without the call stack. " ,
2002-08-27 23:16:52 +00:00
" StepMania warning " , MB_OK | MB_ICONEXCLAMATION ) )
2002-08-27 22:02:30 +00:00
return TRUE ;
2002-09-07 20:59:16 +00:00
ViewWithNotepad ( " crashinfo.txt " ) ;
2002-08-27 22:02:30 +00:00
return TRUE ;
case IDC_BUTTON_RESTART :
{
2002-10-20 03:53:45 +00:00
/* Clear the startup mutex, since we're starting a new
* instance before ending ourself. */
CleanupForRestart ( ) ;
2002-08-28 07:39:17 +00:00
char cwd [ MAX_PATH ] ;
SpliceProgramPath ( cwd , MAX_PATH , " " ) ;
2002-10-20 03:53:45 +00:00
TCHAR szFullAppPath [ MAX_PATH ] ;
GetModuleFileName ( NULL , szFullAppPath , MAX_PATH ) ;
2002-08-27 22:02:30 +00:00
// Launch StepMania
PROCESS_INFORMATION pi ;
STARTUPINFO si ;
ZeroMemory ( & si , sizeof ( si ) ) ;
CreateProcess (
NULL , // pointer to name of executable module
2002-10-20 03:53:45 +00:00
szFullAppPath , // pointer to command line string
2002-08-27 22:02:30 +00:00
NULL , // process security attributes
NULL , // thread security attributes
false , // handle inheritance flag
0 , // creation flags
NULL , // pointer to new environment block
2002-08-28 07:39:17 +00:00
cwd , // pointer to current directory name
2002-08-27 22:02:30 +00:00
& 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 ;
case WM_MEASUREITEM :
return g_pcdw - > DoMeasureItem ( lParam ) ;
case WM_DRAWITEM :
return g_pcdw - > DoDrawItem ( lParam ) ;
}
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 ( ) ) ) {
}
}
/* RageLog feeds us logs; keep track of recent logs in a circular
* buffer. This lets us include recent logs in the crash report.
* We don't want to include RageLog, since we don't want to pull
2002-09-13 18:32:10 +00:00
* in MFC.
*
* Since this is a static, preallocated region, even if it gets
* trampled we'll just print garbage, not crash due to an invalid
* pointer. */
2002-08-27 22:02:30 +00:00
static const int BACKLOG_LINES = 10 ;
static char backlog [ BACKLOG_LINES ] [ 1024 ] ;
static int backlog_start = 0 , backlog_cnt = 0 ;
void CrashLog ( const char * str )
{
strncpy ( backlog [ backlog_start ] , str , sizeof ( backlog [ backlog_start ] ) ) ;
backlog [ backlog_start ] [ sizeof ( backlog [ backlog_start ] ) - 1 ] = 0 ;
backlog_start + + ;
if ( backlog_start > backlog_cnt )
backlog_cnt = backlog_start ;
2002-09-03 05:52:08 +00:00
backlog_start % = BACKLOG_LINES ;
2002-08-27 22:02:30 +00:00
}
static void ReportCrashLog ( HWND hwnd , HANDLE hFile )
{
Report ( NULL , hFile , " Partial log: " ) ;
int i ;
for ( i = backlog_start ; i < BACKLOG_LINES & & i < backlog_cnt ; + + i )
Report ( hwnd , hFile , " %s " , backlog [ i ] ) ;
for ( i = 0 ; i < backlog_start & & i < backlog_cnt ; + + i )
Report ( hwnd , hFile , " %s " , backlog [ i ] ) ;
}