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