Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows: Create minidumps for threads terminated by exceptions #1285

Merged
merged 3 commits into from Sep 2, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 46 additions & 7 deletions xbmc/XBApplicationEx.cpp
Expand Up @@ -27,6 +27,12 @@
#else
#define MEASURE_FUNCTION
#endif
#include "commons/Exception.h"

// Put this here for easy enable and disable
#ifndef _DEBUG
#define XBMC_TRACK_EXCEPTIONS
#endif

CXBApplicationEx::CXBApplicationEx()
{
Expand Down Expand Up @@ -77,7 +83,7 @@ INT CXBApplicationEx::Run(bool renderGUI)
unsigned int frameTime = 0;
const unsigned int noRenderFrameTime = 15; // Simulates ~66fps

#ifndef _DEBUG
#ifdef XBMC_TRACK_EXCEPTIONS
BYTE processExceptionCount = 0;
BYTE frameMoveExceptionCount = 0;
BYTE renderExceptionCount = 0;
Expand All @@ -93,17 +99,28 @@ INT CXBApplicationEx::Run(bool renderGUI)
//-----------------------------------------
// Animate and render a frame
//-----------------------------------------
#ifndef _DEBUG
#ifdef XBMC_TRACK_EXCEPTIONS
try
{
#endif
lastFrameTime = XbmcThreads::SystemClockMillis();
Process();
//reset exception count
#ifndef _DEBUG
#ifdef XBMC_TRACK_EXCEPTIONS
processExceptionCount = 0;

}
catch (const XbmcCommons::UncheckedException &e)
{
e.LogThrowMessage("CApplication::Process()");
processExceptionCount++;
//MAX_EXCEPTION_COUNT exceptions in a row? -> bail out
if (processExceptionCount > MAX_EXCEPTION_COUNT)
{
CLog::Log(LOGERROR, "CApplication::Process(), too many exceptions");
throw;
}
}
catch (...)
{
CLog::Log(LOGERROR, "exception in CApplication::Process()");
Expand All @@ -117,16 +134,27 @@ INT CXBApplicationEx::Run(bool renderGUI)
}
#endif
// Frame move the scene
#ifndef _DEBUG
#ifdef XBMC_TRACK_EXCEPTIONS
try
{
#endif
if (!m_bStop) FrameMove(true, renderGUI);
//reset exception count
#ifndef _DEBUG
#ifdef XBMC_TRACK_EXCEPTIONS
frameMoveExceptionCount = 0;

}
catch (const XbmcCommons::UncheckedException &e)
{
e.LogThrowMessage("CApplication::FrameMove()");
frameMoveExceptionCount++;
//MAX_EXCEPTION_COUNT exceptions in a row? -> bail out
if (frameMoveExceptionCount > MAX_EXCEPTION_COUNT)
{
CLog::Log(LOGERROR, "CApplication::FrameMove(), too many exceptions");
throw;
}
}
catch (...)
{
CLog::Log(LOGERROR, "exception in CApplication::FrameMove()");
Expand All @@ -141,7 +169,7 @@ INT CXBApplicationEx::Run(bool renderGUI)
#endif

// Render the scene
#ifndef _DEBUG
#ifdef XBMC_TRACK_EXCEPTIONS
try
{
#endif
Expand All @@ -152,11 +180,22 @@ INT CXBApplicationEx::Run(bool renderGUI)
if(frameTime < noRenderFrameTime)
Sleep(noRenderFrameTime - frameTime);
}
#ifndef _DEBUG
#ifdef XBMC_TRACK_EXCEPTIONS
//reset exception count
renderExceptionCount = 0;

}
catch (const XbmcCommons::UncheckedException &e)
{
e.LogThrowMessage("CApplication::Render()");
renderExceptionCount++;
//MAX_EXCEPTION_COUNT exceptions in a row? -> bail out
if (renderExceptionCount > MAX_EXCEPTION_COUNT)
{
CLog::Log(LOGERROR, "CApplication::Render(), too many exceptions");
throw;
}
}
catch (...)
{
CLog::Log(LOGERROR, "exception in CApplication::Render()");
Expand Down
3 changes: 1 addition & 2 deletions xbmc/threads/Thread.cpp
Expand Up @@ -196,7 +196,6 @@ void CThread::Sleep(unsigned int milliseconds)

void CThread::Action()
{

try
{
OnStartup();
Expand Down Expand Up @@ -237,7 +236,7 @@ void CThread::Action()
}
catch (...)
{
LOG(LOGERROR, "%s - thread %s, Unhandled exception caught in thread process, aborting. auto delete: %d", __FUNCTION__, m_ThreadName.c_str(), IsAutoDelete());
LOG(LOGERROR, "%s - thread %s, Unhandled exception caught in thread OnExit, aborting. auto delete: %d", __FUNCTION__, m_ThreadName.c_str(), IsAutoDelete());
}
}

2 changes: 2 additions & 0 deletions xbmc/threads/platform/win/ThreadImpl.cpp
Expand Up @@ -70,6 +70,8 @@ void CThread::SetThreadInfo()
__except(EXCEPTION_EXECUTE_HANDLER)
{
}

win32_exception::install_handler();
}

ThreadIdentifier CThread::GetCurrentThreadId()
Expand Down
170 changes: 126 additions & 44 deletions xbmc/threads/platform/win/Win32Exception.cpp
Expand Up @@ -21,85 +21,167 @@

#include "Win32Exception.h"
#include <eh.h>
#include <dbghelp.h>
#include "Util.h"
#include "WIN32Util.h"

#define LOG if(logger) logger->Log

typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);

std::string win32_exception::mVersion;

void win32_exception::install_handler()
{
_set_se_translator(win32_exception::translate);
_set_se_translator(win32_exception::translate);
}

void win32_exception::translate(unsigned code, EXCEPTION_POINTERS* info)
{
// Windows guarantees that *(info->ExceptionRecord) is valid
switch (code) {
switch (code)
{
case EXCEPTION_ACCESS_VIOLATION:
throw access_violation(*(info->ExceptionRecord));
break;
throw access_violation(info);
break;
default:
throw win32_exception(*(info->ExceptionRecord));
}
throw win32_exception(info);
}
}

win32_exception::win32_exception(const EXCEPTION_RECORD& info, const char* classname) :
XbmcCommons::Exception(classname ? classname : "win32_exception"),
mWhat("Win32 exception"), mWhere(info.ExceptionAddress), mCode(info.ExceptionCode)
win32_exception::win32_exception(EXCEPTION_POINTERS* info, const char* classname) :
XbmcCommons::UncheckedException(classname ? classname : "win32_exception"),
mWhat("Win32 exception"), mWhere(info->ExceptionRecord->ExceptionAddress), mCode(info->ExceptionRecord->ExceptionCode), mExceptionPointers(info)
{
switch (info.ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
mWhat = "Access violation";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
mWhat = "Division by zero";
break;
}
// Windows guarantees that *(info->ExceptionRecord) is valid
switch (info->ExceptionRecord->ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
mWhat = "Access violation";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
mWhat = "Division by zero";
break;
}
}

void win32_exception::LogThrowMessage(const char *prefix) const
{
if( prefix )
LOG(LOGERROR, "%s : %s (code:0x%08x) at 0x%08x", prefix, (unsigned int) what(), code(), where());
LOG(LOGERROR, "Unhandled exception in %s : %s (code:0x%08x) at 0x%08x", prefix, (unsigned int) what(), code(), where());
else
LOG(LOGERROR, "%s (code:0x%08x) at 0x%08x", what(), code(), where());
LOG(LOGERROR, "Unhandled exception in %s (code:0x%08x) at 0x%08x", what(), code(), where());
write_minidump();
}

access_violation::access_violation(const EXCEPTION_RECORD& info)
: win32_exception(info,"access_voilation"), mAccessType(Invalid), mBadAddress(0)
bool win32_exception::write_minidump(EXCEPTION_POINTERS* pEp)
{
switch(info.ExceptionInformation[0])
{
case 0:
mAccessType = Read;
break;
case 1:
mAccessType = Write;
break;
case 8:
mAccessType = DEP;
break;
}
mBadAddress = reinterpret_cast<win32_exception ::Address>(info.ExceptionInformation[1]);
// Create the dump file where the xbmc.exe resides
bool returncode = false;
CStdString dumpFileName;
SYSTEMTIME stLocalTime;
GetLocalTime(&stLocalTime);

dumpFileName.Format("xbmc_crashlog-%s-%04d%02d%02d-%02d%02d%02d.dmp",
mVersion.c_str(),
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);

dumpFileName.Format("%s\\%s", CWIN32Util::GetProfilePath().c_str(), CUtil::MakeLegalFileName(dumpFileName));

HANDLE hDumpFile = CreateFile(dumpFileName.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);

if (hDumpFile == INVALID_HANDLE_VALUE)
{
LOG(LOGERROR, "CreateFile '%s' failed with error id %d", dumpFileName.c_str(), GetLastError());
goto cleanup;
}

// Load the DBGHELP DLL
HMODULE hDbgHelpDll = ::LoadLibrary("DBGHELP.DLL");
if (!hDbgHelpDll)
{
LOG(LOGERROR, "LoadLibrary 'DBGHELP.DLL' failed with error id %d", GetLastError());
goto cleanup;
}

MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDbgHelpDll, "MiniDumpWriteDump");
if (!pDump)
{
LOG(LOGERROR, "Failed to locate MiniDumpWriteDump with error id %d", GetLastError());
goto cleanup;
}

// Initialize minidump structure
MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = pEp;
mdei.ClientPointers = FALSE;

// Call the minidump api with normal dumping
// We can get more detail information by using other minidump types but the dump file will be
// extremely large.
BOOL bMiniDumpSuccessful = pDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &mdei, 0, NULL);
if( !bMiniDumpSuccessful )
{
LOG(LOGERROR, "MiniDumpWriteDump failed with error id %d", GetLastError());
goto cleanup;
}

returncode = true;

cleanup:

if (hDumpFile != INVALID_HANDLE_VALUE)
CloseHandle(hDumpFile);

if (hDbgHelpDll)
FreeLibrary(hDbgHelpDll);

return returncode;
}

access_violation::access_violation(EXCEPTION_POINTERS* info) :
win32_exception(info,"access_voilation"), mAccessType(Invalid), mBadAddress(0)
{
switch(info->ExceptionRecord->ExceptionInformation[0])
{
case 0:
mAccessType = Read;
break;
case 1:
mAccessType = Write;
break;
case 8:
mAccessType = DEP;
break;
}
mBadAddress = reinterpret_cast<win32_exception ::Address>(info->ExceptionRecord->ExceptionInformation[1]);
}

void access_violation::LogThrowMessage(const char *prefix) const
{
if( prefix )
if( mAccessType == Write)
LOG(LOGERROR, "%s : %s at 0x%08x: Writing location 0x%08x", prefix, what(), where(), address());
LOG(LOGERROR, "Unhandled exception in %s : %s at 0x%08x: Writing location 0x%08x", prefix, what(), where(), address());
else if( mAccessType == Read)
LOG(LOGERROR, "%s : %s at 0x%08x: Reading location 0x%08x", prefix, what(), where(), address());
LOG(LOGERROR, "Unhandled exception in %s : %s at 0x%08x: Reading location 0x%08x", prefix, what(), where(), address());
else if( mAccessType == DEP)
LOG(LOGERROR, "%s : %s at 0x%08x: DEP violation, location 0x%08x", prefix, what(), where(), address());
LOG(LOGERROR, "Unhandled exception in %s : %s at 0x%08x: DEP violation, location 0x%08x", prefix, what(), where(), address());
else
LOG(LOGERROR, "%s : %s at 0x%08x: unknown access type, location 0x%08x", prefix, what(), where(), address());
LOG(LOGERROR, "Unhandled exception in %s : %s at 0x%08x: unknown access type, location 0x%08x", prefix, what(), where(), address());
else
if( mAccessType == Write)
LOG(LOGERROR, "%s at 0x%08x: Writing location 0x%08x", what(), where(), address());
LOG(LOGERROR, "Unhandled exception in %s at 0x%08x: Writing location 0x%08x", what(), where(), address());
else if( mAccessType == Read)
LOG(LOGERROR, "%s at 0x%08x: Reading location 0x%08x", what(), where(), address());
LOG(LOGERROR, "Unhandled exception in %s at 0x%08x: Reading location 0x%08x", what(), where(), address());
else if( mAccessType == DEP)
LOG(LOGERROR, "%s at 0x%08x: DEP violation, location 0x%08x", what(), where(), address());
LOG(LOGERROR, "Unhandled exception in %s at 0x%08x: DEP violation, location 0x%08x", what(), where(), address());
else
LOG(LOGERROR, "%s at 0x%08x: unknown access type, location 0x%08x", what(), where(), address());
LOG(LOGERROR, "Unhandled exception in %s at 0x%08x: unknown access type, location 0x%08x", what(), where(), address());

write_minidump();
}
12 changes: 9 additions & 3 deletions xbmc/threads/platform/win/Win32Exception.h
Expand Up @@ -25,23 +25,29 @@
#include <exception>
#include "commons/Exception.h"

class win32_exception: public XbmcCommons::Exception
class win32_exception: public XbmcCommons::UncheckedException
{
public:
typedef const void* Address; // OK on Win32 platform

static void install_handler();
static void set_version(std::string version) { mVersion = version; };
virtual const char* what() const { return mWhat; };
Address where() const { return mWhere; };
unsigned code() const { return mCode; };
virtual void LogThrowMessage(const char *prefix) const;
static bool write_minidump(EXCEPTION_POINTERS* pEp);
protected:
win32_exception(const EXCEPTION_RECORD& info, const char* classname = NULL);
win32_exception(EXCEPTION_POINTERS*, const char* classname = NULL);
static void translate(unsigned code, EXCEPTION_POINTERS* info);

inline bool write_minidump() const { return write_minidump(mExceptionPointers); };
private:
const char* mWhat;
Address mWhere;
unsigned mCode;
EXCEPTION_POINTERS *mExceptionPointers;
static std::string mVersion;
};

class access_violation: public win32_exception
Expand All @@ -62,5 +68,5 @@ class access_violation: public win32_exception
private:
access_type mAccessType;
Address mBadAddress;
access_violation(const EXCEPTION_RECORD& info);
access_violation(EXCEPTION_POINTERS* info);
};