Skip to content

Commit

Permalink
Merge pull request xbmc#1285 from wsoltys/minidump
Browse files Browse the repository at this point in the history
[WIN32] Create minidumps for threads terminated by exceptions
  • Loading branch information
wsoltys committed Sep 2, 2012
2 parents ee77d6b + f6f1884 commit c2b9d75
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 120 deletions.
53 changes: 46 additions & 7 deletions xbmc/XBApplicationEx.cpp
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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);
};
Loading

0 comments on commit c2b9d75

Please sign in to comment.