on mutex deadlock, get a backtrace of the thread that we're deadlocked

with
This commit is contained in:
Glenn Maynard
2004-02-25 21:08:10 +00:00
parent 15e19d4885
commit 93c86a63a4
2 changed files with 243 additions and 159 deletions
+174 -94
View File
@@ -38,7 +38,7 @@
static HFONT hFontMono = NULL; static HFONT hFontMono = NULL;
static void DoSave(const EXCEPTION_POINTERS *pExc); static void DoSave();
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@@ -52,6 +52,7 @@ extern unsigned long version_num;
extern HINSTANCE g_hInstance; extern HINSTANCE g_hInstance;
#define BACKTRACE_MAX_SIZE 100
// WARNING: This is called from crash-time conditions! No malloc() or new!!! // WARNING: This is called from crash-time conditions! No malloc() or new!!!
@@ -69,7 +70,10 @@ static void SpliceProgramPath(char *buf, int bufsiz, const char *fn) {
strcpy(pszFile, fn); strcpy(pszFile, fn);
} }
struct VDDebugInfoContext { struct VDDebugInfoContext
{
VDDebugInfoContext() { pRVAHeap=NULL; }
bool Loaded() const { return pRVAHeap != NULL; }
void *pRawBlock; void *pRawBlock;
int nBuildNumber; int nBuildNumber;
@@ -113,9 +117,47 @@ static const struct ExceptionLookup {
{ NULL }, { NULL },
}; };
static const char *LookupException( DWORD code )
{
for( int i = 0; exceptions[i].code; ++i )
if( exceptions[i].code == code )
return exceptions[i].name;
return NULL;
}
struct CrashInfo
{
char m_CrashReason[256];
const void *m_BacktracePointers[BACKTRACE_MAX_SIZE];
const void *m_AlternateThreadBacktrace[BACKTRACE_MAX_SIZE];
CrashInfo()
{
m_CrashReason[0] = 0;
m_BacktracePointers[0] = m_AlternateThreadBacktrace[0] = NULL;
}
};
static CrashInfo g_CrashInfo;
static void GetReason( const EXCEPTION_RECORD *pRecord, CrashInfo *crash )
{
// fill out bomb reason
const char *reason = LookupException( pRecord->ExceptionCode );
if( reason == NULL )
wsprintf( crash->m_CrashReason, "Crash reason: unknown exception 0x%08lx", pRecord->ExceptionCode );
else
{
strcpy( crash->m_CrashReason, "Crash reason: " );
strcat( crash->m_CrashReason, reason );
}
}
long VDDebugInfoLookupRVA(VDDebugInfoContext *pctx, unsigned rva, char *buf, int buflen); long VDDebugInfoLookupRVA(VDDebugInfoContext *pctx, unsigned rva, char *buf, int buflen);
bool VDDebugInfoInitFromMemory(VDDebugInfoContext *pctx, const void *_src); bool VDDebugInfoInitFromMemory(VDDebugInfoContext *pctx, const void *_src);
bool VDDebugInfoInitFromFile(VDDebugInfoContext *pctx, const char *pszFilename); bool VDDebugInfoInitFromFile( VDDebugInfoContext *pctx );
void VDDebugInfoDeinit(VDDebugInfoContext *pctx); void VDDebugInfoDeinit(VDDebugInfoContext *pctx);
@@ -197,13 +239,14 @@ long __stdcall CrashHandler(EXCEPTION_POINTERS *pExc)
// Attempt to read debug file. // Attempt to read debug file.
char buf[1024]; VDDebugInfoInitFromFile( &g_debugInfo );
SpliceProgramPath(buf, sizeof buf, "StepMania.vdi");
VDDebugInfoInitFromFile(&g_debugInfo, buf);
/* In case something goes amiss before the user can view the crash /* In case something goes amiss before the user can view the crash
* dump, save it now. */ * dump, save it now. */
DoSave(pExc); 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();
++InHere; ++InHere;
@@ -231,8 +274,7 @@ long __stdcall CrashHandler(EXCEPTION_POINTERS *pExc)
GetModuleFileName(NULL, szFullAppPath, MAX_PATH); GetModuleFileName(NULL, szFullAppPath, MAX_PATH);
HINSTANCE handle = LoadLibrary(szFullAppPath); HINSTANCE handle = LoadLibrary(szFullAppPath);
DialogBoxParam(handle, MAKEINTRESOURCE(IDD_DISASM_CRASH), NULL, DialogBoxParam(handle, MAKEINTRESOURCE(IDD_DISASM_CRASH), NULL, CrashDlgProc, (LPARAM)pExc);
CrashDlgProc, (LPARAM)pExc);
} }
VDDebugInfoDeinit(&g_debugInfo); VDDebugInfoDeinit(&g_debugInfo);
@@ -269,43 +311,19 @@ static void Report(HWND hwndList, HANDLE hFile, const char *format, ...) {
} }
} }
static void ReportReason(HWND hwndReason, HANDLE hFile, const EXCEPTION_POINTERS *const pExc)
static void ReportReason( HWND hwndReason, HANDLE hFile, const CrashInfo *pCrash )
{ {
const EXCEPTION_RECORD *const pRecord = (const EXCEPTION_RECORD *)pExc->ExceptionRecord;
// fill out bomb reason
const struct ExceptionLookup *pel = exceptions;
while(pel->code) {
if (pel->code == pRecord->ExceptionCode)
break;
++pel;
}
// Unfortunately, EXCEPTION_ACCESS_VIOLATION doesn't seem to provide
// us with the read/write flag and virtual address as the docs say...
// *sigh*
char buf[256] = "";
if (!pel->code)
wsprintf( buf, "Crash reason: unknown exception 0x%08lx", pRecord->ExceptionCode );
else
{
strcpy( buf, "Crash reason: " );
strcat( buf, pel->name );
}
if( hwndReason ) if( hwndReason )
SetWindowText( hwndReason, buf ); SetWindowText( hwndReason, pCrash->m_CrashReason );
if( hFile ) if( hFile )
Report( NULL, hFile, buf ); Report( NULL, hFile, pCrash->m_CrashReason );
} }
static const char *GetNameFromHeap(const char *heap, int idx) { static const char *GetNameFromHeap(const char *heap, int idx)
{
while(idx--) while(idx--)
while(*heap++); while(*heap++);
@@ -314,7 +332,8 @@ static const char *GetNameFromHeap(const char *heap, int idx) {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
static bool IsValidCall(char *buf, int len) { static bool IsValidCall(char *buf, int len)
{
// Permissible CALL sequences that we care about: // Permissible CALL sequences that we care about:
// //
// E8 xx xx xx xx CALL near relative // E8 xx xx xx xx CALL near relative
@@ -450,8 +469,14 @@ void VDDebugInfoDeinit(VDDebugInfoContext *pctx) {
} }
} }
bool VDDebugInfoInitFromFile(VDDebugInfoContext *pctx, const char *pszFilename) bool VDDebugInfoInitFromFile( VDDebugInfoContext *pctx )
{ {
if( pctx->Loaded() )
return true;
char pszFilename[1024];
SpliceProgramPath(pszFilename, sizeof(pszFilename), "StepMania.vdi");
pctx->pRawBlock = NULL; pctx->pRawBlock = NULL;
pctx->pRVAHeap = NULL; pctx->pRVAHeap = NULL;
@@ -624,14 +649,19 @@ static bool PointsToValidCall( unsigned long ptr )
return IsValidCall(buf+7, len); return IsValidCall(buf+7, len);
} }
static void do_backtrace( const void **buf, size_t size, void do_backtrace( const void **buf, size_t size,
HANDLE hProcess, HANDLE hThread, const CONTEXT *pContext ) HANDLE hProcess, HANDLE hThread, const CONTEXT *pContext )
{ {
// Retrieve stack pointers. // Retrieve stack pointers.
const char *pStackBase; const char *pStackBase;
{ {
LDT_ENTRY sel; LDT_ENTRY sel;
GetThreadSelectorEntry( hThread, pContext->SegFs, &sel ); if( !GetThreadSelectorEntry( hThread, pContext->SegFs, &sel ) )
{
buf[0] = NULL;
return;
}
const NT_TIB *tib = (NT_TIB *) ((sel.HighWord.Bits.BaseHi<<24)+(sel.HighWord.Bits.BaseMid<<16)+sel.BaseLow); const NT_TIB *tib = (NT_TIB *) ((sel.HighWord.Bits.BaseHi<<24)+(sel.HighWord.Bits.BaseMid<<16)+sel.BaseLow);
const NT_TIB *pTib = tib->Self; const NT_TIB *pTib = tib->Self;
pStackBase = (char *)pTib->StackBase; pStackBase = (char *)pTib->StackBase;
@@ -669,10 +699,51 @@ static void do_backtrace( const void **buf, size_t size,
buf[i++] = NULL; buf[i++] = NULL;
} }
void SymLookup( const void *ptr, char *buf )
static bool ReportCrashCallStack( HWND hwnd, HANDLE hFile, const CONTEXT *pContext )
{ {
if( !g_debugInfo.pRVAHeap ) 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, "%08p: %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, "Could not open debug resource file (StepMania.vdi)."); Report(hwnd, hFile, "Could not open debug resource file (StepMania.vdi).");
return false; return false;
@@ -680,44 +751,15 @@ static bool ReportCrashCallStack( HWND hwnd, HANDLE hFile, const CONTEXT *pConte
if( g_debugInfo.nBuildNumber != int(version_num) ) if( g_debugInfo.nBuildNumber != int(version_num) )
{ {
Report(hwnd, hFile, "Incorrect StepMania.vdi file (build %d, expected %d) for this version of StepMania -- call stack unavailable.", g_debugInfo.nBuildNumber, int(version_num)); Report(hwnd, hFile, "Incorrect StepMania.vdi file (build %d, expected %d) for this version of " PRODUCT_NAME " -- call stack unavailable.", g_debugInfo.nBuildNumber, int(version_num));
return false; return false;
} }
#define BACKTRACE_MAX_SIZE 100 for( int i = 0; Backtrace[i]; ++i )
static const void *BacktracePointers[BACKTRACE_MAX_SIZE];
do_backtrace( BacktracePointers, BACKTRACE_MAX_SIZE, GetCurrentProcess(), GetCurrentThread(), pContext );
for( int i = 0; BacktracePointers[i]; ++i )
{ {
MEMORY_BASIC_INFORMATION meminfo; char buf[10240];
VirtualQuery( BacktracePointers[i], &meminfo, sizeof meminfo ); SymLookup( Backtrace[i], buf );
Report( hwnd, hFile, "%s", buf );
char buf[512];
if( VDDebugInfoLookupRVA(&g_debugInfo, (unsigned int)BacktracePointers[i], buf, sizeof(buf)) >= 0 )
{
Report(hwnd, hFile, "%08p: %s", BacktracePointers[i], Demangle(buf));
} else {
char szName[MAX_PATH];
if( !CrashGetModuleBaseName((HMODULE)meminfo.AllocationBase, szName) )
strcpy( szName, "???" );
DWORD64 disp;
SYMBOL_INFO *pSymbol = GetSym( (unsigned int)BacktracePointers[i], disp );
if( pSymbol )
{
Report(hwnd, hFile, "%08lx: %s!%s [%08lx+%lx+%lx]",
(unsigned long) BacktracePointers[i], szName, pSymbol->Name,
(unsigned long) meminfo.AllocationBase,
(unsigned long) (pSymbol->Address) - (unsigned long) (meminfo.AllocationBase),
(unsigned long) disp);
} else {
Report(hwnd, hFile, "%08lx: %s!%08lx",
(unsigned long) BacktracePointers[i], szName,
(unsigned long) meminfo.AllocationBase );
}
}
} }
return true; return true;
@@ -729,7 +771,7 @@ void WriteBuf( HANDLE hFile, const char *buf )
WriteFile(hFile, buf, strlen(buf), &dwActual, NULL); WriteFile(hFile, buf, strlen(buf), &dwActual, NULL);
} }
static void DoSave(const EXCEPTION_POINTERS *pExc) static void DoSave()
{ {
char szModName2[MAX_PATH]; char szModName2[MAX_PATH];
@@ -744,16 +786,25 @@ static void DoSave(const EXCEPTION_POINTERS *pExc)
"--------------------------------------" "--------------------------------------"
"\r\n", PRODUCT_NAME_VER, version_num); "\r\n", PRODUCT_NAME_VER, version_num);
ReportReason(NULL, hFile, pExc); ReportReason( NULL, hFile, &g_CrashInfo );
Report(NULL, hFile, ""); Report(NULL, hFile, "");
// Dump thread stacks // Dump thread stacks
WriteBuf( hFile, Checkpoints::GetLogs("\r\n") ); WriteBuf( hFile, Checkpoints::GetLogs("\r\n") );
Report(NULL, hFile, ""); Report(NULL, hFile, "");
ReportCrashCallStack( NULL, hFile, pExc->ContextRecord ); ReportCallStack( NULL, hFile, g_CrashInfo.m_BacktracePointers );
Report(NULL, hFile, ""); Report(NULL, hFile, "");
if( g_CrashInfo.m_AlternateThreadBacktrace[0] )
{
Report( NULL, hFile, "Deadlocked with:" );
Report( NULL, hFile, "" );
ReportCallStack( NULL, hFile, g_CrashInfo.m_AlternateThreadBacktrace );
Report(NULL, hFile, "");
}
Report(NULL, hFile, "Static log:"); Report(NULL, hFile, "Static log:");
WriteBuf( hFile, RageLog::GetInfo() ); WriteBuf( hFile, RageLog::GetInfo() );
WriteBuf( hFile, RageLog::GetAdditionalLog() ); WriteBuf( hFile, RageLog::GetAdditionalLog() );
@@ -796,25 +847,22 @@ void ViewWithNotepad(const char *str)
); );
} }
BOOL APIENTRY CrashDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { BOOL APIENTRY CrashDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
static const EXCEPTION_POINTERS *s_pExc; {
static bool s_bHaveCallstack; static bool s_bHaveCallstack;
switch(msg) { switch(msg)
{
case WM_INITDIALOG: case WM_INITDIALOG:
{ {
HWND hwndList = GetDlgItem(hDlg, IDC_CALL_STACK); HWND hwndList = GetDlgItem(hDlg, IDC_CALL_STACK);
HWND hwndReason = GetDlgItem(hDlg, IDC_STATIC_BOMBREASON); HWND hwndReason = GetDlgItem(hDlg, IDC_STATIC_BOMBREASON);
const EXCEPTION_POINTERS *const pExc = (const EXCEPTION_POINTERS *)lParam;
s_pExc = pExc;
if (hFontMono) if (hFontMono)
SendMessage(hwndList, WM_SETFONT, (WPARAM)hFontMono, MAKELPARAM(TRUE, 0)); SendMessage(hwndList, WM_SETFONT, (WPARAM)hFontMono, MAKELPARAM(TRUE, 0));
ReportReason(hwndReason, NULL, pExc); ReportReason( hwndReason, NULL, &g_CrashInfo );
s_bHaveCallstack = ReportCrashCallStack( hwndList, NULL, pExc->ContextRecord ); s_bHaveCallstack = ReportCallStack( hwndList, NULL, g_CrashInfo.m_BacktracePointers );
} }
return TRUE; return TRUE;
@@ -878,7 +926,8 @@ BOOL APIENTRY CrashDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
return FALSE; return FALSE;
} }
void debug_crash() { void NORETURN debug_crash()
{
__try { __try {
__asm xor ebx,ebx __asm xor ebx,ebx
__asm mov eax,dword ptr [ebx] __asm mov eax,dword ptr [ebx]
@@ -887,3 +936,34 @@ void debug_crash() {
} __except(CrashHandler((EXCEPTION_POINTERS*)_exception_info())) { } __except(CrashHandler((EXCEPTION_POINTERS*)_exception_info())) {
} }
} }
/* Get a stack trace of the current thread and the specified thread. */
void NORETURN Crash_BacktraceThread( HANDLE hThread )
{
if( GetThreadId( hThread ) == GetCurrentThreadId() )
{
/* hThread is the current thread. This shouldn't happen. */
strcpy( g_CrashInfo.m_CrashReason, "Crash reason: Thread deadlock (with the current thread?)" );
debug_crash();
}
/* Suspend the other thread we're going to backtrace. (We need to at least suspend
* hThread, for GetThreadContext to work.) */
RageThread::HaltAllThreads( false );
// SuspendThread( hThread );
CONTEXT context;
context.ContextFlags = CONTEXT_FULL;
if( !GetThreadContext( hThread, &context ) )
{
strcpy( g_CrashInfo.m_CrashReason, "Crash reason: Thread deadlock (GetThreadContext failed)" );
debug_crash();
}
static const void *BacktracePointers[BACKTRACE_MAX_SIZE];
do_backtrace( g_CrashInfo.m_AlternateThreadBacktrace, BACKTRACE_MAX_SIZE, GetCurrentProcess(), hThread, &context );
strcpy( g_CrashInfo.m_CrashReason, "Crash reason: Thread deadlock" );
debug_crash();
}
+8 -4
View File
@@ -15,13 +15,17 @@
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#ifndef f_CRASH_H #ifndef CRASH_H
#define f_CRASH_H #define CRASH_H
#include <windows.h>
extern long __stdcall CrashHandler(struct _EXCEPTION_POINTERS *ExceptionInfo); extern long __stdcall CrashHandler(struct _EXCEPTION_POINTERS *ExceptionInfo);
/* Exactly as advertised. (This will bring up the crash handler even /* Exactly as advertised. (This will bring up the crash handler even
* in the debugger.) */ * in the debugger.) */
void debug_crash(); void NORETURN debug_crash();
void do_backtrace( const void **buf, size_t size, HANDLE hProcess, HANDLE hThread, const CONTEXT *pContext );
void SymLookup( const void *ptr, char *buf );
void NORETURN Crash_BacktraceThread( HANDLE hThread );
#endif #endif