From 607a8e2e11a794ba594ba6da8b558c5ed6609d87 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Thu, 9 Feb 2006 23:11:31 +0000 Subject: [PATCH] I've been hesitant to do this, but it's the next step in making crash handling more consistent. Move the crash handling dialog into a new process, like the Unix handler does. - We're allowed to operate normally; we have a new heap, so allocations are safe. - Gzipping the VDI is easy now; we can simply use GunzipFile(). The only particularly ugly thing in this is the need to run CrashGetModuleBaseName in the original process. That's lame, but straightforward. It'd be better to use GetModuleFileNameEx, but I didn't want to drag psapi.dll into this. This may also make window hiding more reliable. Previously, we wrote the crashinfo, tried to hide the window, then showed the dialog. Hiding the window was risky and could hang or crash again. Now we can forcibly kill the process from the child, if needed, and still be able to show a dialog. This also removes the backtrace and "reason" displays from the dialog. Those weren't useful in practice; I always loaded the crashinfo.txt and never used them, and they're intimidating to casual users. --- stepmania/src/archutils/Win32/Crash.cpp | 874 ++++-------------- stepmania/src/archutils/Win32/Crash.h | 2 + .../src/archutils/Win32/CrashHandlerChild.cpp | 711 ++++++++++++++ .../archutils/Win32/CrashHandlerInternal.h | 52 ++ 4 files changed, 966 insertions(+), 673 deletions(-) create mode 100644 stepmania/src/archutils/Win32/CrashHandlerChild.cpp create mode 100644 stepmania/src/archutils/Win32/CrashHandlerInternal.h diff --git a/stepmania/src/archutils/Win32/Crash.cpp b/stepmania/src/archutils/Win32/Crash.cpp index d7c114b8dd..34fc19bc7e 100644 --- a/stepmania/src/archutils/Win32/Crash.cpp +++ b/stepmania/src/archutils/Win32/Crash.cpp @@ -2,51 +2,23 @@ // DO NOT USE stdio.h! printf() calls malloc()! //#include -#include -#include #include -#include #include "arch/Threads/Threads_Win32.h" -#include "archutils/Win32/WindowsResources.h" #include "crash.h" -#include "ProductInfo.h" -#include "archutils/win32/DialogUtil.h" +#include "CrashHandlerInternal.h" #include "RageLog.h" /* for RageLog::GetAdditionalLog and Flush */ #include "RageThreads.h" /* for GetCheckpointLogs */ #include "PrefsManager.h" /* for g_bAutoRestart */ #include "RestartProgram.h" -#include "GotoURL.h" - -static HFONT hFontMono = NULL; - -static void DoSave(); - -extern HINSTANCE g_hInstance; -extern unsigned long version_num; -extern const char *version_time; -extern HINSTANCE g_hInstance; -#define BACKTRACE_MAX_SIZE 100 - // WARNING: This is called from crash-time conditions! No malloc() or new!!! #define malloc not_allowed_here #define new not_allowed_here -static void GetVDIPath( char *buf, int bufsiz ) -{ - GetModuleFileName( NULL, buf, bufsiz ); - buf[bufsiz-5] = 0; - char *p = strrchr( buf, '.' ); - if( p ) - strcpy( p, ".vdi" ); - else - strcat( buf, ".vdi" ); -} - static void SpliceProgramPath(char *buf, int bufsiz, const char *fn) { char tbuf[MAX_PATH]; char *pszFile; @@ -56,50 +28,26 @@ static void SpliceProgramPath(char *buf, int bufsiz, const char *fn) { strcpy(pszFile, fn); } -struct VDDebugInfoContext -{ - VDDebugInfoContext() { pRVAHeap=NULL; } - bool Loaded() const { return pRVAHeap != NULL; } - void *pRawBlock; - - int nBuildNumber; - - const unsigned char *pRVAHeap; - unsigned nFirstRVA; - - const char *pFuncNameHeap; - const unsigned long (*pSegments)[2]; - int nSegments; - char sFilename[1024]; - char szError[1024]; -}; - - -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" }, + { 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", }, { NULL }, }; @@ -113,25 +61,6 @@ static const char *LookupException( DWORD code ) return NULL; } -struct CrashInfo -{ - char m_CrashReason[1024*8]; - - const void *m_BacktracePointers[BACKTRACE_MAX_SIZE]; - - enum { MAX_BACKTRACE_THREADS = 32 }; - const void *m_AlternateThreadBacktrace[MAX_BACKTRACE_THREADS][BACKTRACE_MAX_SIZE]; - char m_AlternateThreadName[MAX_BACKTRACE_THREADS][128]; - - CrashInfo() - { - m_CrashReason[0] = 0; - memset( m_AlternateThreadBacktrace, 0, sizeof(m_AlternateThreadBacktrace) ); - memset( m_AlternateThreadName, 0, sizeof(m_AlternateThreadName) ); - m_BacktracePointers[0] = NULL; - } -}; - static CrashInfo g_CrashInfo; static void GetReason( const EXCEPTION_RECORD *pRecord, CrashInfo *crash ) { @@ -147,17 +76,193 @@ static void GetReason( const EXCEPTION_RECORD *pRecord, CrashInfo *crash ) } } -long VDDebugInfoLookupRVA(VDDebugInfoContext *pctx, unsigned rva, char *buf, int buflen); -bool VDDebugInfoInitFromMemory(VDDebugInfoContext *pctx, const void *_src); -bool VDDebugInfoInitFromFile( VDDebugInfoContext *pctx ); -void VDDebugInfoDeinit( VDDebugInfoContext *pctx ); - static HWND g_hForegroundWnd = NULL; void CrashHandler::SetForegroundWindow( HWND hWnd ) { g_hForegroundWnd = hWnd; } +void WriteToChild( HANDLE hPipe, const void *pData, size_t iSize ) +{ + while( iSize ) + { + DWORD iActual; + if( !WriteFile(hPipe, pData, iSize, &iActual, NULL) ) + return; + iSize -= iActual; + } +} + +/* Execute the child process. Return a handle to the process, a writable handle + * to its stdin, and a readable handle to its stdout. */ +bool StartChild( HANDLE &hProcess, HANDLE &hToStdin, HANDLE &hFromStdout ) +{ + char cwd[MAX_PATH]; + SpliceProgramPath( cwd, MAX_PATH, "" ); + + STARTUPINFO si; + ZeroMemory( &si, sizeof(si) ); + si.dwFlags |= STARTF_USESTDHANDLES; + + { + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = true; + sa.lpSecurityDescriptor = NULL; + + CreatePipe( &si.hStdInput, &hToStdin, &sa, 0 ); + CreatePipe( &hFromStdout, &si.hStdOutput, &sa, 0 ); + SetHandleInformation( hToStdin, HANDLE_FLAG_INHERIT, 0 ); + SetHandleInformation( hFromStdout, HANDLE_FLAG_INHERIT, 0 ); + } + + char szBuf[256] = ""; + GetModuleFileName( NULL, szBuf, MAX_PATH ); + strcat( szBuf, " " ); + strcat( szBuf, CHILD_MAGIC_PARAMETER ); + + PROCESS_INFORMATION pi; + if( !CreateProcess( + NULL, // pointer to name of executable module + szBuf, // pointer to command line string + NULL, // process security attributes + NULL, // thread security attributes + true, // 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 + ) ) + return false; + + hProcess = pi.hProcess; + + return true; +} + +static const char *CrashGetModuleBaseName(HMODULE hmod, char *pszBaseName) +{ + char szPath1[MAX_PATH]; + char szPath2[MAX_PATH]; + + __try { + if( !GetModuleFileName(hmod, szPath1, sizeof(szPath1)) ) + return NULL; + + char *pszFile; + DWORD dw = GetFullPathName( szPath1, sizeof(szPath2), szPath2, &pszFile ); + + if( !dw || dw > sizeof(szPath2) ) + return NULL; + + strcpy( pszBaseName, pszFile ); + + pszFile = pszBaseName; + + char *period = NULL; + while( *pszFile++ ) + if( pszFile[-1]=='.' ) + period = pszFile-1; + + if( period ) + *period = 0; + } __except(1) { + return NULL; + } + + return pszBaseName; +} + +void RunChild() +{ + HANDLE hProcess, hToStdin, hFromStdout; + StartChild( hProcess, hToStdin, hFromStdout ); + + /* 0. Send a handle of this process to the crash handling process, which it can use to handle symbol lookups. */ + { + HANDLE hTargetHandle; + DuplicateHandle( + GetCurrentProcess(), + GetCurrentProcess(), + hProcess, + &hTargetHandle, + 0, + false, + DUPLICATE_SAME_ACCESS + ); + + WriteToChild( hToStdin, &hTargetHandle, sizeof(hTargetHandle) ); + } + + /* 1. Write the CrashData. */ + WriteToChild( hToStdin, &g_CrashInfo, sizeof(g_CrashInfo) ); + + /* 2. Write info. */ + const char *p = RageLog::GetInfo(); + int iSize = strlen( p )+1; + WriteToChild( hToStdin, &iSize, sizeof(iSize) ); + WriteToChild( hToStdin, p, iSize ); + + /* 3. Write AdditionalLog. */ + p = RageLog::GetAdditionalLog(); + iSize = strlen( p )+1; + WriteToChild( hToStdin, &iSize, sizeof(iSize) ); + WriteToChild( hToStdin, p, iSize ); + + /* 4. Write RecentLogs. */ + int cnt = 0; + const char *ps[1024]; + while( cnt < 1024 && (ps[cnt] = RageLog::GetRecentLog( cnt )) != NULL ) + ++cnt; + + WriteToChild(hToStdin, &cnt, sizeof(cnt)); + for( int i = 0; i < cnt; ++i ) + { + iSize = strlen(ps[i])+1; + WriteToChild( hToStdin, &iSize, sizeof(iSize) ); + WriteToChild( hToStdin, ps[i], iSize ); + } + + /* 5. Write CHECKPOINTs. */ + static char buf[1024*32]; + Checkpoints::GetLogs( buf, sizeof(buf), "$$" ); + iSize = strlen( buf )+1; + WriteToChild( hToStdin, &iSize, sizeof(iSize) ); + WriteToChild( hToStdin, buf, iSize ); + + /* 6. Write the crashed thread's name. */ + p = RageThread::GetCurThreadName(); + iSize = strlen( p )+1; + WriteToChild( hToStdin, &iSize, sizeof(iSize) ); + WriteToChild( hToStdin, p, iSize ); + + /* The parent process needs to access this process briefly. When it's done, it'll + * write a byte to stdout. Wait until we see that before exiting. */ + while(1) + { + int iCmd; + DWORD iActual; + ReadFile( hFromStdout, &iCmd, sizeof(iCmd), &iActual, NULL ); + if( iCmd == 0 ) + break; + + /* Ugly: the new process can't execute GetModuleFileName on this process, + * since GetModuleFileNameEx might not be available. Run the requests here. */ + if( iCmd == 1 ) + { + HMODULE hMod; + ReadFile( hFromStdout, &hMod, sizeof(hMod), &iActual, NULL ); + char szName[MAX_PATH]; + if( !CrashGetModuleBaseName(hMod, szName) ) + strcpy( szName, "???" ); + iSize = strlen( szName ); + WriteToChild( hToStdin, &iSize, sizeof(iSize) ); + WriteToChild( hToStdin, szName, iSize ); + } + } +} + long __stdcall CrashHandler::ExceptionHandler( EXCEPTION_POINTERS *pExc ) { /* Flush the log it isn't cut off at the end. */ @@ -213,39 +318,17 @@ long __stdcall CrashHandler::ExceptionHandler( EXCEPTION_POINTERS *pExc ) RageThread::HaltAllThreads( false ); - 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" - ); - - if( !hFontMono ) - hFontMono = (HFONT) GetStockObject( ANSI_FIXED_FONT ); - - // Attempt to read debug file. - - VDDebugInfoInitFromFile( &g_debugInfo ); - - /* In case something goes amiss before the user can view the crash - * dump, save it now. */ 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(); + + RunChild(); ++InHere; + if( g_bAutoRestart ) + Win32RestartProgram(); + /* Now things get more risky. If we're fullscreen, the window will obscure the * crash dialog. Try to hide the window. Things might blow up here; do this * after DoSave, so we always write a crash dump. */ @@ -261,20 +344,6 @@ long __stdcall CrashHandler::ExceptionHandler( EXCEPTION_POINTERS *pExc ) ChangeDisplaySettings( NULL, 0 ); } - if( g_bAutoRestart ) - Win32RestartProgram(); - - /* Little trick to get an HINSTANCE of ourself without having access to the hwnd ... */ - { - TCHAR szFullAppPath[MAX_PATH]; - GetModuleFileName( NULL, szFullAppPath, MAX_PATH ); - HINSTANCE hHandle = LoadLibrary( szFullAppPath ); - - DialogBoxParam( hHandle, MAKEINTRESOURCE(IDD_DISASM_CRASH), NULL, CrashDlgProc, (LPARAM) pExc ); - } - - VDDebugInfoDeinit( &g_debugInfo ); - InHere = false; SetUnhandledExceptionFilter( NULL ); @@ -286,46 +355,6 @@ long __stdcall CrashHandler::ExceptionHandler( EXCEPTION_POINTERS *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 ReportReason( HWND hwndReason, HANDLE hFile, const CrashInfo *pCrash ) -{ - if( hwndReason ) - SetWindowText( hwndReason, pCrash->m_CrashReason ); - - if( hFile ) - Report( NULL, hFile, pCrash->m_CrashReason ); -} - -static const char *GetNameFromHeap(const char *heap, int idx) -{ - while(idx--) - while(*heap++); - - return heap; -} - ////////////////////////////////////////////////////////////////////////////// static bool IsValidCall(char *buf, int len) @@ -374,8 +403,6 @@ static bool IsValidCall(char *buf, int len) return false; } -/////////////////////////////////////////////////////////////////////////// - static bool IsExecutableProtection(DWORD dwProtect) { MEMORY_BASIC_INFORMATION meminfo; @@ -400,246 +427,6 @@ static bool IsExecutableProtection(DWORD dwProtect) { 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; - - static const char *header = "symbolic debug information"; - if (memcmp((char *)src, header, strlen(header))) - { - strcpy( pctx->szError, "header doesn't match" ); - return false; - } - - // Extract fields - - src += 64; - - pctx->nBuildNumber = *(int *)src; - 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); - - return true; -} - -void VDDebugInfoDeinit(VDDebugInfoContext *pctx) { - if (pctx->pRawBlock) { - VirtualFree(pctx->pRawBlock, 0, MEM_RELEASE); - pctx->pRawBlock = NULL; - } -} - -bool VDDebugInfoInitFromFile( VDDebugInfoContext *pctx ) -{ - if( pctx->Loaded() ) - return true; - - pctx->pRawBlock = NULL; - pctx->pRVAHeap = NULL; - GetVDIPath( pctx->sFilename, ARRAYSIZE(pctx->sFilename) ); - pctx->szError[0] = 0; - - HANDLE h = CreateFile(pctx->sFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - if (INVALID_HANDLE_VALUE == h) - { - strcpy( pctx->szError, "CreateFile failed" ); - 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; - } - - VirtualFree(pctx->pRawBlock, 0, MEM_RELEASE); - - } while(false); - - VDDebugInfoDeinit(pctx); - CloseHandle(h); - return false; -} - -static bool PointerIsInAnySegment( const VDDebugInfoContext *pctx, unsigned rva ) -{ - for( int i=0; inSegments; ++i ) - { - if (rva >= pctx->pSegments[i][0] && rva < pctx->pSegments[i][0] + pctx->pSegments[i][1]) - return true; - } - - return false; -} - -long VDDebugInfoLookupRVA(VDDebugInfoContext *pctx, unsigned rva, char *buf, int buflen) -{ - if ( !PointerIsInAnySegment(pctx, rva) ) - return -1; - - const unsigned char *pr = pctx->pRVAHeap; - const unsigned char *pr_limit = (const unsigned char *)pctx->pFuncNameHeap; - 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; - } - if (pr >= pr_limit) - return -1; - - // Decompress name for RVA - const char *fn_name = GetNameFromHeap(pctx->pFuncNameHeap, idx); - - if (!*fn_name) - fn_name = "(special)"; - - strncpy( buf, fn_name, buflen ); - buf[buflen-1]=0; - - return rva; -} - -/////////////////////////////////////////////////////////////////////////// -#include "archutils/Win32/ddk/dbghelp.h" -#if defined(_MSC_VER) -#pragma comment(lib, "archutils/Win32/ddk/dbghelp.lib") -#endif - -static bool InitDbghelp() -{ - static bool initted = false; - if( !initted ) - { - SymSetOptions( SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS ); - - if (!SymInitialize(GetCurrentProcess(), NULL, TRUE)) - return false; - - initted = true; - } - - return true; -} - -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]; - 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; - } - - if( obuf[0] == '_' ) - { - strcat( obuf, "()" ); /* _main -> _main() */ - return obuf+1; /* _main -> main */ - } - - return obuf; -} - static bool PointsToValidCall( unsigned long ptr ) { char buf[7]; @@ -708,265 +495,6 @@ void CrashHandler::do_backtrace( const void **buf, size_t size, buf[i++] = NULL; } -void SymLookup( const void *ptr, char *buf ) -{ - 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 ) - { - wsprintf( buf, "%08x: %s", ptr, Demangle(tmp) ); - 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() ) - { - Report( hwnd, hFile, "debug resource file '%s': %s.", g_debugInfo.sFilename, g_debugInfo.szError ); - return false; - } - - if( g_debugInfo.nBuildNumber != int(version_num) ) - { - Report(hwnd, hFile, "Incorrect %s file (build %d, expected %d) for this version of " PRODUCT_NAME " -- call stack unavailable.", - g_debugInfo.sFilename, g_debugInfo.nBuildNumber, int(version_num)); - return false; - } - - for( int i = 0; Backtrace[i]; ++i ) - { - char buf[10240]; - SymLookup( Backtrace[i], buf ); - Report( hwnd, hFile, "%s", buf ); - } - - return true; -} - -void WriteBuf( HANDLE hFile, const char *buf ) -{ - DWORD dwActual; - WriteFile(hFile, buf, strlen(buf), &dwActual, NULL); -} - -static void DoSave() -{ - char szModName2[MAX_PATH]; - - SpliceProgramPath(szModName2, sizeof szModName2, "../crashinfo.txt"); - - HANDLE hFile = CreateFile(szModName2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - return; - - Report(NULL, hFile, - "%s crash report (build %d, %s)\r\n" - "--------------------------------------" - "\r\n", PRODUCT_NAME_VER, version_num, version_time); - - ReportReason( NULL, hFile, &g_CrashInfo ); - Report(NULL, hFile, ""); - - // Dump thread stacks - static char buf[1024*32]; - Checkpoints::GetLogs( buf, sizeof(buf), "\r\n" ); - WriteBuf( hFile, buf ); - Report(NULL, hFile, ""); - - ReportCallStack( NULL, hFile, g_CrashInfo.m_BacktracePointers ); - Report(NULL, hFile, ""); - - if( g_CrashInfo.m_AlternateThreadBacktrace[0] ) - { - for( int i = 0; i < CrashInfo::MAX_BACKTRACE_THREADS; ++i ) - { - if( !g_CrashInfo.m_AlternateThreadBacktrace[i][0] ) - continue; - - Report( NULL, hFile, "Thread %s:", g_CrashInfo.m_AlternateThreadName[i] ); - Report( NULL, hFile, "" ); - ReportCallStack( NULL, hFile, g_CrashInfo.m_AlternateThreadBacktrace[i] ); - Report(NULL, hFile, ""); - } - } - - Report(NULL, hFile, "Static log:"); - WriteBuf( hFile, RageLog::GetInfo() ); - WriteBuf( hFile, RageLog::GetAdditionalLog() ); - Report(NULL, hFile, ""); - - Report(NULL, hFile, "Partial log:"); - int i = 0; - while( const char *p = RageLog::GetRecentLog( i++ ) ) - Report(NULL, hFile, "%s", p); - 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 bool s_bHaveCallstack; - - switch(msg) - { - case WM_INITDIALOG: - { - HWND hwndList = GetDlgItem(hDlg, IDC_CALL_STACK); - HWND hwndReason = GetDlgItem(hDlg, IDC_STATIC_BOMBREASON); - - if (hFontMono) - { - SendMessage(hwndList, WM_SETFONT, (WPARAM)hFontMono, MAKELPARAM(TRUE, 0)); - SendMessage(hwndReason, WM_SETFONT, (WPARAM)hFontMono, MAKELPARAM(TRUE, 0)); - } - DialogUtil::SetHeaderFont( hDlg, IDC_STATIC_HEADER_TEXT ); - - ReportReason( hwndReason, NULL, &g_CrashInfo ); - s_bHaveCallstack = ReportCallStack( hwndList, NULL, g_CrashInfo.m_BacktracePointers ); - } - return TRUE; - - case WM_COMMAND: - switch(LOWORD(wParam)) { - case IDC_BUTTON_CLOSE: - EndDialog(hDlg, FALSE); - return TRUE; - case IDOK: - // EndDialog(hDlg, TRUE); /* don't always exit on ENTER */ - return TRUE; - case IDC_VIEW_LOG: - ViewWithNotepad("../log.txt"); - break; - 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( REPORT_BUG_URL ); - break; - } - break; - case WM_CTLCOLORSTATIC: - { - HDC hdc = (HDC)wParam; - HWND hwndStatic = (HWND)lParam; - HBRUSH hbr = NULL; - - // TODO: Change any attributes of the DC here - switch( GetDlgCtrlID(hwndStatic) ) - { - case IDC_STATIC_HEADER_TEXT: - case IDC_STATIC_ICON: - hbr = (HBRUSH)::GetStockObject(WHITE_BRUSH); - SetBkMode( hdc, OPAQUE ); - SetBkColor( hdc, RGB(255,255,255) ); - break; - } - - // TODO: Return a different brush if the default is not desired - return (BOOL)hbr; - } - } - - return FALSE; -} - /* Trigger the crash handler. This works even in the debugger. */ static void NORETURN debug_crash() { diff --git a/stepmania/src/archutils/Win32/Crash.h b/stepmania/src/archutils/Win32/Crash.h index f367810e57..1aa0a2c3a9 100644 --- a/stepmania/src/archutils/Win32/Crash.h +++ b/stepmania/src/archutils/Win32/Crash.h @@ -16,6 +16,8 @@ namespace CrashHandler * set, the crash handler will attempt to hide the window or reset the video * mode. */ void SetForegroundWindow( HWND hWnd ); + + void CrashHandlerHandleArgs( int argc, char* argv[] ); }; #endif diff --git a/stepmania/src/archutils/Win32/CrashHandlerChild.cpp b/stepmania/src/archutils/Win32/CrashHandlerChild.cpp new file mode 100644 index 0000000000..f23f83cb0f --- /dev/null +++ b/stepmania/src/archutils/Win32/CrashHandlerChild.cpp @@ -0,0 +1,711 @@ +#include "global.h" +#include "CrashHandlerInternal.h" +#include "Crash.h" + +#include +#include "archutils/Win32/ddk/dbghelp.h" +#include +#include + +#include "archutils/Win32/WindowsResources.h" +#include "archutils/Win32/DialogUtil.h" +#include "archutils/Win32/GotoURL.h" +#include "ProductInfo.h" +#include "RageUtil.h" + +#if defined(_MSC_VER) +#pragma comment(lib, "archutils/Win32/ddk/dbghelp.lib") +#endif + +extern unsigned long version_num; +extern const char *version_time; + +// VDI symbol lookup: +namespace VDDebugInfo +{ + struct Context + { + Context() { pRVAHeap=NULL; } + bool Loaded() const { return pRVAHeap != NULL; } + void *pRawBlock; + + int nBuildNumber; + + const unsigned char *pRVAHeap; + unsigned nFirstRVA; + + const char *pFuncNameHeap; + const unsigned long (*pSegments)[2]; + int nSegments; + char sFilename[1024]; + char szError[1024]; + }; + + static void GetVDIPath( char *buf, int bufsiz ) + { + GetModuleFileName( NULL, buf, bufsiz ); + buf[bufsiz-5] = 0; + char *p = strrchr( buf, '.' ); + if( p ) + strcpy( p, ".vdi" ); + else + strcat( buf, ".vdi" ); + } + + bool VDDebugInfoInitFromMemory( Context *pctx, const void *src_ ) + { + const unsigned char *src = (const unsigned char *)src_; + + pctx->pRVAHeap = NULL; + + static const char *header = "symbolic debug information"; + if( memcmp(src, header, strlen(header)) ) + { + strcpy( pctx->szError, "header doesn't match" ); + return false; + } + + // Extract fields + + src += 64; + + pctx->nBuildNumber = *(int *)src; + 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); + + return true; + } + + void VDDebugInfoDeinit( Context *pctx ) + { + if( pctx->pRawBlock ) + { + VirtualFree(pctx->pRawBlock, 0, MEM_RELEASE); + pctx->pRawBlock = NULL; + } + } + + bool VDDebugInfoInitFromFile( Context *pctx ) + { + if( pctx->Loaded() ) + return true; + + pctx->pRawBlock = NULL; + pctx->pRVAHeap = NULL; + GetVDIPath( pctx->sFilename, ARRAYSIZE(pctx->sFilename) ); + pctx->szError[0] = 0; + + HANDLE h = CreateFile("StepMania.vdi", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if( h == INVALID_HANDLE_VALUE ) + { + strcpy( pctx->szError, "CreateFile failed" ); + return false; + } + + do { + DWORD dwFileSize = GetFileSize( h, NULL ); + if( dwFileSize == INVALID_FILE_SIZE ) + 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; + } + + VirtualFree( pctx->pRawBlock, 0, MEM_RELEASE ); + } while(0); + + VDDebugInfoDeinit(pctx); + CloseHandle(h); + return false; + } + + static bool PointerIsInAnySegment( const Context *pctx, unsigned rva ) + { + for( int i=0; inSegments; ++i ) + { + if (rva >= pctx->pSegments[i][0] && rva < pctx->pSegments[i][0] + pctx->pSegments[i][1]) + return true; + } + + return false; + } + + static const char *GetNameFromHeap(const char *heap, int idx) + { + while(idx--) + while(*heap++); + + return heap; + } + + long VDDebugInfoLookupRVA( Context *pctx, unsigned rva, char *buf, int buflen ) + { + if( !PointerIsInAnySegment(pctx, rva) ) + return -1; + + const unsigned char *pr = pctx->pRVAHeap; + const unsigned char *pr_limit = (const unsigned char *)pctx->pFuncNameHeap; + 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; + } + if( pr >= pr_limit ) + return -1; + + // Decompress name for RVA + const char *fn_name = GetNameFromHeap(pctx->pFuncNameHeap, idx); + + if( !*fn_name ) + fn_name = "(special)"; + + strncpy( buf, fn_name, buflen ); + buf[buflen-1] = 0; + + return rva; + } +} + +bool ReadFromParent( int fd, void *p, int size ) +{ + char *buf = (char *) p; + int got = 0; + while( got < size ) + { + int ret = read( fd, buf+got, size-got ); + if( ret == -1 ) + { + if( errno == EINTR ) + continue; + fprintf( stderr, "Crash handler: error communicating with parent: %s\n", strerror(errno) ); + return false; + } + + if( ret == 0 ) + { + fprintf( stderr, "Crash handler: EOF communicating with parent.\n" ); + return false; + } + + got += ret; + } + + return true; +} + + +// General symbol lookup; uses VDDebugInfo for detailed information within the +// process, and DbgHelp for simpler information about loaded DLLs. +namespace SymbolLookup +{ + HANDLE g_hParent; + + bool InitDbghelp() + { + static bool bInitted = false; + if( !bInitted ) + { + SymSetOptions( SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS ); + + if( !SymInitialize(g_hParent, NULL, TRUE) ) + return false; + + bInitted = true; + } + + return true; + } + + 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(g_hParent, ptr, &disp, pSymbol) ) + return NULL; + + return pSymbol; + } + + const char *Demangle( const char *buf ) + { + if( !InitDbghelp() ) + return buf; + + static char obuf[1024]; + 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; + } + + if( obuf[0] == '_' ) + { + strcat( obuf, "()" ); /* _main -> _main() */ + return obuf+1; /* _main -> main */ + } + + return obuf; + } + + RString CrashChildGetModuleBaseName( HMODULE hMod ) + { + int iCmd = 1; + write( _fileno(stdout), &iCmd, sizeof(iCmd) ); + write( _fileno(stdout), &hMod, sizeof(hMod) ); + + int iFD = fileno(stdin); + int iSize; + if( !ReadFromParent(iFD, &iSize, sizeof(iSize)) ) + return "???"; + RString sName; + char *pBuf = sName.GetBuffer( iSize ); + if( !ReadFromParent(iFD, pBuf, iSize) ) + return "???"; + sName.ReleaseBuffer( iSize ); + return sName; + } + + void SymLookup( VDDebugInfo::Context *pctx, const void *ptr, char *buf ) + { + if( !pctx->Loaded() ) + { + strcpy( buf, "error" ); + return; + } + + MEMORY_BASIC_INFORMATION meminfo; + VirtualQueryEx( g_hParent, ptr, &meminfo, sizeof meminfo ); + + char tmp[512]; + if( VDDebugInfoLookupRVA(pctx, (unsigned int)ptr, tmp, sizeof(tmp)) >= 0 ) + { + wsprintf( buf, "%08x: %s", ptr, Demangle(tmp) ); + return; + } + + + RString sName = CrashChildGetModuleBaseName( (HMODULE)meminfo.AllocationBase ); + + DWORD64 disp; + SYMBOL_INFO *pSymbol = GetSym( (unsigned int)ptr, disp ); + + if( pSymbol ) + { + wsprintf( buf, "%08lx: %s!%s [%08lx+%lx+%lx]", + (unsigned long) ptr, sName.c_str(), 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, sName.c_str(), + (unsigned long) meminfo.AllocationBase ); + } +} + +namespace +{ + +RString SpliceProgramPath( RString fn ) +{ + char szBuf[MAX_PATH]; + GetModuleFileName( NULL, szBuf, sizeof(szBuf) ); + + char szModName[MAX_PATH]; + char *pszFile; + GetFullPathName( szBuf, sizeof(szModName), szModName, &pszFile ); + strcpy( pszFile, fn ); + + return szModName; +} + +namespace +{ + HFONT hFontMono = NULL; + VDDebugInfo::Context g_debugInfo; + + RString ReportCallStack( const void * const *Backtrace ) + { + if( !g_debugInfo.Loaded() ) + return ssprintf( "debug resource file '%s': %s.\n", g_debugInfo.sFilename, g_debugInfo.szError ); + + if( g_debugInfo.nBuildNumber != int(version_num) ) + { + return ssprintf( "Incorrect %s file (build %d, expected %d) for this version of " PRODUCT_NAME " -- call stack unavailable.\n", + g_debugInfo.sFilename, g_debugInfo.nBuildNumber, int(version_num) ); + } + + RString sRet; + for( int i = 0; Backtrace[i]; ++i ) + { + char buf[10240]; + SymbolLookup::SymLookup( &g_debugInfo, Backtrace[i], buf ); + sRet += ssprintf( "%s\n", buf ); + } + + return sRet; + } +} + +struct CompleteCrashData +{ + CrashInfo m_CrashInfo; + RString m_sInfo; + RString m_sAdditionalLog; + RString m_sCrashedThread; + vector m_asRecent; + vector m_asCheckpoints; +}; + +static void MakeCrashReport( const CompleteCrashData &Data, RString &sOut ) +{ + sOut += ssprintf( + "%s crash report (build %d, %s)\n" + "--------------------------------------\n\n", + PRODUCT_NAME_VER, version_num, version_time ); + + sOut += ssprintf( "%s\n", Data.m_CrashInfo.m_CrashReason ); + sOut += ssprintf( "\n" ); + + // Dump thread stacks + static char buf[1024*32]; + sOut += ssprintf( "%s\n", join("\n", Data.m_asCheckpoints).c_str() ); + + sOut += ReportCallStack( Data.m_CrashInfo.m_BacktracePointers ); + sOut += ssprintf( "\n" ); + + if( Data.m_CrashInfo.m_AlternateThreadBacktrace[0] ) + { + for( int i = 0; i < CrashInfo::MAX_BACKTRACE_THREADS; ++i ) + { + if( !Data.m_CrashInfo.m_AlternateThreadBacktrace[i][0] ) + continue; + + sOut += ssprintf( "Thread %s:\n", Data.m_CrashInfo.m_AlternateThreadName[i] ); + sOut += ssprintf( "\n" ); + sOut += ReportCallStack( Data.m_CrashInfo.m_AlternateThreadBacktrace[i] ); + sOut += ssprintf( "" ); + } + } + + sOut += ssprintf( "Static log:\n" ); + sOut += ssprintf( "%s\n", Data.m_sInfo.c_str() ); + sOut += ssprintf( "%s\n", Data.m_sAdditionalLog.c_str() ); + sOut += ssprintf( "\n" ); + + sOut += ssprintf( "Partial log:\n" ); + for( size_t i = 0; i < Data.m_asRecent.size(); ++i ) + sOut += ssprintf( "%s\n", Data.m_asRecent[i].c_str() ); + sOut += ssprintf( "\n" ); + + sOut += ssprintf( "-- End of report\n" ); +} + +static void DoSave( const CompleteCrashData &Data ) +{ + RString sReport; + MakeCrashReport( Data, sReport ); + + RString sName = SpliceProgramPath( "../crashinfo.txt" ); + + SetFileAttributes( sName, FILE_ATTRIBUTE_NORMAL ); + FILE *pFile = fopen( sName, "w+" ); + if( pFile == NULL ) + return; + fprintf( pFile, "%s", sReport.c_str() ); + + fclose( pFile ); + + /* Discourage changing crashinfo.txt. */ + SetFileAttributes( sName, FILE_ATTRIBUTE_READONLY ); +} + +void ViewWithNotepad(const char *str) +{ + char buf[256] = ""; + strcat( buf, "notepad.exe " ); + strcat( buf, str ); + + RString cwd = SpliceProgramPath( "" ); + + 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) +{ + switch(msg) + { + case WM_INITDIALOG: + DialogUtil::SetHeaderFont( hDlg, IDC_STATIC_HEADER_TEXT ); + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_BUTTON_CLOSE: + EndDialog(hDlg, FALSE); + return TRUE; + case IDOK: + // EndDialog(hDlg, TRUE); /* don't always exit on ENTER */ + return TRUE; + case IDC_VIEW_LOG: + ViewWithNotepad("../log.txt"); + break; + case IDC_CRASH_SAVE: + ViewWithNotepad("../crashinfo.txt"); + return TRUE; + case IDC_BUTTON_RESTART: + { + RString cwd = SpliceProgramPath(""); + + 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( REPORT_BUG_URL ); + break; + } + break; + } + + return FALSE; +} + + +void ChildProcess() +{ + CompleteCrashData Data; + /* Read the crash data from the crashed parent. */ + { + _setmode( _fileno(stdin), O_BINARY ); + + /* 0. Read the parent handle. */ + int iFD = fileno(stdin); + if( !ReadFromParent(iFD, &SymbolLookup::g_hParent, sizeof(SymbolLookup::g_hParent)) ) + return; + + /* 1. Read the CrashData. */ + if( !ReadFromParent(iFD, &Data.m_CrashInfo, sizeof(Data.m_CrashInfo)) ) + return; + + /* 2. Read info. */ + int iSize; + if( !ReadFromParent(iFD, &iSize, sizeof(iSize)) ) + return; + + char *pBuf = Data.m_sInfo.GetBuffer( iSize ); + if( !ReadFromParent(iFD, pBuf, iSize) ) + return; + Data.m_sInfo.ReleaseBuffer( iSize ); + + /* 3. Read AdditionalLog. */ + if( !ReadFromParent(iFD, &iSize, sizeof(iSize)) ) + return; + + pBuf = Data.m_sAdditionalLog.GetBuffer( iSize ); + if( !ReadFromParent(iFD, pBuf, iSize) ) + return; + Data.m_sAdditionalLog.ReleaseBuffer( iSize ); + + /* 4. Read RecentLogs. */ + int iCnt = 0; + if( !ReadFromParent(iFD, &iCnt, sizeof(iCnt)) ) + return; + for( int i = 0; i < iCnt; ++i ) + { + if( !ReadFromParent(iFD, &iSize, sizeof(iSize)) ) + return; + RString sBuf; + pBuf = sBuf.GetBuffer( iSize ); + if( !ReadFromParent(iFD, pBuf, iSize) ) + return; + Data.m_asRecent.push_back( sBuf ); + sBuf.ReleaseBuffer( iSize ); + } + + /* 5. Read CHECKPOINTs. */ + if( !ReadFromParent(iFD, &iSize, sizeof(iSize)) ) + return; + + RString sBuf; + pBuf = sBuf.GetBuffer( iSize ); + if( !ReadFromParent(iFD, pBuf, iSize) ) + return; + + split( sBuf, "$$", Data.m_asCheckpoints ); + sBuf.ReleaseBuffer( iSize ); + + /* 6. Read the crashed thread's name. */ + if( !ReadFromParent(iFD, &iSize, sizeof(iSize)) ) + return; + pBuf = Data.m_sCrashedThread.GetBuffer( iSize ); + if( !ReadFromParent(iFD, pBuf, iSize) ) + return; + Data.m_sCrashedThread.ReleaseBuffer(); + } + + VDDebugInfoInitFromFile( &g_debugInfo ); + DoSave( Data ); + VDDebugInfoDeinit( &g_debugInfo ); + + /* Tell the crashing process that it can exit. Be sure to write crashinfo.txt first. */ + int iCmd = 0; + write( _fileno(stdout), &iCmd, sizeof(iCmd) ); + + /* Now that we've done that, the process is gone. Don't use g_hParent. */ + CloseHandle( SymbolLookup::g_hParent ); + SymbolLookup::g_hParent = NULL; + + 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" + ); + + if( !hFontMono ) + hFontMono = (HFONT) GetStockObject( ANSI_FIXED_FONT ); + + /* Little trick to get an HINSTANCE of ourself without having access to the hwnd ... */ + { + TCHAR szFullAppPath[MAX_PATH]; + GetModuleFileName( NULL, szFullAppPath, MAX_PATH ); + HINSTANCE hHandle = LoadLibrary( szFullAppPath ); + + DialogBoxParam( hHandle, MAKEINTRESOURCE(IDD_DISASM_CRASH), NULL, CrashDlgProc, NULL ); + } +} + +} + +void CrashHandler::CrashHandlerHandleArgs( int argc, char* argv[] ) +{ + if( argc == 2 && !strcmp(argv[1], CHILD_MAGIC_PARAMETER) ) + { + ChildProcess(); + exit(0); + } +} + +/* + * (c) 2003-2006 Glenn Maynard + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/archutils/Win32/CrashHandlerInternal.h b/stepmania/src/archutils/Win32/CrashHandlerInternal.h new file mode 100644 index 0000000000..895d5aa043 --- /dev/null +++ b/stepmania/src/archutils/Win32/CrashHandlerInternal.h @@ -0,0 +1,52 @@ +#ifndef CRASH_HANDLER_INTERNAL_H +#define CRASH_HANDLER_INTERNAL_H + +#define BACKTRACE_MAX_SIZE 100 + +struct CrashInfo +{ + char m_CrashReason[1024*8]; + + const void *m_BacktracePointers[BACKTRACE_MAX_SIZE]; + + enum { MAX_BACKTRACE_THREADS = 32 }; + const void *m_AlternateThreadBacktrace[MAX_BACKTRACE_THREADS][BACKTRACE_MAX_SIZE]; + char m_AlternateThreadName[MAX_BACKTRACE_THREADS][128]; + + CrashInfo() + { + m_CrashReason[0] = 0; + memset( m_AlternateThreadBacktrace, 0, sizeof(m_AlternateThreadBacktrace) ); + memset( m_AlternateThreadName, 0, sizeof(m_AlternateThreadName) ); + m_BacktracePointers[0] = NULL; + } +}; + +#define CHILD_MAGIC_PARAMETER "--private-do-crash-handler" + +#endif + +/* + * (c) 2003-2006 Glenn Maynard + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */