Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Windows: Create minidumps for threads terminated by exceptions #1285

Merged
merged 3 commits into from

2 participants

@wsoltys
Collaborator

This is just a resurrection of #813. I had to move the writedump methods to XBApplicationEx.cpp because they wasn't caught in thread.cpp.
I've added quite some code duplication in XBApplicationEx.cpp because I didn't want to introduce new methods or goto's. Please comment if there's a better way.

@wsoltys
Collaborator

@jimfcarroll I've added some minidumps to thread.cpp as CrystalP did: wsoltys@fe6cc74 (somehow I can't see that yet in the pr). Do I still need the own catch routine there?

@jimfcarroll
Collaborator

I tried to take a quick look but didn't get far enough to understand more than what you're trying to do. I should be able to take a closer look over the weekend.

Just so you know, when I was working with CrystalP I added the abstract Exception handling so that you could plug in logging (minidump, etc) in the LogThrowMessage of the Win32Exception. I never heard back from him as to whether that worked for him.

@wsoltys
Collaborator

Thanks for looking into it. I forced a division by zero in a thread to test it but it was only caught in the default handler (...). After calling win32_exception::install_handler(); in CThread:action (see 1b436e6) it worked at least with the two catch's I've added.

@jimfcarroll
Collaborator

I want to do the same thing. where is a good place for the divide by zero?

@wsoltys
Collaborator

MP3Codec.cpp comment l60 and play a mp3 file :)

@wsoltys
Collaborator

note that for debugging the catch's in xbapplicationex are ifdef out that the ide catches the exception.

@wsoltys
Collaborator

okay, squashed and rebased with jcarrolls changes.

@wsoltys wsoltys merged commit c2b9d75 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
53 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()");
View
3  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());
}
}
View
2  xbmc/threads/platform/win/ThreadImpl.cpp
@@ -70,6 +70,8 @@ void CThread::SetThreadInfo()
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
+
+ win32_exception::install_handler();
}
ThreadIdentifier CThread::GetCurrentThreadId()
View
170 xbmc/threads/platform/win/Win32Exception.cpp
@@ -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();
}
View
12 xbmc/threads/platform/win/Win32Exception.h
@@ -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
@@ -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);
};
View
68 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
Something went wrong with that request. Please try again.