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