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