ok
[xonotic/netradiant.git] / radiant / stacktrace.cpp
1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include "stacktrace.h"
23 #include "stream/textstream.h"
24
25 #include "environment.h"
26
27 #if defined (WIN32) && defined (_MSC_VER)
28
29 #include "windows.h"
30 #include "winnt.h"
31 #include "dbghelp.h"
32
33 class Address
34 {
35 public:
36   void* m_value;
37   Address(void* value) : m_value(value)
38   {
39   }
40 };
41
42 /// \brief Writes an address \p p to \p ostream in hexadecimal form.
43 template<typename TextOutputStreamType>
44 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Address& p)
45 {
46   const std::size_t bufferSize = (sizeof(void*) * 2) + 1;
47   char buf[bufferSize];
48   ostream.write(buf, snprintf(buf, bufferSize, "%0p", p.m_value));
49   return ostream;
50 }
51
52 class Offset
53 {
54 public:
55   void* m_value;
56   Offset(void* value) : m_value(value)
57   {
58   }
59 };
60
61 /// \brief Writes an address \p p to \p ostream in hexadecimal form.
62 template<typename TextOutputStreamType>
63 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Offset& p)
64 {
65   const std::size_t bufferSize = (sizeof(void*) * 2) + 1;
66   char buf[bufferSize];
67   ostream.write(buf, snprintf(buf, bufferSize, "%X", p.m_value));
68   return ostream;
69 }
70
71 /// \brief Writes a WCHAR string \p s to \p ostream.
72 template<typename TextOutputStreamType>
73 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const WCHAR* s)
74 {
75   const std::size_t bufferSize = 1024;
76   char buf[bufferSize];
77   ostream.write(buf, snprintf(buf, bufferSize, "%ls", s));
78   return ostream;
79 }
80
81 struct EnumerateSymbolsContext
82 {
83   STACKFRAME& sf;
84   TextOutputStream& outputStream;
85   std::size_t count;
86   EnumerateSymbolsContext(STACKFRAME& sf, TextOutputStream& outputStream) : sf(sf), outputStream(outputStream), count(0)
87   {
88   }
89 };
90
91 void write_symbol(PSYMBOL_INFO pSym, STACKFRAME& sf, TextOutputStream& outputStream, std::size_t& count)
92 {
93   if ( pSym->Flags & SYMFLAG_PARAMETER )
94   {
95 #if 0
96     DWORD basicType;
97     if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
98                         TI_GET_BASETYPE, &basicType ) )
99     {
100       int bleh = 0;
101     }
102     else
103     {
104       DWORD typeId;
105       if(SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
106                       TI_GET_TYPEID, &typeId ))
107       {
108         if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
109                             TI_GET_BASETYPE, &basicType ) )
110         {
111           int bleh = 0;
112         }
113         else
114         {
115           const char* FormatGetLastError();
116           const char* error = FormatGetLastError();
117           int bleh = 0;
118
119           WCHAR* name;
120           if(SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, typeId,
121                           TI_GET_SYMNAME, &name ))
122           {
123             outputStream << name << " ";
124             LocalFree(name);
125             int bleh = 0;
126           }
127           else
128           {
129             const char* FormatGetLastError();
130             const char* error = FormatGetLastError();
131             int bleh = 0;
132           }
133         }
134       }
135       else
136       {
137         const char* FormatGetLastError();
138         const char* error = FormatGetLastError();
139         int bleh = 0;
140       }
141     }
142 #endif
143     if(count != 0)
144     {
145       outputStream << ", ";
146     }
147     outputStream << pSym->Name;
148     ++count;
149   }
150 }
151
152 BOOL CALLBACK
153 EnumerateSymbolsCallback(
154     PSYMBOL_INFO  pSymInfo,
155     ULONG         SymbolSize,
156     PVOID         UserContext )
157 {
158   write_symbol( pSymInfo, ((EnumerateSymbolsContext*)UserContext)->sf, ((EnumerateSymbolsContext*)UserContext)->outputStream, ((EnumerateSymbolsContext*)UserContext)->count);
159
160
161   return TRUE;
162 }
163
164 void write_stack_trace(PCONTEXT pContext, TextOutputStream& outputStream)
165 {
166   HANDLE m_hProcess = GetCurrentProcess();
167   DWORD dwMachineType = 0;
168
169   CONTEXT context = *pContext;
170
171   // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
172   if ( !SymInitialize( m_hProcess, (PSTR)environment_get_app_path(), TRUE ) )
173   {
174     return;
175   }
176
177   STACKFRAME sf;
178   memset( &sf, 0, sizeof(sf) );
179
180 #ifdef _M_IX86
181   // Initialize the STACKFRAME structure for the first call.  This is only
182   // necessary for Intel CPUs, and isn't mentioned in the documentation.
183   sf.AddrPC.Offset       = context.Eip;
184   sf.AddrPC.Mode         = AddrModeFlat;
185   sf.AddrStack.Offset    = context.Esp;
186   sf.AddrStack.Mode      = AddrModeFlat;
187   sf.AddrFrame.Offset    = context.Ebp;
188   sf.AddrFrame.Mode      = AddrModeFlat;
189
190   dwMachineType = IMAGE_FILE_MACHINE_I386;
191 #endif
192
193   while ( 1 )
194   {
195     // Get the next stack frame
196     if ( ! StackWalk(  dwMachineType,
197                         m_hProcess,
198                         GetCurrentThread(),
199                         &sf,
200                         &context,
201                         0,
202                         SymFunctionTableAccess,
203                         SymGetModuleBase,
204                         0 ) )
205         break;
206
207     if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure
208       break;                      // the frame is OK.  Bail if not.
209
210     // Get the name of the function for this stack frame entry
211     BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + MAX_SYM_NAME ];
212     PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
213     pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
214     pSymbol->MaxNameLen = MAX_SYM_NAME;
215                     
216     DWORD64 symDisplacement = 0;    // Displacement of the input address,
217                                     // relative to the start of the symbol
218
219     IMAGEHLP_MODULE module = { sizeof(IMAGEHLP_MODULE) };
220     if(SymGetModuleInfo(m_hProcess, sf.AddrPC.Offset, &module))
221     {
222       outputStream << module.ModuleName << "!";
223
224       if ( SymFromAddr(m_hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol))
225       {
226         char undecoratedName[MAX_SYM_NAME];
227         UnDecorateSymbolName(pSymbol->Name, undecoratedName, MAX_SYM_NAME, UNDNAME_COMPLETE);
228
229         outputStream << undecoratedName;
230
231         outputStream << "(";
232         // Use SymSetContext to get just the locals/params for this frame
233         IMAGEHLP_STACK_FRAME imagehlpStackFrame;
234         imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
235         SymSetContext( m_hProcess, &imagehlpStackFrame, 0 );
236
237         // Enumerate the locals/parameters
238         EnumerateSymbolsContext context(sf, outputStream);
239         SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &context );
240         outputStream << ")";
241
242         outputStream << " + " << Offset(reinterpret_cast<void*>(symDisplacement));
243
244         // Get the source line for this stack frame entry
245         IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
246         DWORD dwLineDisplacement;
247         if ( SymGetLineFromAddr( m_hProcess, sf.AddrPC.Offset,
248                                 &dwLineDisplacement, &lineInfo ) )
249         {
250           outputStream << " " << lineInfo.FileName << " line " << Unsigned(lineInfo.LineNumber); 
251         }
252       }
253       else
254       {
255         outputStream << Address(reinterpret_cast<void*>(sf.AddrPC.Offset));
256       }
257     }
258
259     outputStream << "\n";
260   }
261
262   SymCleanup(m_hProcess);
263
264   return;
265 }
266
267 void write_stack_trace(TextOutputStream& outputStream)
268 {
269   __try{ RaiseException(0,0,0,0); } __except(write_stack_trace((GetExceptionInformation())->ContextRecord, outputStream), EXCEPTION_CONTINUE_EXECUTION) {}
270 }
271
272 #endif