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. + */