Merge pull request #97 from Bombe/focus-host-input
[quassel.git] / src / common / logbacktrace_win.cpp
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
50 struct EnumModulesContext {
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