2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
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.
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.
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
22 #include "stacktrace.h"
23 #include "globaldefs.h"
25 #include "stream/textstream.h"
27 #include "environment.h"
32 void write_stack_trace( TextOutputStream& outputStream ){
33 const unsigned int MAX_SYMBOLS = 256;
34 void* symbols[MAX_SYMBOLS];
36 // get return addresses
37 int symbol_count = backtrace( symbols, MAX_SYMBOLS );
39 if ( !symbol_count ) {
43 // resolve and print names
44 char** symbol_names = backtrace_symbols( symbols, symbol_count );
46 for ( int i = 0; ( i < symbol_count ); ++i )
47 outputStream << symbol_names[i] << "\n";
49 // not a memleak, see www.gnu.org/software/libc/manual (Debugging Support, Backtraces)
53 #elif GDEF_COMPILER_MSVC
63 Address( void* value ) : m_value( value ){
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;
72 ostream.write( buf, snprintf( buf, bufferSize, "%0p", p.m_value ) );
80 Offset( void* value ) : m_value( value ){
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;
89 ostream.write( buf, snprintf( buf, bufferSize, "%X", p.m_value ) );
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;
98 ostream.write( buf, snprintf( buf, bufferSize, "%ls", s ) );
102 struct EnumerateSymbolsContext
105 TextOutputStream& outputStream;
107 EnumerateSymbolsContext( STACKFRAME64& sf, TextOutputStream& outputStream ) : sf( sf ), outputStream( outputStream ), count( 0 ){
111 void write_symbol( PSYMBOL_INFO pSym, STACKFRAME64& sf, TextOutputStream& outputStream, std::size_t& count ){
113 if ( pSym->Flags & SYMFLAG_PARAMETER ) {
116 if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
117 TI_GET_BASETYPE, &basicType ) ) {
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 ) ) {
131 const char* FormatGetLastError();
132 const char* error = FormatGetLastError();
136 if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, typeId,
137 TI_GET_SYMNAME, &name ) ) {
138 outputStream << name << " ";
144 const char* FormatGetLastError();
145 const char* error = FormatGetLastError();
152 const char* FormatGetLastError();
153 const char* error = FormatGetLastError();
158 outputStream << ", ";
160 outputStream << pSym->Name;
167 EnumerateSymbolsCallback(
168 PSYMBOL_INFO pSymInfo,
171 write_symbol( pSymInfo, ( (EnumerateSymbolsContext*)UserContext )->sf, ( (EnumerateSymbolsContext*)UserContext )->outputStream, ( (EnumerateSymbolsContext*)UserContext )->count );
177 void write_stack_trace( PCONTEXT pContext, TextOutputStream& outputStream ){
178 HANDLE m_hProcess = GetCurrentProcess();
179 DWORD dwMachineType = 0;
181 CONTEXT context = *pContext;
183 // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
184 if ( !SymInitialize( m_hProcess, (PSTR)environment_get_app_path(), TRUE ) ) {
189 memset( &sf, 0, sizeof( sf ) );
190 sf.AddrPC.Mode = AddrModeFlat;
191 sf.AddrStack.Mode = AddrModeFlat;
192 sf.AddrFrame.Mode = AddrModeFlat;
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;
201 dwMachineType = IMAGE_FILE_MACHINE_I386;
203 sf.AddrPC.Offset = context.Rip;
204 sf.AddrStack.Offset = context.Rsp;
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;
210 dwMachineType = IMAGE_FILE_MACHINE_AMD64;
213 const unsigned int max_sym_name = 1024; // should be enough
217 // Get the next stack frame
218 if ( !StackWalk64( dwMachineType,
224 SymFunctionTableAccess64,
230 if ( 0 == sf.AddrFrame.Offset ) { // Basic sanity check to make sure
231 break; // the frame is OK. Bail if not.
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;
240 DWORD64 symDisplacement = 0; // Displacement of the input address,
241 // relative to the start of the symbol
243 IMAGEHLP_MODULE64 module = { sizeof( IMAGEHLP_MODULE64 ) };
244 if ( SymGetModuleInfo64( m_hProcess, sf.AddrPC.Offset, &module ) ) {
245 outputStream << module.ModuleName << "!";
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 );
251 outputStream << undecoratedName;
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 );
259 // Enumerate the locals/parameters
260 EnumerateSymbolsContext context( sf, outputStream );
261 SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &context );
264 outputStream << " + " << Offset( reinterpret_cast<void*>( symDisplacement ) );
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 );
276 outputStream << Address( reinterpret_cast<void*>( sf.AddrPC.Offset ) );
280 outputStream << "\n";
283 SymCleanup( m_hProcess );
288 void write_stack_trace( TextOutputStream& outputStream ){
289 __try { RaiseException( 0,0,0,0 ); } __except( write_stack_trace( ( GetExceptionInformation() )->ContextRecord, outputStream ), EXCEPTION_CONTINUE_EXECUTION ) {
293 #elif GDEF_OS_WINDOWS
294 void write_stack_trace( TextOutputStream& outputStream ){
295 outputStream << "\nStacktrace is disabled on this compiler\n";
298 void write_stack_trace( TextOutputStream& outputStream ){
299 outputStream << "\nStacktrace is disabled on this platform\n";