diff options
author | WiSo <wiso@xbmc.org> | 2013-02-02 14:42:55 +0100 |
---|---|---|
committer | wsoltys <wiso@no.way> | 2013-02-04 20:41:49 +0100 |
commit | 5f37b1179dfe266fb2db1f3122a004511b9b4bf7 (patch) | |
tree | e7e909a5e9742bbab78636e766f4b0cb896bbe7a | |
parent | c508394d89aa61a438a7473b1518967d264a87a7 (diff) |
[WIN32] implemented a poor man's stacktrace for windows.
-rw-r--r-- | xbmc/threads/platform/win/Win32Exception.cpp | 165 | ||||
-rw-r--r-- | xbmc/threads/platform/win/Win32Exception.h | 2 | ||||
-rw-r--r-- | xbmc/win32/XBMC_PC.cpp | 1 |
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;; } |