/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "stacktrace.h" #include "stream/textstream.h" #include "environment.h" #ifdef __linux__ #include void write_stack_trace( TextOutputStream& outputStream ){ const unsigned int MAX_SYMBOLS = 256; void* symbols[MAX_SYMBOLS]; // get return addresses int symbol_count = backtrace( symbols, MAX_SYMBOLS ); if ( !symbol_count ) { return; } // resolve and print names char** symbol_names = backtrace_symbols( symbols, symbol_count ); if ( symbol_names ) { for ( int i = 0; ( i < symbol_count ); ++i ) outputStream << symbol_names[i] << "\n"; // not a memleak, see www.gnu.org/software/libc/manual (Debugging Support, Backtraces) free( symbol_names ); } } #elif defined ( WIN32 ) && defined ( _MSC_VER ) #include "windows.h" #include "winnt.h" #include "dbghelp.h" class Address { public: void* m_value; Address( void* value ) : m_value( value ){ } }; /// \brief Writes an address \p p to \p ostream in hexadecimal form. template inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const Address& p ){ const std::size_t bufferSize = ( sizeof( void* ) * 2 ) + 1; char buf[bufferSize]; ostream.write( buf, snprintf( buf, bufferSize, "%0p", p.m_value ) ); return ostream; } class Offset { public: void* m_value; Offset( void* value ) : m_value( value ){ } }; /// \brief Writes an address \p p to \p ostream in hexadecimal form. template inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const Offset& p ){ const std::size_t bufferSize = ( sizeof( void* ) * 2 ) + 1; char buf[bufferSize]; ostream.write( buf, snprintf( buf, bufferSize, "%X", p.m_value ) ); return ostream; } /// \brief Writes a WCHAR string \p s to \p ostream. template inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const WCHAR* s ){ const std::size_t bufferSize = 1024; char buf[bufferSize]; ostream.write( buf, snprintf( buf, bufferSize, "%ls", s ) ); return ostream; } struct EnumerateSymbolsContext { STACKFRAME64& sf; TextOutputStream& outputStream; std::size_t count; EnumerateSymbolsContext( STACKFRAME64& sf, TextOutputStream& outputStream ) : sf( sf ), outputStream( outputStream ), count( 0 ){ } }; void write_symbol( PSYMBOL_INFO pSym, STACKFRAME64& sf, TextOutputStream& outputStream, std::size_t& count ){ #if 0 if ( pSym->Flags & SYMFLAG_PARAMETER ) { DWORD basicType; if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex, TI_GET_BASETYPE, &basicType ) ) { int bleh = 0; } else { DWORD typeId; if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex, TI_GET_TYPEID, &typeId ) ) { if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex, TI_GET_BASETYPE, &basicType ) ) { int bleh = 0; } else { const char* FormatGetLastError(); const char* error = FormatGetLastError(); int bleh = 0; WCHAR* name; if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, typeId, TI_GET_SYMNAME, &name ) ) { outputStream << name << " "; LocalFree( name ); int bleh = 0; } else { const char* FormatGetLastError(); const char* error = FormatGetLastError(); int bleh = 0; } } } else { const char* FormatGetLastError(); const char* error = FormatGetLastError(); int bleh = 0; } } if ( count != 0 ) { outputStream << ", "; } outputStream << pSym->Name; ++count; } #endif } BOOL CALLBACK EnumerateSymbolsCallback( PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext ){ write_symbol( pSymInfo, ( (EnumerateSymbolsContext*)UserContext )->sf, ( (EnumerateSymbolsContext*)UserContext )->outputStream, ( (EnumerateSymbolsContext*)UserContext )->count ); return TRUE; } void write_stack_trace( PCONTEXT pContext, TextOutputStream& outputStream ){ HANDLE m_hProcess = GetCurrentProcess(); DWORD dwMachineType = 0; CONTEXT context = *pContext; // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag if ( !SymInitialize( m_hProcess, (PSTR)environment_get_app_path(), TRUE ) ) { return; } STACKFRAME64 sf; memset( &sf, 0, sizeof( sf ) ); sf.AddrPC.Mode = AddrModeFlat; sf.AddrStack.Mode = AddrModeFlat; sf.AddrFrame.Mode = AddrModeFlat; #ifdef _M_IX86 // Initialize the STACKFRAME structure for the first call. This is only // necessary for Intel CPUs, and isn't mentioned in the documentation. sf.AddrPC.Offset = context.Eip; sf.AddrStack.Offset = context.Esp; sf.AddrFrame.Offset = context.Ebp; dwMachineType = IMAGE_FILE_MACHINE_I386; #elif _M_X64 sf.AddrPC.Offset = context.Rip; sf.AddrStack.Offset = context.Rsp; // MSDN: x64: The frame pointer is RBP or RDI. This value is not always used. // very funny, we'll try Rdi for now sf.AddrFrame.Offset = context.Rdi; dwMachineType = IMAGE_FILE_MACHINE_AMD64; #endif const unsigned int max_sym_name = 1024; // should be enough while ( 1 ) { // Get the next stack frame if ( !StackWalk64( dwMachineType, m_hProcess, GetCurrentThread(), &sf, &context, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0 ) ) { break; } if ( 0 == sf.AddrFrame.Offset ) { // Basic sanity check to make sure break; // the frame is OK. Bail if not. } // Get the name of the function for this stack frame entry BYTE symbolBuffer[ sizeof( SYMBOL_INFO ) + max_sym_name ]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer; pSymbol->SizeOfStruct = sizeof( SYMBOL_INFO ); pSymbol->MaxNameLen = max_sym_name; DWORD64 symDisplacement = 0; // Displacement of the input address, // relative to the start of the symbol IMAGEHLP_MODULE64 module = { sizeof( IMAGEHLP_MODULE64 ) }; if ( SymGetModuleInfo64( m_hProcess, sf.AddrPC.Offset, &module ) ) { outputStream << module.ModuleName << "!"; if ( SymFromAddr( m_hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol ) ) { char undecoratedName[max_sym_name]; UnDecorateSymbolName( pSymbol->Name, undecoratedName, max_sym_name, UNDNAME_COMPLETE ); outputStream << undecoratedName; outputStream << "("; // Use SymSetContext to get just the locals/params for this frame IMAGEHLP_STACK_FRAME imagehlpStackFrame; imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset; SymSetContext( m_hProcess, &imagehlpStackFrame, 0 ); // Enumerate the locals/parameters EnumerateSymbolsContext context( sf, outputStream ); SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &context ); outputStream << ")"; outputStream << " + " << Offset( reinterpret_cast( symDisplacement ) ); // Get the source line for this stack frame entry IMAGEHLP_LINE64 lineInfo = { sizeof( IMAGEHLP_LINE64 ) }; DWORD dwLineDisplacement; if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo ) ) { outputStream << " " << lineInfo.FileName << " line " << Unsigned( lineInfo.LineNumber ); } } else { outputStream << Address( reinterpret_cast( sf.AddrPC.Offset ) ); } } outputStream << "\n"; } SymCleanup( m_hProcess ); return; } void write_stack_trace( TextOutputStream& outputStream ){ __try { RaiseException( 0,0,0,0 ); } __except( write_stack_trace( ( GetExceptionInformation() )->ContextRecord, outputStream ), EXCEPTION_CONTINUE_EXECUTION ) { } } #elif defined ( WIN32 ) void write_stack_trace( TextOutputStream& outputStream ){ outputStream << "\nStacktrace is disabled on this compiler\n"; } #else void write_stack_trace( TextOutputStream& outputStream ){ outputStream << "\nStacktrace is disabled on this platform\n"; } #endif