Automatic back traces on windows.
authorMarcus Eggenberger <egs@quassel-irc.org>
Mon, 10 Nov 2008 21:57:40 +0000 (22:57 +0100)
committerMarcus Eggenberger <egs@quassel-irc.org>
Mon, 10 Nov 2008 21:58:43 +0000 (22:58 +0100)
This might break the nightly builds (static windows) (though I really hope not) or have other unforseen side effects. :)

CMakeLists.txt
src/common/CMakeLists.txt
src/common/logbacktrace_unix.cpp [new file with mode: 0644]
src/common/logbacktrace_win.cpp [new file with mode: 0755]
src/common/quassel.cpp
src/common/quassel.h

index 9ac2d30..da59dbd 100644 (file)
@@ -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
index 4218661..8baf259 100644 (file)
@@ -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 (file)
index 0000000..431c802
--- /dev/null
@@ -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 <execinfo.h>
+#  include <dlfcn.h>
+#  include <cxxabi.h>
+#  include <QFile>
+#  include <QTextStream>
+#  include <QDebug>
+#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 (executable)
index 0000000..abcab48
--- /dev/null
@@ -0,0 +1,203 @@
+/***************************************************************************\r
+ *   Copyright (C) 2005-08 by the Quassel IRC Team                         *\r
+ *   devel@quassel-irc.org                                                 *\r
+ *                                                                         *\r
+ *   This program is free software; you can redistribute it and/or modify  *\r
+ *   it under the terms of the GNU General Public License as published by  *\r
+ *   the Free Software Foundation; either version 2 of the License, or     *\r
+ *   (at your option) version 3.                                           *\r
+ *                                                                         *\r
+ *   This program is distributed in the hope that it will be useful,       *\r
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *\r
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *\r
+ *   GNU General Public License for more details.                          *\r
+ *                                                                         *\r
+ *   You should have received a copy of the GNU General Public License     *\r
+ *   along with this program; if not, write to the                         *\r
+ *   Free Software Foundation, Inc.,                                       *\r
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *\r
+ ***************************************************************************/\r
+\r
+#include "quassel.h"\r
+\r
+#include <windows.h>\r
+#include <dbghelp.h>\r
+#include <stdio.h>\r
+\r
+// #include <QDebug>\r
+#include <QFile>\r
+#include <QTextStream>\r
+\r
+void loadHelpStackFrame(IMAGEHLP_STACK_FRAME &ihsf, const STACKFRAME64 &stackFrame) {\r
+  ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME));\r
+  ihsf.InstructionOffset = stackFrame.AddrPC.Offset;\r
+  ihsf.FrameOffset = stackFrame.AddrFrame.Offset;\r
+}\r
+\r
+BOOL CALLBACK EnumSymbolsCB(PSYMBOL_INFO symInfo, ULONG size, PVOID user) { \r
+  QStringList *params = (QStringList *)user;\r
+  if(symInfo->Flags & SYMFLAG_PARAMETER) {\r
+    params->append(symInfo->Name);\r
+  }\r
+  return TRUE;\r
+}\r
+\r
+\r
+struct EnumModulesContext {\r
+  HANDLE hProcess;\r
+  QTextStream &stream;\r
+  EnumModulesContext(HANDLE hProcess, QTextStream &stream) : hProcess(hProcess), stream(stream) {}\r
+};\r
+\r
+BOOL CALLBACK EnumModulesCB(PCTSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext) {\r
+  IMAGEHLP_MODULE64 mod;\r
+  EnumModulesContext *context = (EnumModulesContext *)UserContext;\r
+  mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);\r
+  if(SymGetModuleInfo64(context->hProcess, BaseOfDll, &mod)) {\r
+    QString line = QString("%1 0x%2 Image: %3").arg(mod.ModuleName, -14)\r
+      .arg(BaseOfDll, 8, 16, QLatin1Char('0'))\r
+      .arg(mod.LoadedImageName);\r
+    // qDebug() << qPrintable(line);\r
+    context->stream  << line << '\n';\r
+    \r
+    QString pdbName(mod.LoadedPdbName);\r
+    if(!pdbName.isEmpty()) {\r
+      QString line2 = QString("%1 %2").arg("", 32).arg(pdbName);\r
+      // qDebug() << qPrintable(line2);\r
+      context->stream  << line2 << '\n';\r
+    }\r
+  }\r
+  return TRUE;\r
+}\r
+\r
+#ifdef _M_IX86\r
+  // Disable global optimization and ignore /GS waning caused by\r
+  // inline assembly.\r
+  #pragma optimize("g", off)\r
+  #pragma warning(push)\r
+  #pragma warning(disable : 4748)\r
+#endif\r
+void Quassel::logBacktrace(const QString &filename) {\r
+  DWORD MachineType;\r
+  CONTEXT Context;\r
+  STACKFRAME64 StackFrame;\r
+\r
+#ifdef _M_IX86\r
+    ZeroMemory(&Context, sizeof(CONTEXT));\r
+    Context.ContextFlags = CONTEXT_CONTROL;\r
+    __asm {\r
+    Label:\r
+      mov [Context.Ebp], ebp;\r
+      mov [Context.Esp], esp;\r
+      mov eax, [Label];\r
+      mov [Context.Eip], eax;\r
+    }\r
+#else\r
+    RtlCaptureContext(&Context);\r
+#endif\r
+\r
+  ZeroMemory(&StackFrame, sizeof(STACKFRAME64));\r
+#ifdef _M_IX86\r
+  MachineType                 = IMAGE_FILE_MACHINE_I386;\r
+  StackFrame.AddrPC.Offset    = Context.Eip;\r
+  StackFrame.AddrPC.Mode      = AddrModeFlat;\r
+  StackFrame.AddrFrame.Offset = Context.Ebp;\r
+  StackFrame.AddrFrame.Mode   = AddrModeFlat;\r
+  StackFrame.AddrStack.Offset = Context.Esp;\r
+  StackFrame.AddrStack.Mode   = AddrModeFlat;\r
+#elif _M_X64\r
+  MachineType                 = IMAGE_FILE_MACHINE_AMD64;\r
+  StackFrame.AddrPC.Offset    = Context.Rip;\r
+  StackFrame.AddrPC.Mode      = AddrModeFlat;\r
+  StackFrame.AddrFrame.Offset = Context.Rsp;\r
+  StackFrame.AddrFrame.Mode   = AddrModeFlat;\r
+  StackFrame.AddrStack.Offset = Context.Rsp;\r
+  StackFrame.AddrStack.Mode   = AddrModeFlat;\r
+#elif _M_IA64\r
+  MachineType                 = IMAGE_FILE_MACHINE_IA64;\r
+  StackFrame.AddrPC.Offset    = Context.StIIP;\r
+  StackFrame.AddrPC.Mode      = AddrModeFlat;\r
+  StackFrame.AddrFrame.Offset = Context.IntSp;\r
+  StackFrame.AddrFrame.Mode   = AddrModeFlat;\r
+  StackFrame.AddrBStore.Offset= Context.RsBSP;\r
+  StackFrame.AddrBStore.Mode  = AddrModeFlat;\r
+  StackFrame.AddrStack.Offset = Context.IntSp;\r
+  StackFrame.AddrStack.Mode   = AddrModeFlat;\r
+#else\r
+  #error "Unsupported platform"\r
+#endif\r
+\r
+  //EnterCriticalSection(&DbgHelpLock);\r
+\r
+  QFile logFile(filename);\r
+  logFile.open(QIODevice::Append);\r
+  QTextStream logStream(&logFile);\r
+\r
+  HANDLE hProcess = GetCurrentProcess();\r
+  HANDLE hThread = GetCurrentThread();\r
+  SymInitialize(hProcess, NULL, TRUE);\r
+\r
+  DWORD64 dwDisplacement;\r
+\r
+  ULONG64 buffer[(sizeof(SYMBOL_INFO) +\r
+                 MAX_SYM_NAME*sizeof(TCHAR) +\r
+                 sizeof(ULONG64) - 1) /  sizeof(ULONG64)];\r
+  PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;\r
+  pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);\r
+  pSymbol->MaxNameLen = MAX_SYM_NAME;\r
+  \r
+  IMAGEHLP_MODULE64 mod;\r
+  mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);\r
+\r
+  IMAGEHLP_STACK_FRAME ihsf;\r
+  ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME));\r
+\r
+  int i = 0;\r
+  while(StackWalk64(MachineType, hProcess, hThread, &StackFrame, &Context, NULL, NULL, NULL, NULL)) {\r
+    if(i == 128)\r
+      break;\r
+\r
+    loadHelpStackFrame(ihsf, StackFrame);\r
+    if(StackFrame.AddrPC.Offset != 0) { // Valid frame.\r
+      \r
+      QString fileName("???");\r
+      if(SymGetModuleInfo64(hProcess, ihsf.InstructionOffset, &mod)) {\r
+       fileName = QString(mod.ImageName);\r
+       int slashPos = fileName.lastIndexOf('\\');\r
+       if(slashPos != -1)\r
+         fileName = fileName.mid(slashPos + 1);\r
+      }\r
+      QString funcName;\r
+      if(SymFromAddr(hProcess, ihsf.InstructionOffset, &dwDisplacement, pSymbol)) {\r
+       funcName = QString(pSymbol->Name);\r
+      } else {\r
+       funcName = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'));\r
+      }\r
+      QStringList params;\r
+      SymSetContext(hProcess, &ihsf, NULL);\r
+      SymEnumSymbols(hProcess, 0, NULL, EnumSymbolsCB, (PVOID)&params);\r
+      \r
+      QString debugLine = QString("#%1 %2 0x%3 %4(%5)").arg(i, 3, 10)\r
+       .arg(fileName, -20)\r
+       .arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'))\r
+       .arg(funcName)\r
+       .arg(params.join(", "));\r
+      // qDebug() << qPrintable(debugLine);\r
+      logStream << debugLine << '\n';\r
+      i++;\r
+    } else {\r
+      break; // we're at the end.\r
+    }\r
+  }\r
+\r
+  // qDebug() << "List of linked Modules:";\r
+  logStream << "\n\nList of linked Modules:\n";\r
+  EnumModulesContext modulesContext(hProcess, logStream);\r
+  SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);\r
+\r
+  logFile.close();\r
+}\r
+#ifdef _M_IX86\r
+  #pragma warning(pop)\r
+  #pragma optimize("g", on)\r
+#endif\r
index d06aa28..29b6e55 100644 (file)
 #include "types.h"
 #include "syncableobject.h"
 
-#if defined(HAVE_EXECINFO) && !defined(Q_OS_MAC)
-#  define BUILD_CRASHHANDLER
-#  include <execinfo.h>
-#  include <dlfcn.h>
-#  include <cxxabi.h>
-#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;
 }
index a9fe53c..539b579 100644 (file)
@@ -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;