From f5d08a1e466b69f8c35e3b30e4cf898a71bc8b83 Mon Sep 17 00:00:00 2001 From: Marcus Eggenberger Date: Mon, 10 Nov 2008 22:57:40 +0100 Subject: [PATCH] Automatic back traces on windows. This might break the nightly builds (static windows) (though I really hope not) or have other unforseen side effects. :) --- CMakeLists.txt | 12 +- src/common/CMakeLists.txt | 9 ++ src/common/logbacktrace_unix.cpp | 94 ++++++++++++++ src/common/logbacktrace_win.cpp | 203 +++++++++++++++++++++++++++++++ src/common/quassel.cpp | 133 ++++++-------------- src/common/quassel.h | 2 +- 6 files changed, 348 insertions(+), 105 deletions(-) create mode 100644 src/common/logbacktrace_unix.cpp create mode 100755 src/common/logbacktrace_win.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ac2d308..da59dbdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,12 +162,14 @@ if(STATIC AND CMAKE_COMPILER_IS_GNUCXX) endif(HAVE_SSL) endif(STATIC AND CMAKE_COMPILER_IS_GNUCXX) -if(STATIC AND WIN32) - link_libraries(imm32 winmm) # missing by default :/ - if(HAVE_SSL) +if(WIN32) + #if(STATIC) + link_libraries(imm32 winmm dbghelp) # missing by default :/ + #endif(STATIC) + if(HAVE_SSL) link_libraries(${OPENSSL_LIBRARIES} libeay32MD) - endif(HAVE_SSL) -endif(STATIC AND WIN32) + endif(HAVE_SSL) +endif(WIN32) if(WIN32) set(RC_WIN32 ../pics/win32.rc) # for app icons on windows diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 4218661e..8baf2597 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -24,6 +24,15 @@ set(SOURCES syncableobject.cpp util.cpp) +if(CMAKE_HOST_WIN32) + set(SOURCES ${SOURCES} logbacktrace_win.cpp) +endif(CMAKE_HOST_WIN32) +if(CMAKE_HOST_UNIX) + set(SOURCES ${SOURCES} logbacktrace_unix.cpp) +endif(CMAKE_HOST_UNIX) + + + set(MOC_HDRS aliasmanager.h backlogmanager.h diff --git a/src/common/logbacktrace_unix.cpp b/src/common/logbacktrace_unix.cpp new file mode 100644 index 00000000..431c8021 --- /dev/null +++ b/src/common/logbacktrace_unix.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2005-08 by the Quassel IRC Team * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "quassel.h" + +#if defined(HAVE_EXECINFO) // && !defined(Q_OS_MAC) +# define BUILD_CRASHHANDLER +# include +# include +# include +# include +# include +# include +#endif + +void Quassel::logBacktrace(const QString &filename) { +#ifndef BUILD_CRASHHANDLER + Q_UNUSED(filename) +#else + void* callstack[128]; + int i, frames = backtrace(callstack, 128); + + QFile dumpFile(filename); + dumpFile.open(QIODevice::Append); + QTextStream dumpStream(&dumpFile); + + for (i = 0; i < frames; ++i) { + Dl_info info; + dladdr (callstack[i], &info); + // as a reference: + // typedef struct + // { + // __const char *dli_fname; /* File name of defining object. */ + // void *dli_fbase; /* Load address of that object. */ + // __const char *dli_sname; /* Name of nearest symbol. */ + // void *dli_saddr; /* Exact value of nearest symbol. */ + // } Dl_info; + + #if __LP64__ + int addrSize = 16; + #else + int addrSize = 8; + #endif + + QString funcName; + if(info.dli_sname) { + char *func = abi::__cxa_demangle(info.dli_sname, 0, 0, 0); + if(func) { + funcName = QString(func); + free(func); + } else { + funcName = QString(info.dli_sname); + } + } else { + funcName = QString("0x%1").arg((ulong)(info.dli_saddr), addrSize, 16, QLatin1Char('0')); + } + + // prettificating the filename + QString fileName("???"); + if(info.dli_fname) { + fileName = QString(info.dli_fname); + int slashPos = fileName.lastIndexOf('/'); + if(slashPos != -1) + fileName = fileName.mid(slashPos + 1); + } + + QString debugLine = QString("#%1 %2 0x%3 %4").arg(i, 3, 10) + .arg(fileName, - 20) + .arg((ulong)(callstack[i]), addrSize, 16, QLatin1Char('0')) + .arg(funcName); + + dumpStream << debugLine << "\n"; + qDebug() << qPrintable(debugLine); + } + dumpFile.close(); +#endif /* BUILD_CRASHHANDLER */ +} diff --git a/src/common/logbacktrace_win.cpp b/src/common/logbacktrace_win.cpp new file mode 100755 index 00000000..abcab480 --- /dev/null +++ b/src/common/logbacktrace_win.cpp @@ -0,0 +1,203 @@ +/*************************************************************************** + * Copyright (C) 2005-08 by the Quassel IRC Team * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "quassel.h" + +#include +#include +#include + +// #include +#include +#include + +void loadHelpStackFrame(IMAGEHLP_STACK_FRAME &ihsf, const STACKFRAME64 &stackFrame) { + ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME)); + ihsf.InstructionOffset = stackFrame.AddrPC.Offset; + ihsf.FrameOffset = stackFrame.AddrFrame.Offset; +} + +BOOL CALLBACK EnumSymbolsCB(PSYMBOL_INFO symInfo, ULONG size, PVOID user) { + QStringList *params = (QStringList *)user; + if(symInfo->Flags & SYMFLAG_PARAMETER) { + params->append(symInfo->Name); + } + return TRUE; +} + + +struct EnumModulesContext { + HANDLE hProcess; + QTextStream &stream; + EnumModulesContext(HANDLE hProcess, QTextStream &stream) : hProcess(hProcess), stream(stream) {} +}; + +BOOL CALLBACK EnumModulesCB(PCTSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext) { + IMAGEHLP_MODULE64 mod; + EnumModulesContext *context = (EnumModulesContext *)UserContext; + mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + if(SymGetModuleInfo64(context->hProcess, BaseOfDll, &mod)) { + QString line = QString("%1 0x%2 Image: %3").arg(mod.ModuleName, -14) + .arg(BaseOfDll, 8, 16, QLatin1Char('0')) + .arg(mod.LoadedImageName); + // qDebug() << qPrintable(line); + context->stream << line << '\n'; + + QString pdbName(mod.LoadedPdbName); + if(!pdbName.isEmpty()) { + QString line2 = QString("%1 %2").arg("", 32).arg(pdbName); + // qDebug() << qPrintable(line2); + context->stream << line2 << '\n'; + } + } + return TRUE; +} + +#ifdef _M_IX86 + // Disable global optimization and ignore /GS waning caused by + // inline assembly. + #pragma optimize("g", off) + #pragma warning(push) + #pragma warning(disable : 4748) +#endif +void Quassel::logBacktrace(const QString &filename) { + DWORD MachineType; + CONTEXT Context; + STACKFRAME64 StackFrame; + +#ifdef _M_IX86 + ZeroMemory(&Context, sizeof(CONTEXT)); + Context.ContextFlags = CONTEXT_CONTROL; + __asm { + Label: + mov [Context.Ebp], ebp; + mov [Context.Esp], esp; + mov eax, [Label]; + mov [Context.Eip], eax; + } +#else + RtlCaptureContext(&Context); +#endif + + ZeroMemory(&StackFrame, sizeof(STACKFRAME64)); +#ifdef _M_IX86 + MachineType = IMAGE_FILE_MACHINE_I386; + StackFrame.AddrPC.Offset = Context.Eip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = Context.Ebp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = Context.Esp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#elif _M_X64 + MachineType = IMAGE_FILE_MACHINE_AMD64; + StackFrame.AddrPC.Offset = Context.Rip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = Context.Rsp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = Context.Rsp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#elif _M_IA64 + MachineType = IMAGE_FILE_MACHINE_IA64; + StackFrame.AddrPC.Offset = Context.StIIP; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = Context.IntSp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrBStore.Offset= Context.RsBSP; + StackFrame.AddrBStore.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = Context.IntSp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#else + #error "Unsupported platform" +#endif + + //EnterCriticalSection(&DbgHelpLock); + + QFile logFile(filename); + logFile.open(QIODevice::Append); + QTextStream logStream(&logFile); + + HANDLE hProcess = GetCurrentProcess(); + HANDLE hThread = GetCurrentThread(); + SymInitialize(hProcess, NULL, TRUE); + + DWORD64 dwDisplacement; + + ULONG64 buffer[(sizeof(SYMBOL_INFO) + + MAX_SYM_NAME*sizeof(TCHAR) + + sizeof(ULONG64) - 1) / sizeof(ULONG64)]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); + pSymbol->MaxNameLen = MAX_SYM_NAME; + + IMAGEHLP_MODULE64 mod; + mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + + IMAGEHLP_STACK_FRAME ihsf; + ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME)); + + int i = 0; + while(StackWalk64(MachineType, hProcess, hThread, &StackFrame, &Context, NULL, NULL, NULL, NULL)) { + if(i == 128) + break; + + loadHelpStackFrame(ihsf, StackFrame); + if(StackFrame.AddrPC.Offset != 0) { // Valid frame. + + QString fileName("???"); + if(SymGetModuleInfo64(hProcess, ihsf.InstructionOffset, &mod)) { + fileName = QString(mod.ImageName); + int slashPos = fileName.lastIndexOf('\\'); + if(slashPos != -1) + fileName = fileName.mid(slashPos + 1); + } + QString funcName; + if(SymFromAddr(hProcess, ihsf.InstructionOffset, &dwDisplacement, pSymbol)) { + funcName = QString(pSymbol->Name); + } else { + funcName = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0')); + } + QStringList params; + SymSetContext(hProcess, &ihsf, NULL); + SymEnumSymbols(hProcess, 0, NULL, EnumSymbolsCB, (PVOID)¶ms); + + QString debugLine = QString("#%1 %2 0x%3 %4(%5)").arg(i, 3, 10) + .arg(fileName, -20) + .arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0')) + .arg(funcName) + .arg(params.join(", ")); + // qDebug() << qPrintable(debugLine); + logStream << debugLine << '\n'; + i++; + } else { + break; // we're at the end. + } + } + + // qDebug() << "List of linked Modules:"; + logStream << "\n\nList of linked Modules:\n"; + EnumModulesContext modulesContext(hProcess, logStream); + SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext); + + logFile.close(); +} +#ifdef _M_IX86 + #pragma warning(pop) + #pragma optimize("g", on) +#endif diff --git a/src/common/quassel.cpp b/src/common/quassel.cpp index d06aa289..29b6e55e 100644 --- a/src/common/quassel.cpp +++ b/src/common/quassel.cpp @@ -34,13 +34,6 @@ #include "types.h" #include "syncableobject.h" -#if defined(HAVE_EXECINFO) && !defined(Q_OS_MAC) -# define BUILD_CRASHHANDLER -# include -# include -# include -#endif - Quassel::BuildInfo Quassel::_buildInfo; CliParser *Quassel::_cliParser = 0; Quassel::RunMode Quassel::_runMode; @@ -55,11 +48,15 @@ Quassel::Quassel() { signal(SIGTERM, handleSignal); signal(SIGINT, handleSignal); -#ifdef BUILD_CRASHHANDLER + // we have crashhandler for win32 and unix (based on execinfo). + // on mac os we use it's integrated backtrace generator +#if defined(Q_OS_WIN32) || (defined(HAVE_EXECINFO) && !defined(Q_OS_MAC)) signal(SIGABRT, handleSignal); - signal(SIGBUS, handleSignal); signal(SIGSEGV, handleSignal); -#endif // #if defined(HAVE_EXECINFO) && !defined(Q_OS_MAC) +# ifndef Q_OS_WIN32 + signal(SIGBUS, handleSignal); +# endif +#endif _cliParser = new CliParser(); @@ -207,109 +204,47 @@ void Quassel::setupBuildInfo(const QString &generated) { //! Signal handler for graceful shutdown. void Quassel::handleSignal(int sig) { switch(sig) { - case SIGTERM: - case SIGINT: - qWarning("%s", qPrintable(QString("Caught signal %1 - exiting.").arg(sig))); - QCoreApplication::quit(); - break; - -#ifdef BUILD_CRASHHANDLER - case SIGABRT: - case SIGBUS: - case SIGSEGV: - handleCrash(); + case SIGTERM: + case SIGINT: + qWarning("%s", qPrintable(QString("Caught signal %1 - exiting.").arg(sig))); + QCoreApplication::quit(); + break; + case SIGABRT: + case SIGSEGV: +#ifndef Q_OS_WIN32 + case SIGBUS: #endif - break; - default: - break; + logBacktrace(coreDumpFileName()); + break; + default: + break; } } void Quassel::logFatalMessage(const char *msg) { -#ifndef Q_OS_MAC +#ifdef Q_OS_MAC + Q_UNUSED(msg) +#else QFile dumpFile(coreDumpFileName()); - dumpFile.open(QIODevice::WriteOnly); + dumpFile.open(QIODevice::Append); QTextStream dumpStream(&dumpFile); -#else - QTextStream dumpStream(stderr); -#endif dumpStream << "Fatal: " << msg << '\n'; dumpStream.flush(); - - qInstallMsgHandler(0); - abort(); -} - -void Quassel::handleCrash() { -#ifdef BUILD_CRASHHANDLER - void* callstack[128]; - int i, frames = backtrace(callstack, 128); - - QFile dumpFile(coreDumpFileName()); - dumpFile.open(QIODevice::Append); - QTextStream dumpStream(&dumpFile); - - dumpStream << "Quassel IRC: " << _buildInfo.baseVersion << ' ' << _buildInfo.commitHash << '\n'; - - for (i = 0; i < frames; ++i) { - Dl_info info; - dladdr (callstack[i], &info); - // as a reference: - // typedef struct - // { - // __const char *dli_fname; /* File name of defining object. */ - // void *dli_fbase; /* Load address of that object. */ - // __const char *dli_sname; /* Name of nearest symbol. */ - // void *dli_saddr; /* Exact value of nearest symbol. */ - // } Dl_info; - - #if __LP64__ - int addrSize = 16; - #else - int addrSize = 8; - #endif - - QString funcName; - if(info.dli_sname) { - char *func = abi::__cxa_demangle(info.dli_sname, 0, 0, 0); - if(func) { - funcName = QString(func); - free(func); - } else { - funcName = QString(info.dli_sname); - } - } else { - funcName = QString("0x%1").arg((long)info.dli_saddr, addrSize, QLatin1Char('0')); - } - - // prettificating the filename - QString fileName("???"); - if(info.dli_fname) { - fileName = QString(info.dli_fname); - int slashPos = fileName.lastIndexOf('/'); - if(slashPos != -1) - fileName = fileName.mid(slashPos + 1); - if(fileName.count() < 20) - fileName += QString(20 - fileName.count(), ' '); - } - - QString debugLine = QString("#%1 %2 0x%3 %4").arg(i, 3, 10) - .arg(fileName) - .arg((long)(callstack[i]), addrSize, 16, QLatin1Char('0')) - .arg(funcName); - - dumpStream << debugLine << "\n"; - qDebug() << qPrintable(debugLine); - } dumpFile.close(); - exit(27); -#endif /* BUILD_CRASHHANDLER */ +#endif } const QString &Quassel::coreDumpFileName() { - if(_coreDumpFileName.isEmpty()) + if(_coreDumpFileName.isEmpty()) { _coreDumpFileName = QString("Quassel-Crash-%1.log").arg(QDateTime::currentDateTime().toString("yyyyMMdd-hhmm")); - + QFile dumpFile(_coreDumpFileName); + dumpFile.open(QIODevice::Append); + QTextStream dumpStream(&dumpFile); + dumpStream << "Quassel IRC: " << _buildInfo.baseVersion << ' ' << _buildInfo.commitHash << '\n'; + qDebug() << "Quassel IRC: " << _buildInfo.baseVersion << ' ' << _buildInfo.commitHash; + dumpStream.flush(); + dumpFile.close(); + } return _coreDumpFileName; } diff --git a/src/common/quassel.h b/src/common/quassel.h index a9fe53c6..539b579f 100644 --- a/src/common/quassel.h +++ b/src/common/quassel.h @@ -85,7 +85,7 @@ private: void registerMetaTypes(); static void handleSignal(int signal); - static void handleCrash(); + static void logBacktrace(const QString &filename); static BuildInfo _buildInfo; static CliParser *_cliParser; -- 2.20.1