cmake: avoid de-duplication of user's CXXFLAGS
[quassel.git] / src / common / logbacktrace_win.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2020 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 <cstdio>
22
23 #include <windows.h>
24 #include <dbghelp.h>
25
26 #include <QFile>
27 #include <QTextStream>
28
29 #include "quassel.h"
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 BOOL CALLBACK EnumSymbolsCB(PSYMBOL_INFO symInfo, ULONG size, PVOID user)
39 {
40     Q_UNUSED(size)
41     QStringList* params = (QStringList*)user;
42     if (symInfo->Flags & SYMFLAG_PARAMETER) {
43         params->append(symInfo->Name);
44     }
45     return TRUE;
46 }
47
48 struct EnumModulesContext
49 {
50     HANDLE hProcess;
51     QTextStream& stream;
52     EnumModulesContext(HANDLE hProcess, QTextStream& stream)
53         : hProcess(hProcess)
54         , stream(stream)
55     {}
56 };
57
58 BOOL CALLBACK EnumModulesCB(LPCSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext)
59 {
60     Q_UNUSED(ModuleName)
61     IMAGEHLP_MODULE64 mod;
62     EnumModulesContext* context = (EnumModulesContext*)UserContext;
63     mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
64     if (SymGetModuleInfo64(context->hProcess, BaseOfDll, &mod)) {
65         QString line = QString("%1 0x%2 Image: %3").arg(mod.ModuleName, -14).arg(BaseOfDll, 8, 16, QLatin1Char('0')).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 #if defined(_M_IX86) && defined(Q_CC_MSVC)
80 // Disable global optimization and ignore /GS waning caused by
81 // inline assembly.
82 // not needed with mingw cause we can tell mingw which registers we use
83 #    pragma optimize("g", off)
84 #    pragma warning(push)
85 #    pragma warning(disable : 4748)
86 #endif
87 void Quassel::logBacktrace(const QString& filename)
88 {
89     DWORD MachineType;
90     CONTEXT Context;
91     STACKFRAME64 StackFrame;
92
93 #ifdef _M_IX86
94     ZeroMemory(&Context, sizeof(CONTEXT));
95     Context.ContextFlags = CONTEXT_CONTROL;
96
97 #    ifdef __MINGW32__
98     asm("Label:\n\t"
99         "movl %%ebp,%0;\n\t"
100         "movl %%esp,%1;\n\t"
101         "movl $Label,%%eax;\n\t"
102         "movl %%eax,%2;\n\t"
103         : "=r"(Context.Ebp), "=r"(Context.Esp), "=r"(Context.Eip)
104         :  // no input
105         : "eax");
106 #    else
107     _asm {
108 Label:
109         mov[Context.Ebp], ebp;
110         mov[Context.Esp], esp;
111         mov eax, [Label];
112         mov[Context.Eip], eax;
113     }
114 #    endif
115 #else
116     RtlCaptureContext(&Context);
117 #endif
118
119     ZeroMemory(&StackFrame, sizeof(STACKFRAME64));
120 #ifdef _M_IX86
121     MachineType = IMAGE_FILE_MACHINE_I386;
122     StackFrame.AddrPC.Offset = Context.Eip;
123     StackFrame.AddrPC.Mode = AddrModeFlat;
124     StackFrame.AddrFrame.Offset = Context.Ebp;
125     StackFrame.AddrFrame.Mode = AddrModeFlat;
126     StackFrame.AddrStack.Offset = Context.Esp;
127     StackFrame.AddrStack.Mode = AddrModeFlat;
128 #elif defined(_M_X64)
129     MachineType = IMAGE_FILE_MACHINE_AMD64;
130     StackFrame.AddrPC.Offset = Context.Rip;
131     StackFrame.AddrPC.Mode = AddrModeFlat;
132     StackFrame.AddrFrame.Offset = Context.Rsp;
133     StackFrame.AddrFrame.Mode = AddrModeFlat;
134     StackFrame.AddrStack.Offset = Context.Rsp;
135     StackFrame.AddrStack.Mode = AddrModeFlat;
136 #elif defined(_M_IA64)
137     MachineType = IMAGE_FILE_MACHINE_IA64;
138     StackFrame.AddrPC.Offset = Context.StIIP;
139     StackFrame.AddrPC.Mode = AddrModeFlat;
140     StackFrame.AddrFrame.Offset = Context.IntSp;
141     StackFrame.AddrFrame.Mode = AddrModeFlat;
142     StackFrame.AddrBStore.Offset = Context.RsBSP;
143     StackFrame.AddrBStore.Mode = AddrModeFlat;
144     StackFrame.AddrStack.Offset = Context.IntSp;
145     StackFrame.AddrStack.Mode = AddrModeFlat;
146 #else
147 #    error "Unsupported platform"
148 #endif
149
150     // EnterCriticalSection(&DbgHelpLock);
151
152     QFile logFile(filename);
153     logFile.open(QIODevice::Append);
154     QTextStream logStream(&logFile);
155
156     HANDLE hProcess = GetCurrentProcess();
157     HANDLE hThread = GetCurrentThread();
158     SymInitialize(hProcess, NULL, TRUE);
159
160     DWORD64 dwDisplacement;
161
162     ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
163     PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
164     pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
165     pSymbol->MaxNameLen = MAX_SYM_NAME;
166
167     IMAGEHLP_MODULE64 mod;
168     mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
169
170     IMAGEHLP_STACK_FRAME ihsf;
171     ZeroMemory(&ihsf, sizeof(IMAGEHLP_STACK_FRAME));
172
173     int i = 0;
174     while (StackWalk64(MachineType, hProcess, hThread, &StackFrame, &Context, NULL, NULL, NULL, NULL)) {
175         if (i == 128)
176             break;
177
178         loadHelpStackFrame(ihsf, StackFrame);
179         if (StackFrame.AddrPC.Offset != 0) {  // Valid frame.
180             QString fileName("???");
181             if (SymGetModuleInfo64(hProcess, ihsf.InstructionOffset, &mod)) {
182                 fileName = QString(mod.ImageName);
183                 int slashPos = fileName.lastIndexOf('\\');
184                 if (slashPos != -1)
185                     fileName = fileName.mid(slashPos + 1);
186             }
187             QString funcName;
188             if (SymFromAddr(hProcess, ihsf.InstructionOffset, &dwDisplacement, pSymbol)) {
189                 funcName = QString(pSymbol->Name);
190             }
191             else {
192                 funcName = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'));
193             }
194             QStringList params;
195             SymSetContext(hProcess, &ihsf, NULL);
196             SymEnumSymbols(hProcess, 0, NULL, EnumSymbolsCB, (PVOID)&params);
197
198             QString debugLine = QString("#%1 %2 0x%3 %4(%5)")
199                                     .arg(i, 3, 10)
200                                     .arg(fileName, -20)
201                                     .arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'))
202                                     .arg(funcName)
203                                     .arg(params.join(", "));
204             // qDebug() << qPrintable(debugLine);
205             logStream << debugLine << '\n';
206             i++;
207         }
208         else {
209             break;  // we're at the end.
210         }
211     }
212
213     // qDebug() << "List of linked Modules:";
214     logStream << "\n\nList of linked Modules:\n";
215     EnumModulesContext modulesContext(hProcess, logStream);
216     SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
217
218     logFile.close();
219 }
220
221 #if defined(_M_IX86) && defined(Q_CC_MSVC)
222 #    pragma warning(pop)
223 #    pragma optimize("g", on)
224 #endif