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
+235 -155
View File
@@ -38,7 +38,7 @@
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;
#define BACKTRACE_MAX_SIZE 100
// 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);
}
struct VDDebugInfoContext {
struct VDDebugInfoContext
{
VDDebugInfoContext() { pRVAHeap=NULL; }
bool Loaded() const { return pRVAHeap != NULL; }
void *pRawBlock;
int nBuildNumber;
@@ -113,9 +117,47 @@ static const struct ExceptionLookup {
{ 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);
bool VDDebugInfoInitFromMemory(VDDebugInfoContext *pctx, const void *_src);
bool VDDebugInfoInitFromFile(VDDebugInfoContext *pctx, const char *pszFilename);
bool VDDebugInfoInitFromFile( VDDebugInfoContext *pctx );
void VDDebugInfoDeinit(VDDebugInfoContext *pctx);
@@ -197,13 +239,14 @@ long __stdcall CrashHandler(EXCEPTION_POINTERS *pExc)
// Attempt to read debug file.
char buf[1024];
SpliceProgramPath(buf, sizeof buf, "StepMania.vdi");
VDDebugInfoInitFromFile(&g_debugInfo, buf);
VDDebugInfoInitFromFile( &g_debugInfo );
/* In case something goes amiss before the user can view the crash
* 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;
@@ -231,8 +274,7 @@ long __stdcall CrashHandler(EXCEPTION_POINTERS *pExc)
GetModuleFileName(NULL, szFullAppPath, MAX_PATH);
HINSTANCE handle = LoadLibrary(szFullAppPath);
DialogBoxParam(handle, MAKEINTRESOURCE(IDD_DISASM_CRASH), NULL,
CrashDlgProc, (LPARAM)pExc);
DialogBoxParam(handle, MAKEINTRESOURCE(IDD_DISASM_CRASH), NULL, CrashDlgProc, (LPARAM)pExc);
}
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 )
SetWindowText( hwndReason, buf );
SetWindowText( hwndReason, pCrash->m_CrashReason );
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(*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:
//
// 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->pRVAHeap = NULL;
@@ -624,14 +649,19 @@ static bool PointsToValidCall( unsigned long ptr )
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 )
{
// Retrieve stack pointers.
const char *pStackBase;
{
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 *pTib = tib->Self;
pStackBase = (char *)pTib->StackBase;
@@ -669,10 +699,51 @@ static void do_backtrace( const void **buf, size_t size,
buf[i++] = NULL;
}
static bool ReportCrashCallStack( HWND hwnd, HANDLE hFile, const CONTEXT *pContext )
void SymLookup( const void *ptr, char *buf )
{
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).");
return false;
@@ -680,44 +751,15 @@ static bool ReportCrashCallStack( HWND hwnd, HANDLE hFile, const CONTEXT *pConte
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;
}
#define BACKTRACE_MAX_SIZE 100
static const void *BacktracePointers[BACKTRACE_MAX_SIZE];
do_backtrace( BacktracePointers, BACKTRACE_MAX_SIZE, GetCurrentProcess(), GetCurrentThread(), pContext );
for( int i = 0; BacktracePointers[i]; ++i )
for( int i = 0; Backtrace[i]; ++i )
{
MEMORY_BASIC_INFORMATION meminfo;
VirtualQuery( BacktracePointers[i], &meminfo, sizeof meminfo );
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 );
}
}
char buf[10240];
SymLookup( Backtrace[i], buf );
Report( hwnd, hFile, "%s", buf );
}
return true;
@@ -729,7 +771,7 @@ void WriteBuf( HANDLE hFile, const char *buf )
WriteFile(hFile, buf, strlen(buf), &dwActual, NULL);
}
static void DoSave(const EXCEPTION_POINTERS *pExc)
static void DoSave()
{
char szModName2[MAX_PATH];
@@ -744,16 +786,25 @@ static void DoSave(const EXCEPTION_POINTERS *pExc)
"--------------------------------------"
"\r\n", PRODUCT_NAME_VER, version_num);
ReportReason(NULL, hFile, pExc);
ReportReason( NULL, hFile, &g_CrashInfo );
Report(NULL, hFile, "");
// Dump thread stacks
WriteBuf( hFile, Checkpoints::GetLogs("\r\n") );
Report(NULL, hFile, "");
ReportCrashCallStack( NULL, hFile, pExc->ContextRecord );
ReportCallStack( NULL, hFile, g_CrashInfo.m_BacktracePointers );
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:");
WriteBuf( hFile, RageLog::GetInfo() );
WriteBuf( hFile, RageLog::GetAdditionalLog() );
@@ -796,89 +847,87 @@ void ViewWithNotepad(const char *str)
);
}
BOOL APIENTRY CrashDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
static const EXCEPTION_POINTERS *s_pExc;
BOOL APIENTRY CrashDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
static bool s_bHaveCallstack;
switch(msg) {
switch(msg)
{
case WM_INITDIALOG:
{
HWND hwndList = GetDlgItem(hDlg, IDC_CALL_STACK);
HWND hwndReason = GetDlgItem(hDlg, IDC_STATIC_BOMBREASON);
case WM_INITDIALOG:
{
HWND hwndList = GetDlgItem(hDlg, IDC_CALL_STACK);
HWND hwndReason = GetDlgItem(hDlg, IDC_STATIC_BOMBREASON);
const EXCEPTION_POINTERS *const pExc = (const EXCEPTION_POINTERS *)lParam;
if (hFontMono)
SendMessage(hwndList, WM_SETFONT, (WPARAM)hFontMono, MAKELPARAM(TRUE, 0));
s_pExc = pExc;
ReportReason( hwndReason, NULL, &g_CrashInfo );
s_bHaveCallstack = ReportCallStack( hwndList, NULL, g_CrashInfo.m_BacktracePointers );
}
return TRUE;
if (hFontMono)
SendMessage(hwndList, WM_SETFONT, (WPARAM)hFontMono, MAKELPARAM(TRUE, 0));
ReportReason(hwndReason, NULL, pExc);
s_bHaveCallstack = ReportCrashCallStack( hwndList, NULL, pExc->ContextRecord );
}
case WM_COMMAND:
switch(LOWORD(wParam)) {
case IDC_BUTTON_CLOSE:
EndDialog(hDlg, FALSE);
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( "http://sourceforge.net/tracker/?func=add&group_id=37892&atid=421366" );
break;
}
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( "http://sourceforge.net/tracker/?func=add&group_id=37892&atid=421366" );
break;
}
break;
}
return FALSE;
}
void debug_crash() {
void NORETURN debug_crash()
{
__try {
__asm xor ebx,ebx
__asm mov eax,dword ptr [ebx]
@@ -887,3 +936,34 @@ void debug_crash() {
} __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();
}