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