Automatic back traces on windows.
[quassel.git] / src / common / logbacktrace_win.cpp
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