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