?? crash.cpp
字號(hào):
//Crash.cpp
//
//Ported away from Glenn Maynard's Rage.
//Self contained crash reporting.
// DO NOT USE stdio.h! printf() calls malloc()!
//#include <stdio.h>
#if defined(_MSC_VER) && (_MSC_VER > 1100)
#pragma warning (push)
#pragma warning (disable : 4018) // Ignore signing
#endif
#include <stdarg.h>
//#include <crtdbg.h>
#include <windows.h>
#include <tlhelp32.h>
#include "crash.h"
#include "RestartProgram.h"
#include "WindowsResources.h"
#include <iostream> //For this specific implementation
using namespace std;
static void DoSave();
bool g_bAutoRestart = false;
///////////////////////////////////////////////////////////////////////////
extern HINSTANCE g_hInstance;
#define version_num VERSION_NUM
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
unsigned long g_CallBack = NULL;
char ReasonForCrash[10000];
int PosInReason;
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;
GetModuleFileName(NULL, tbuf, sizeof tbuf);
GetFullPathName(tbuf, bufsiz, buf, &pszFile);
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];
};
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" },
{ 0xe06d7363, "Unhandled Microsoft C++ Exception", },
// hmm... '_msc'... gee, who would have thought?
{ DWORD(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[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 )
{
// 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 );
void VDDebugInfoDeinit(VDDebugInfoContext *pctx);
long __stdcall CrashHandler(EXCEPTION_POINTERS *pExc)
{
PosInReason = 0;
//Pfft. Who cares about these? Let's just keep going
switch( pExc->ExceptionRecord->ExceptionCode )
{
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_UNDERFLOW:
case EXCEPTION_FLT_INEXACT_RESULT:
pExc->ContextRecord->FloatSave.ControlWord |= 0x3F;
return EXCEPTION_CONTINUE_EXECUTION;
}
//Make SURE we do not try executing multiple times
static int InHere = 0;
if( InHere > 0 )
{
SetUnhandledExceptionFilter(NULL);
return EXCEPTION_EXECUTE_HANDLER;
}
++InHere;
/* 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();
if ( g_CallBack == 1 )
{
InHere = 0;
throw ( FatalCrashException() );
}
if( g_bAutoRestart )
Win32RestartProgram();
VDDebugInfoDeinit(&g_debugInfo);
InHere = false;
SetUnhandledExceptionFilter(NULL);
/* Forcibly terminate; if we keep going, we'll try to shut down threads and do other
* things that may deadlock, which is confusing for users. */
TerminateProcess( GetCurrentProcess(), 0 );
return EXCEPTION_EXECUTE_HANDLER;
}
static void Report(HWND hwndList, HANDLE hFile, const char *format, ...) {
char buf[10240];
va_list val;
int ch;
int i;
va_start(val, format);
ch = wvsprintf(buf, format, val);
//memcpy ( &(ReasonForCrash) + PosInReason, buf, strlen( buf ) );
for ( i = 0; i< strlen(buf); i++)
if (buf[i] != 0 )
ReasonForCrash[PosInReason+i] = buf[i];
PosInReason += strlen( buf ) + 2;
ReasonForCrash[PosInReason-2] = '\r';
ReasonForCrash[PosInReason-1] = '\n';
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)
{
// Permissible CALL sequences that we care about:
//
// E8 xx xx xx xx CALL near relative
// FF (group 2) CALL near absolute indirect
//
// Minimum sequence is 2 bytes (call eax).
// Maximum sequence is 7 bytes (call dword ptr [eax+disp32]).
if (len >= 5 && buf[-5] == '\xe8')
return true;
// FF 14 xx CALL [reg32+reg32*scale]
if (len >= 3 && buf[-3] == '\xff' && buf[-2]=='\x14')
return true;
// FF 15 xx xx xx xx CALL disp32
if (len >= 6 && buf[-6] == '\xff' && buf[-5]=='\x15')
return true;
// FF 00-3F(!14/15) CALL [reg32]
if (len >= 2 && buf[-2] == '\xff' && (unsigned char)buf[-1] < '\x40')
return true;
// FF D0-D7 CALL reg32
if (len >= 2 && buf[-2] == '\xff' && (buf[-1]&0xF8) == '\xd0')
return true;
// FF 50-57 xx CALL [reg32+reg32*scale+disp8]
if (len >= 3 && buf[-3] == '\xff' && (buf[-2]&0xF8) == '\x50')
return true;
// FF 90-97 xx xx xx xx xx CALL [reg32+reg32*scale+disp32]
if (len >= 7 && buf[-7] == '\xff' && (buf[-6]&0xF8) == '\x90')
return true;
return false;
}
///////////////////////////////////////////////////////////////////////////
static bool IsExecutableProtection(DWORD dwProtect) {
MEMORY_BASIC_INFORMATION meminfo;
// Windows NT/2000 allows Execute permissions, but Win9x seems to
// rip it off. So we query the permissions on our own code block,
// and use it to determine if READONLY/READWRITE should be
// considered 'executable.'
VirtualQuery((void*)IsExecutableProtection, &meminfo, sizeof meminfo);
switch((unsigned char)dwProtect) {
case PAGE_READONLY: // *sigh* Win9x...
case PAGE_READWRITE: // *sigh*
return meminfo.Protect==PAGE_READONLY || meminfo.Protect==PAGE_READWRITE;
case PAGE_EXECUTE:
case PAGE_EXECUTE_READ:
case PAGE_EXECUTE_READWRITE:
case PAGE_EXECUTE_WRITECOPY:
return true;
}
return false;
}
static const char *CrashGetModuleBaseName(HMODULE hmod, char *pszBaseName) {
char szPath1[MAX_PATH];
char szPath2[MAX_PATH];
#if defined( _MSC_VER )
__try {
#endif
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;
#if defined( _MSC_VER )
} __except(1) {
return ( NULL );
}
#endif
return pszBaseName;
}
///////////////////////////////////////////////////////////////////////////
bool VDDebugInfoInitFromMemory(VDDebugInfoContext *pctx, const void *_src) {
const unsigned char *src = (const unsigned char *)_src;
pctx->pRVAHeap = NULL;
if (memcmp((char *)src, "StepMania symbolic debug information", 36))
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;
GetVDIPath( pctx->sFilename, sizeof(pctx->sFilename) );
pctx->pRawBlock = NULL;
pctx->pRVAHeap = NULL;
HANDLE h = CreateFile(pctx->sFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == h)
return false;
do {
DWORD dwFileSize = GetFileSize(h, NULL);
if (dwFileSize == 0xFFFFFFFF)
break;
pctx->pRawBlock = VirtualAlloc(NULL, dwFileSize, MEM_COMMIT, PAGE_READWRITE);
if (!pctx->pRawBlock)
break;
DWORD dwActual;
if (!ReadFile(h, pctx->pRawBlock, dwFileSize, &dwActual, NULL) || dwActual != dwFileSize)
break;
if (VDDebugInfoInitFromMemory(pctx, pctx->pRawBlock)) {
CloseHandle(h);
return true;
}
VirtualFree(pctx->pRawBlock, 0, MEM_RELEASE);
} while(false);
VDDebugInfoDeinit(pctx);
CloseHandle(h);
return false;
}
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -