diff --git a/xbmc/XBApplicationEx.cpp b/xbmc/XBApplicationEx.cpp index 0a73343a3e5da..2e830262f720c 100644 --- a/xbmc/XBApplicationEx.cpp +++ b/xbmc/XBApplicationEx.cpp @@ -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() { @@ -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; @@ -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()"); @@ -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()"); @@ -141,7 +169,7 @@ INT CXBApplicationEx::Run(bool renderGUI) #endif // Render the scene -#ifndef _DEBUG +#ifdef XBMC_TRACK_EXCEPTIONS try { #endif @@ -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()"); diff --git a/xbmc/threads/Thread.cpp b/xbmc/threads/Thread.cpp index 9faac80e5fc11..91eb02389bd42 100644 --- a/xbmc/threads/Thread.cpp +++ b/xbmc/threads/Thread.cpp @@ -196,7 +196,6 @@ void CThread::Sleep(unsigned int milliseconds) void CThread::Action() { - try { OnStartup(); @@ -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()); } } diff --git a/xbmc/threads/platform/win/ThreadImpl.cpp b/xbmc/threads/platform/win/ThreadImpl.cpp index 967bff97bc7de..df084900c3729 100644 --- a/xbmc/threads/platform/win/ThreadImpl.cpp +++ b/xbmc/threads/platform/win/ThreadImpl.cpp @@ -70,6 +70,8 @@ void CThread::SetThreadInfo() __except(EXCEPTION_EXECUTE_HANDLER) { } + + win32_exception::install_handler(); } ThreadIdentifier CThread::GetCurrentThreadId() diff --git a/xbmc/threads/platform/win/Win32Exception.cpp b/xbmc/threads/platform/win/Win32Exception.cpp index 7f80cba710ba6..d9f3bc425c446 100644 --- a/xbmc/threads/platform/win/Win32Exception.cpp +++ b/xbmc/threads/platform/win/Win32Exception.cpp @@ -21,85 +21,167 @@ #include "Win32Exception.h" #include +#include +#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(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(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(); } diff --git a/xbmc/threads/platform/win/Win32Exception.h b/xbmc/threads/platform/win/Win32Exception.h index 9b957513e9d11..53506d48f4959 100644 --- a/xbmc/threads/platform/win/Win32Exception.h +++ b/xbmc/threads/platform/win/Win32Exception.h @@ -25,23 +25,29 @@ #include #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 @@ -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); }; diff --git a/xbmc/win32/XBMC_PC.cpp b/xbmc/win32/XBMC_PC.cpp index 2cf9f4b72a97d..6d6c3e321c802 100644 --- a/xbmc/win32/XBMC_PC.cpp +++ b/xbmc/win32/XBMC_PC.cpp @@ -23,80 +23,19 @@ #include "settings/AppParamParser.h" #include "utils/CharsetConverter.h" #include "utils/log.h" -#include "WIN32Util.h" +#include "threads/platform/win/Win32Exception.h" #include "shellapi.h" #include "dbghelp.h" #include "XBDateTime.h" #include "threads/Thread.h" #include "Application.h" #include "XbmcContext.h" - -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); - +#include "GUIInfoManager.h" // Minidump creation function LONG WINAPI CreateMiniDump( EXCEPTION_POINTERS* pEp ) { - // Create the dump file where the xbmc.exe resides - CStdString errorMsg; - CStdString dumpFile; - CDateTime now(CDateTime::GetCurrentDateTime()); - dumpFile.Format("%s\\xbmc_crashlog-%s-%04i%02i%02i-%02i%02i%02i.dmp", CWIN32Util::GetProfilePath().c_str(), GIT_REV, now.GetYear(), now.GetMonth(), now.GetDay(), now.GetHour(), now.GetMinute(), now.GetSecond()); - HANDLE hFile = CreateFile(dumpFile.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); - - // Call MiniDumpWriteDump api with the dump file - if ( hFile && ( hFile != INVALID_HANDLE_VALUE ) ) - { - // Load the DBGHELP DLL - HMODULE hDbgHelpDll = ::LoadLibrary("DBGHELP.DLL"); - if (hDbgHelpDll) - { - MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDbgHelpDll, "MiniDumpWriteDump"); - if (pDump) - { - // Initialize minidump structure - MINIDUMP_EXCEPTION_INFORMATION mdei; - mdei.ThreadId = CThread::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 - // extermely large. - BOOL rv = pDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, 0, NULL); - if( !rv ) - { - errorMsg.Format("MiniDumpWriteDump failed with error id %d", GetLastError()); - MessageBox(NULL, errorMsg.c_str(), "XBMC: Error", MB_OK|MB_ICONERROR); - } - } - else - { - errorMsg.Format("MiniDumpWriteDump failed to load with error id %d", GetLastError()); - MessageBox(NULL, errorMsg.c_str(), "XBMC: Error", MB_OK|MB_ICONERROR); - } - - // Close the DLL - FreeLibrary(hDbgHelpDll); - } - else - { - errorMsg.Format("LoadLibrary 'DBGHELP.DLL' failed with error id %d", GetLastError()); - MessageBox(NULL, errorMsg.c_str(), "XBMC: Error", MB_OK|MB_ICONERROR); - } - - // Close the file - CloseHandle( hFile ); - } - else - { - errorMsg.Format("CreateFile '%s' failed with error id %d", dumpFile.c_str(), GetLastError()); - MessageBox(NULL, errorMsg.c_str(), "XBMC: Error", MB_OK|MB_ICONERROR); - } - + win32_exception::write_minidump(pEp); return pEp->ExceptionRecord->ExceptionCode;; } @@ -121,6 +60,7 @@ INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR commandLine, INT ) CLog::SetLogLevel(g_advancedSettings.m_logLevel); // Initializes CreateMiniDump to handle exceptions. + win32_exception::set_version(g_infoManager.GetVersion()); SetUnhandledExceptionFilter( CreateMiniDump ); // check if XBMC is already running