aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWiSo <wiso@xbmc.org>2013-02-02 14:42:55 +0100
committerwsoltys <wiso@no.way>2013-02-04 20:41:49 +0100
commit5f37b1179dfe266fb2db1f3122a004511b9b4bf7 (patch)
treee7e909a5e9742bbab78636e766f4b0cb896bbe7a
parentc508394d89aa61a438a7473b1518967d264a87a7 (diff)
[WIN32] implemented a poor man's stacktrace for windows.
-rw-r--r--xbmc/threads/platform/win/Win32Exception.cpp165
-rw-r--r--xbmc/threads/platform/win/Win32Exception.h2
-rw-r--r--xbmc/win32/XBMC_PC.cpp1
3 files changed, 166 insertions, 2 deletions
diff --git a/xbmc/threads/platform/win/Win32Exception.cpp b/xbmc/threads/platform/win/Win32Exception.cpp
index 27eefa4204..38628fecd5 100644
--- a/xbmc/threads/platform/win/Win32Exception.cpp
+++ b/xbmc/threads/platform/win/Win32Exception.cpp
@@ -23,6 +23,7 @@
#include <dbghelp.h>
#include "Util.h"
#include "WIN32Util.h"
+#include "utils/StringUtils.h"
#define LOG if(logger) logger->Log
@@ -31,6 +32,37 @@ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hF
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
+// StackWalk64()
+typedef BOOL (__stdcall *tSW)(
+ DWORD MachineType,
+ HANDLE hProcess,
+ HANDLE hThread,
+ LPSTACKFRAME64 StackFrame,
+ PVOID ContextRecord,
+ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
+ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
+ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
+ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
+
+// SymInitialize()
+typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
+// SymCleanup()
+typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
+// SymGetSymFromAddr64()
+typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
+// UnDecorateSymbolName()
+typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName, DWORD UndecoratedLength, DWORD Flags );
+// SymGetLineFromAddr64()
+typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
+// SymGetModuleBase64()
+typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );
+// SymFunctionTableAccess64()
+typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );
+// SymGetOptions()
+typedef DWORD (__stdcall *tSGO)( VOID );
+// SymSetOptions()
+typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
+
std::string win32_exception::mVersion;
void win32_exception::install_handler()
@@ -40,7 +72,7 @@ void win32_exception::install_handler()
void win32_exception::translate(unsigned code, EXCEPTION_POINTERS* info)
{
- switch (code)
+ switch (code)
{
case EXCEPTION_ACCESS_VIOLATION:
throw access_violation(info);
@@ -73,6 +105,8 @@ void win32_exception::LogThrowMessage(const char *prefix) const
LOG(LOGERROR, "Unhandled exception in %s : %s (code:0x%08x) at 0x%08x", prefix, (unsigned int) what(), code(), where());
else
LOG(LOGERROR, "Unhandled exception in %s (code:0x%08x) at 0x%08x", what(), code(), where());
+
+ write_stacktrace();
write_minidump();
}
@@ -143,7 +177,133 @@ cleanup:
return returncode;
}
-access_violation::access_violation(EXCEPTION_POINTERS* info) :
+/* \brief Writes a simple stack trace to the XBMC user directory.
+ It needs a valid .pdb file to show the method, filename and
+ line number where the exception took place.
+*/
+bool win32_exception::write_stacktrace(EXCEPTION_POINTERS* pEp)
+{
+ #define STACKWALK_MAX_NAMELEN 1024
+
+ std::string dumpFileName, strOutput;
+ CHAR cTemp[STACKWALK_MAX_NAMELEN];
+ DWORD dwBytes;
+ SYSTEMTIME stLocalTime;
+ GetLocalTime(&stLocalTime);
+ bool returncode = false;
+ STACKFRAME64 frame = { 0 };
+ HANDLE hCurProc = GetCurrentProcess();
+
+ HMODULE hDbgHelpDll = ::LoadLibrary("DBGHELP.DLL");
+ if (!hDbgHelpDll)
+ {
+ LOG(LOGERROR, "LoadLibrary 'DBGHELP.DLL' failed with error id %d", GetLastError());
+ goto cleanup;
+ }
+
+ tSI pSI = (tSI) GetProcAddress(hDbgHelpDll, "SymInitialize" );
+ tSGO pSGO = (tSGO) GetProcAddress(hDbgHelpDll, "SymGetOptions" );
+ tSSO pSSO = (tSSO) GetProcAddress(hDbgHelpDll, "SymSetOptions" );
+ tSC pSC = (tSC) GetProcAddress(hDbgHelpDll, "SymCleanup" );
+ tSW pSW = (tSW) GetProcAddress(hDbgHelpDll, "StackWalk64" );
+ tSGSFA pSGSFA = (tSGSFA) GetProcAddress(hDbgHelpDll, "SymGetSymFromAddr64" );
+ tUDSN pUDSN = (tUDSN) GetProcAddress(hDbgHelpDll, "UnDecorateSymbolName" );
+ tSGLFA pSGLFA = (tSGLFA) GetProcAddress(hDbgHelpDll, "SymGetLineFromAddr64" );
+ tSFTA pSFTA = (tSFTA) GetProcAddress(hDbgHelpDll, "SymFunctionTableAccess64" );
+ tSGMB pSGMB = (tSGMB) GetProcAddress(hDbgHelpDll, "SymGetModuleBase64" );
+
+ if(pSI == NULL || pSGO == NULL || pSSO == NULL || pSC == NULL || pSW == NULL || pSGSFA == NULL || pUDSN == NULL || pSGLFA == NULL ||
+ pSFTA == NULL || pSGMB == NULL)
+ goto cleanup;
+
+ dumpFileName = StringUtils::Format("xbmc_stacktrace-%s-%04d%02d%02d-%02d%02d%02d.txt",
+ mVersion.c_str(),
+ stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
+ stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
+
+ dumpFileName = StringUtils::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;
+ }
+
+ frame.AddrPC.Offset = pEp->ContextRecord->Eip; // Current location in program
+ frame.AddrPC.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing
+ frame.AddrStack.Offset = pEp->ContextRecord->Esp; // Stack pointers current value
+ frame.AddrStack.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing
+ frame.AddrFrame.Offset = pEp->ContextRecord->Ebp; // Value of register used to access local function variables.
+ frame.AddrFrame.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing
+
+ if(pSI(hCurProc, NULL, TRUE) == FALSE)
+ goto cleanup;
+
+ DWORD symOptions = pSGO();
+ symOptions |= SYMOPT_LOAD_LINES;
+ symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
+ symOptions &= ~SYMOPT_UNDNAME;
+ symOptions &= ~SYMOPT_DEFERRED_LOADS;
+ symOptions = pSSO(symOptions);
+
+ IMAGEHLP_SYMBOL64 *pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
+ memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
+ pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
+
+ IMAGEHLP_LINE64 Line;
+ memset(&Line, 0, sizeof(Line));
+ Line.SizeOfStruct = sizeof(Line);
+
+ IMAGEHLP_MODULE64 Module;
+ memset(&Module, 0, sizeof(Module));
+ Module.SizeOfStruct = sizeof(Module);
+ int seq=0;
+
+ strOutput = StringUtils::Format("Thread %d (process %d)\r\n", GetCurrentThreadId(), GetCurrentProcessId());
+ WriteFile(hDumpFile, strOutput.c_str(), strOutput.size(), &dwBytes, NULL);
+
+ while(pSW(IMAGE_FILE_MACHINE_I386, hCurProc, GetCurrentThread(), &frame, pEp->ContextRecord, NULL, pSFTA, pSGMB, NULL))
+ {
+ if(frame.AddrPC.Offset != 0)
+ {
+ DWORD64 symoffset=0;
+ DWORD lineoffset=0;
+ strOutput = StringUtils::Format("#%2d", seq++);
+
+ if(pSGSFA(hCurProc, frame.AddrPC.Offset, &symoffset, pSym))
+ {
+ if(pUDSN(pSym->Name, cTemp, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE)>0)
+ strOutput.append(StringUtils::Format(" %s", cTemp));
+ }
+ if(pSGLFA(hCurProc, frame.AddrPC.Offset, &lineoffset, &Line))
+ strOutput.append(StringUtils::Format(" at %s:%d", Line.FileName, Line.LineNumber));
+
+ strOutput.append("\r\n");
+ WriteFile(hDumpFile, strOutput.c_str(), strOutput.size(), &dwBytes, NULL);
+ }
+ }
+ returncode = true;
+
+cleanup:
+ if (pSym)
+ free( pSym );
+
+ if (hDumpFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hDumpFile);
+
+ if(pSC)
+ pSC(hCurProc);
+
+ 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])
@@ -182,5 +342,6 @@ void access_violation::LogThrowMessage(const char *prefix) const
else
LOG(LOGERROR, "Unhandled exception in %s at 0x%08x: unknown access type, location 0x%08x", what(), where(), address());
+ write_stacktrace();
write_minidump();
}
diff --git a/xbmc/threads/platform/win/Win32Exception.h b/xbmc/threads/platform/win/Win32Exception.h
index 6a7e049f5d..e624a3ce48 100644
--- a/xbmc/threads/platform/win/Win32Exception.h
+++ b/xbmc/threads/platform/win/Win32Exception.h
@@ -36,11 +36,13 @@ public:
unsigned code() const { return mCode; };
virtual void LogThrowMessage(const char *prefix) const;
static bool write_minidump(EXCEPTION_POINTERS* pEp);
+ static bool write_stacktrace(EXCEPTION_POINTERS* pEp);
protected:
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); };
+ inline bool write_stacktrace() const { return write_stacktrace(mExceptionPointers); };
private:
const char* mWhat;
Address mWhere;
diff --git a/xbmc/win32/XBMC_PC.cpp b/xbmc/win32/XBMC_PC.cpp
index e228310f2f..64c6383702 100644
--- a/xbmc/win32/XBMC_PC.cpp
+++ b/xbmc/win32/XBMC_PC.cpp
@@ -34,6 +34,7 @@
// Minidump creation function
LONG WINAPI CreateMiniDump( EXCEPTION_POINTERS* pEp )
{
+ win32_exception::write_stacktrace(pEp);
win32_exception::write_minidump(pEp);
return pEp->ExceptionRecord->ExceptionCode;;
}