850556a22a37d5ee18c0c25702d3ef1c4e33c235
[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 #ifdef __linux__
28 #include <execinfo.h>
29
30 void write_stack_trace(TextOutputStream& outputStream)
31 {
32   const unsigned int MAX_SYMBOLS = 256;
33   void* symbols[MAX_SYMBOLS];
34     
35   // get return addresses
36   int symbol_count = backtrace(symbols, MAX_SYMBOLS);
37   
38   if(!symbol_count)
39         return;
40   
41   // resolve and print names
42   char** symbol_names = backtrace_symbols(symbols, symbol_count);       
43   if(symbol_names)
44   {
45      for(int i = 0; (i < symbol_count); ++i)
46         outputStream << symbol_names[i] << "\n";
47         
48      // not a memleak, see www.gnu.org/software/libc/manual (Debugging Support, Backtraces)
49      free(symbol_names);
50   }
51 }       
52 #endif
53
54 #if defined (WIN32) && defined (_MSC_VER) && defined (DEBUG)
55
56 #include "windows.h"
57 #include "winnt.h"
58 #include "dbghelp.h"
59
60 class Address
61 {
62 public:
63   void* m_value;
64   Address(void* value) : m_value(value)
65   {
66   }
67 };
68
69 /// \brief Writes an address \p p to \p ostream in hexadecimal form.
70 template<typename TextOutputStreamType>
71 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Address& p)
72 {
73   const std::size_t bufferSize = (sizeof(void*) * 2) + 1;
74   char buf[bufferSize];
75   ostream.write(buf, snprintf(buf, bufferSize, "%0p", p.m_value));
76   return ostream;
77 }
78
79 class Offset
80 {
81 public:
82   void* m_value;
83   Offset(void* value) : m_value(value)
84   {
85   }
86 };
87
88 /// \brief Writes an address \p p to \p ostream in hexadecimal form.
89 template<typename TextOutputStreamType>
90 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Offset& p)
91 {
92   const std::size_t bufferSize = (sizeof(void*) * 2) + 1;
93   char buf[bufferSize];
94   ostream.write(buf, snprintf(buf, bufferSize, "%X", p.m_value));
95   return ostream;
96 }
97
98 /// \brief Writes a WCHAR string \p s to \p ostream.
99 template<typename TextOutputStreamType>
100 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const WCHAR* s)
101 {
102   const std::size_t bufferSize = 1024;
103   char buf[bufferSize];
104   ostream.write(buf, snprintf(buf, bufferSize, "%ls", s));
105   return ostream;
106 }
107
108 struct EnumerateSymbolsContext
109 {
110   STACKFRAME64& sf;
111   TextOutputStream& outputStream;
112   std::size_t count;
113   EnumerateSymbolsContext(STACKFRAME64& sf, TextOutputStream& outputStream) : sf(sf), outputStream(outputStream), count(0)
114   {
115   }
116 };
117
118 void write_symbol(PSYMBOL_INFO pSym, STACKFRAME64& sf, TextOutputStream& outputStream, std::size_t& count)
119 {
120 #if 0
121  if ( pSym->Flags & SYMFLAG_PARAMETER )
122  {
123
124     DWORD basicType;
125     if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
126                         TI_GET_BASETYPE, &basicType ) )
127     {
128       int bleh = 0;
129     }
130     else
131     {
132       DWORD typeId;
133       if(SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
134                       TI_GET_TYPEID, &typeId ))
135       {
136         if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
137                             TI_GET_BASETYPE, &basicType ) )
138         {
139           int bleh = 0;
140         }
141         else
142         {
143           const char* FormatGetLastError();
144           const char* error = FormatGetLastError();
145           int bleh = 0;
146
147           WCHAR* name;
148           if(SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, typeId,
149                           TI_GET_SYMNAME, &name ))
150           {
151             outputStream << name << " ";
152             LocalFree(name);
153             int bleh = 0;
154           }
155           else
156           {
157             const char* FormatGetLastError();
158             const char* error = FormatGetLastError();
159             int bleh = 0;
160           }
161         }
162       }
163       else
164       {
165         const char* FormatGetLastError();
166         const char* error = FormatGetLastError();
167         int bleh = 0;
168       }
169     }
170     if(count != 0)
171     {
172       outputStream << ", ";
173     }
174     outputStream << pSym->Name;
175     ++count;
176   }
177 #endif
178 }
179
180 BOOL CALLBACK
181 EnumerateSymbolsCallback(
182     PSYMBOL_INFO  pSymInfo,
183     ULONG         SymbolSize,
184     PVOID         UserContext )
185 {
186   write_symbol( pSymInfo, ((EnumerateSymbolsContext*)UserContext)->sf, ((EnumerateSymbolsContext*)UserContext)->outputStream, ((EnumerateSymbolsContext*)UserContext)->count);
187
188
189   return TRUE;
190 }
191
192 void write_stack_trace(PCONTEXT pContext, TextOutputStream& outputStream)
193 {
194   HANDLE m_hProcess = GetCurrentProcess();
195   DWORD dwMachineType = 0;
196
197   CONTEXT context = *pContext;
198
199   // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
200   if ( !SymInitialize( m_hProcess, (PSTR)environment_get_app_path(), TRUE ) )
201   {
202     return;
203   }
204
205   STACKFRAME64 sf;
206   memset( &sf, 0, sizeof(sf) );
207   sf.AddrPC.Mode         = AddrModeFlat;
208   sf.AddrStack.Mode      = AddrModeFlat;
209   sf.AddrFrame.Mode      = AddrModeFlat;
210   
211 #ifdef _M_IX86
212   // Initialize the STACKFRAME structure for the first call.  This is only
213   // necessary for Intel CPUs, and isn't mentioned in the documentation.
214   sf.AddrPC.Offset       = context.Eip;
215   sf.AddrStack.Offset    = context.Esp;
216   sf.AddrFrame.Offset    = context.Ebp;
217   
218   dwMachineType = IMAGE_FILE_MACHINE_I386;
219 #elif _M_X64
220   sf.AddrPC.Offset       = context.Rip;
221   sf.AddrStack.Offset    = context.Rsp;
222   
223   // MSDN: x64:  The frame pointer is RBP or RDI. This value is not always used.
224   // very funny, we'll try Rdi for now
225   sf.AddrFrame.Offset    = context.Rdi;
226   
227   dwMachineType = IMAGE_FILE_MACHINE_AMD64;
228 #endif
229
230   const unsigned int max_sym_name = 1024;// should be enough
231
232   while ( 1 )
233   {
234     // Get the next stack frame
235     if ( ! StackWalk64( dwMachineType,
236                         m_hProcess,
237                         GetCurrentThread(),
238                         &sf,
239                         &context,
240                         0,
241                         SymFunctionTableAccess64,
242                         SymGetModuleBase64,
243                         0 ) )
244         break;
245
246     if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure
247       break;                      // the frame is OK.  Bail if not.
248
249     // Get the name of the function for this stack frame entry
250     BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + max_sym_name ];
251     PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
252     pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
253     pSymbol->MaxNameLen = max_sym_name;
254                     
255     DWORD64 symDisplacement = 0;    // Displacement of the input address,
256                                     // relative to the start of the symbol
257
258     IMAGEHLP_MODULE64 module = { sizeof(IMAGEHLP_MODULE64) };
259     if(SymGetModuleInfo64(m_hProcess, sf.AddrPC.Offset, &module))
260     {
261       outputStream << module.ModuleName << "!";
262
263       if ( SymFromAddr(m_hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol))
264       {
265         char undecoratedName[max_sym_name];
266         UnDecorateSymbolName(pSymbol->Name, undecoratedName, max_sym_name, UNDNAME_COMPLETE);
267
268         outputStream << undecoratedName;
269
270         outputStream << "(";
271         // Use SymSetContext to get just the locals/params for this frame
272         IMAGEHLP_STACK_FRAME imagehlpStackFrame;
273         imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
274         SymSetContext( m_hProcess, &imagehlpStackFrame, 0 );
275
276         // Enumerate the locals/parameters
277         EnumerateSymbolsContext context(sf, outputStream);
278         SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &context );
279         outputStream << ")";
280
281         outputStream << " + " << Offset(reinterpret_cast<void*>(symDisplacement));
282
283         // Get the source line for this stack frame entry
284         IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };
285         DWORD dwLineDisplacement;
286         if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset,
287                                 &dwLineDisplacement, &lineInfo ) )
288         {
289           outputStream << " " << lineInfo.FileName << " line " << Unsigned(lineInfo.LineNumber); 
290         }
291       }
292       else
293       {
294         outputStream << Address(reinterpret_cast<void*>(sf.AddrPC.Offset));
295       }
296     }
297
298     outputStream << "\n";
299   }
300
301   SymCleanup(m_hProcess);
302
303   return;
304 }
305
306 void write_stack_trace(TextOutputStream& outputStream)
307 {
308   __try{ RaiseException(0,0,0,0); } __except(write_stack_trace((GetExceptionInformation())->ContextRecord, outputStream), EXCEPTION_CONTINUE_EXECUTION) {}
309 }
310
311 #else
312 void write_stack_trace(TextOutputStream& outputStream)
313 {
314         outputStream << "\nStacktrace is disabled in release-builds\n";
315 }
316 #endif