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