Quassel IRC  Pre-Release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
logbacktrace_win.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2015 by the Quassel Project *
3  * devel@quassel-irc.org *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) version 3. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19  ***************************************************************************/
20 
21 #include "quassel.h"
22 
23 #include <windows.h>
24 #include <dbghelp.h>
25 #include <stdio.h>
26 
27 // #include <QDebug>
28 #include <QFile>
29 #include <QTextStream>
30 
31 void loadHelpStackFrame(IMAGEHLP_STACK_FRAME &ihsf, const STACKFRAME64 &stackFrame)
32 {
33  ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME));
34  ihsf.InstructionOffset = stackFrame.AddrPC.Offset;
35  ihsf.FrameOffset = stackFrame.AddrFrame.Offset;
36 }
37 
38 
39 BOOL CALLBACK EnumSymbolsCB(PSYMBOL_INFO symInfo, ULONG size, PVOID user)
40 {
41  Q_UNUSED(size)
42  QStringList *params = (QStringList *)user;
43  if (symInfo->Flags & SYMFLAG_PARAMETER) {
44  params->append(symInfo->Name);
45  }
46  return TRUE;
47 }
48 
49 
51  HANDLE hProcess;
52  QTextStream &stream;
53  EnumModulesContext(HANDLE hProcess, QTextStream &stream) : hProcess(hProcess), stream(stream) {}
54 };
55 
56 BOOL CALLBACK EnumModulesCB(LPCSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext)
57 {
58  Q_UNUSED(ModuleName)
59  IMAGEHLP_MODULE64 mod;
60  EnumModulesContext *context = (EnumModulesContext *)UserContext;
61  mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
62  if (SymGetModuleInfo64(context->hProcess, BaseOfDll, &mod)) {
63  QString line = QString("%1 0x%2 Image: %3").arg(mod.ModuleName, -14)
64  .arg(BaseOfDll, 8, 16, QLatin1Char('0'))
65  .arg(mod.LoadedImageName);
66  // qDebug() << qPrintable(line);
67  context->stream << line << '\n';
68 
69  QString pdbName(mod.LoadedPdbName);
70  if (!pdbName.isEmpty()) {
71  QString line2 = QString("%1 %2").arg("", 32).arg(pdbName);
72  // qDebug() << qPrintable(line2);
73  context->stream << line2 << '\n';
74  }
75  }
76  return TRUE;
77 }
78 
79 
80 #if defined(_M_IX86) && defined(Q_CC_MSVC)
81 // Disable global optimization and ignore /GS waning caused by
82 // inline assembly.
83 // not needed with mingw cause we can tell mingw which registers we use
84  #pragma optimize("g", off)
85  #pragma warning(push)
86  #pragma warning(disable : 4748)
87 #endif
88 void Quassel::logBacktrace(const QString &filename)
89 {
90  DWORD MachineType;
91  CONTEXT Context;
92  STACKFRAME64 StackFrame;
93 
94 #ifdef _M_IX86
95  ZeroMemory(&Context, sizeof(CONTEXT));
96  Context.ContextFlags = CONTEXT_CONTROL;
97 
98 #ifdef __MINGW32__
99  asm ("Label:\n\t"
100  "movl %%ebp,%0;\n\t"
101  "movl %%esp,%1;\n\t"
102  "movl $Label,%%eax;\n\t"
103  "movl %%eax,%2;\n\t"
104  : "=r" (Context.Ebp), "=r" (Context.Esp), "=r" (Context.Eip)
105  : //no input
106  : "eax");
107 #else
108  _asm {
109 Label:
110  mov[Context.Ebp], ebp;
111  mov[Context.Esp], esp;
112  mov eax, [Label];
113  mov[Context.Eip], eax;
114  }
115 #endif
116 #else
117  RtlCaptureContext(&Context);
118 #endif
119 
120  ZeroMemory(&StackFrame, sizeof(STACKFRAME64));
121 #ifdef _M_IX86
122  MachineType = IMAGE_FILE_MACHINE_I386;
123  StackFrame.AddrPC.Offset = Context.Eip;
124  StackFrame.AddrPC.Mode = AddrModeFlat;
125  StackFrame.AddrFrame.Offset = Context.Ebp;
126  StackFrame.AddrFrame.Mode = AddrModeFlat;
127  StackFrame.AddrStack.Offset = Context.Esp;
128  StackFrame.AddrStack.Mode = AddrModeFlat;
129 #elif defined(_M_X64)
130  MachineType = IMAGE_FILE_MACHINE_AMD64;
131  StackFrame.AddrPC.Offset = Context.Rip;
132  StackFrame.AddrPC.Mode = AddrModeFlat;
133  StackFrame.AddrFrame.Offset = Context.Rsp;
134  StackFrame.AddrFrame.Mode = AddrModeFlat;
135  StackFrame.AddrStack.Offset = Context.Rsp;
136  StackFrame.AddrStack.Mode = AddrModeFlat;
137 #elif defined(_M_IA64)
138  MachineType = IMAGE_FILE_MACHINE_IA64;
139  StackFrame.AddrPC.Offset = Context.StIIP;
140  StackFrame.AddrPC.Mode = AddrModeFlat;
141  StackFrame.AddrFrame.Offset = Context.IntSp;
142  StackFrame.AddrFrame.Mode = AddrModeFlat;
143  StackFrame.AddrBStore.Offset = Context.RsBSP;
144  StackFrame.AddrBStore.Mode = AddrModeFlat;
145  StackFrame.AddrStack.Offset = Context.IntSp;
146  StackFrame.AddrStack.Mode = AddrModeFlat;
147 #else
148  #error "Unsupported platform"
149 #endif
150 
151  //EnterCriticalSection(&DbgHelpLock);
152 
153  QFile logFile(filename);
154  logFile.open(QIODevice::Append);
155  QTextStream logStream(&logFile);
156 
157  HANDLE hProcess = GetCurrentProcess();
158  HANDLE hThread = GetCurrentThread();
159  SymInitialize(hProcess, NULL, TRUE);
160 
161  DWORD64 dwDisplacement;
162 
163  ULONG64 buffer[(sizeof(SYMBOL_INFO) +
164  MAX_SYM_NAME*sizeof(TCHAR) +
165  sizeof(ULONG64) - 1) / sizeof(ULONG64)];
166  PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
167  pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
168  pSymbol->MaxNameLen = MAX_SYM_NAME;
169 
170  IMAGEHLP_MODULE64 mod;
171  mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
172 
173  IMAGEHLP_STACK_FRAME ihsf;
174  ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME));
175 
176  int i = 0;
177  while (StackWalk64(MachineType, hProcess, hThread, &StackFrame, &Context, NULL, NULL, NULL, NULL)) {
178  if (i == 128)
179  break;
180 
181  loadHelpStackFrame(ihsf, StackFrame);
182  if (StackFrame.AddrPC.Offset != 0) { // Valid frame.
183  QString fileName("???");
184  if (SymGetModuleInfo64(hProcess, ihsf.InstructionOffset, &mod)) {
185  fileName = QString(mod.ImageName);
186  int slashPos = fileName.lastIndexOf('\\');
187  if (slashPos != -1)
188  fileName = fileName.mid(slashPos + 1);
189  }
190  QString funcName;
191  if (SymFromAddr(hProcess, ihsf.InstructionOffset, &dwDisplacement, pSymbol)) {
192  funcName = QString(pSymbol->Name);
193  }
194  else {
195  funcName = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'));
196  }
197  QStringList params;
198  SymSetContext(hProcess, &ihsf, NULL);
199  SymEnumSymbols(hProcess, 0, NULL, EnumSymbolsCB, (PVOID)&params);
200 
201  QString debugLine = QString("#%1 %2 0x%3 %4(%5)").arg(i, 3, 10)
202  .arg(fileName, -20)
203  .arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'))
204  .arg(funcName)
205  .arg(params.join(", "));
206  // qDebug() << qPrintable(debugLine);
207  logStream << debugLine << '\n';
208  i++;
209  }
210  else {
211  break; // we're at the end.
212  }
213  }
214 
215  // qDebug() << "List of linked Modules:";
216  logStream << "\n\nList of linked Modules:\n";
217  EnumModulesContext modulesContext(hProcess, logStream);
218  SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
219 
220  logFile.close();
221 }
222 
223 
224 #if defined(_M_IX86) && defined(Q_CC_MSVC)
225  #pragma warning(pop)
226  #pragma optimize("g", on)
227 #endif