SVN: backtracexx: README backtracexx.cpp backtracexx.hpp example.cpp makefile

pluto pluto at pld-linux.org
Wed Jul 11 15:07:46 CEST 2007


Author: pluto
Date: Wed Jul 11 15:07:46 2007
New Revision: 8669

Modified:
   backtracexx/README
   backtracexx/backtracexx.cpp
   backtracexx/backtracexx.hpp
   backtracexx/example.cpp
   backtracexx/makefile
Log:
- support windows. 


Modified: backtracexx/README
==============================================================================
--- backtracexx/README	(original)
+++ backtracexx/README	Wed Jul 11 15:07:46 2007
@@ -1,7 +1,7 @@
 A backtrace is a summary of how your program got where it is.
 Unfortunately glibc's backtrace() and gdb's (bt) produce an unwind
-path instead of true backtrace. This small library uses an unwind
-informations to produce true backtrace with optionally demangled symbols
-and allows you to embed backtracing facility into your application.
+path instead of true backtrace. This small library uses unwind
+information and produces true backtrace with optionally demangled symbols.
+it allows you to embed backtracing facility into your application.
 
 Sources available at: http://svn.pld-linux.org/cgi-bin/viewsvn/backtracexx/

Modified: backtracexx/backtracexx.cpp
==============================================================================
--- backtracexx/backtracexx.cpp	(original)
+++ backtracexx/backtracexx.cpp	Wed Jul 11 15:07:46 2007
@@ -1,104 +1,192 @@
 #include "backtracexx.hpp"
+
+#if defined( __GNUC__ )
 #include <cxxabi.h>
 #include <dlfcn.h>
 #include <unwind.h>
-#include <iostream>
-#include <iomanip>
-#include <sstream>
+#elif defined( _MSC_VER )
+#include <windows.h>
+#include <dbghelp.h>
+//
+//	please use a recent dbghelp.dll because older versions
+//	have unexpected problems with symbols resolving, e.g.
+//	::SymGetSymFromAddr() produces ERROR_INVALID_ADDRESS.
+//
+//	this code works fine with:
+//	- dbghelp.dll v5.1.2600.2180 from WinXP/SP2.
+//	- dbghelp.dll v6.5.0003.7 from Visual C++ 2005 Express Edition.
+//
+//	this code doesn't work with:
+//	- dbghelp.dll v5.00.2195.6613 from Win2000/SP4.
+//
+#pragma comment( lib, "dbghelp" )
+#endif
 
 namespace backtracexx
 {
 	namespace
 	{
-		// extract caller's address from callee's return point.
-		unsigned char const* caller( unsigned char const* ip )
+		//
+		//	extract caller's address from callee's return point.
+		//
+		unsigned long caller( unsigned long ret )
 		{
+			unsigned char const* ip = reinterpret_cast< unsigned char const* >( ret );
 #if defined( __powerpc__ ) && !defined( __powerpc64__ )
-			// powerpc64 not tested.
+			//	powerpc64 not tested.
 			ip -= 4;
 #elif defined( __sparc__ )
-			// the same for sparc v7/8/9.
+			//	the same for sparc v7/8/9.
 			ip -= 8;
 #elif defined( __alpha__ )
 			ip -= 4;
 #elif defined( __i386__ ) || defined( __x86_64__ )
 			//
-			// TODO:
-			//	analysis of complex addressing forms (see intel/24319102.pdf).
-			//	rework code to cover all cases.
+			//	TODO:
+			//		analysis of complex addressing forms (see intel/24319102.pdf).
+			//		rework code to cover all cases.
 			//
-			// call, near, relative
+			//	call, near, relative
 			if ( ip[ -5 ] == 0xe8 )
-				return ( ip - 5 );
-			// call, near, absolute indirect
+				return ( ret - 5 );
+			//	call, near, absolute indirect
 			if ( ip[ -2 ] == 0xff )
 			{
-				if ( ( ip[ -1 ] & 0xf8 ) == 0xd0 ) // call *%reg
-					return ( ip - 2 );
-				if ( ( ip[ -1 ] & 0xf8 ) == 0x10 ) // call *(%reg)
-					return ( ip - 2 );
+				if ( ( ip[ -1 ] & 0xf8 ) == 0xd0 )	//	call *%reg
+					return ( ret - 2 );
+				if ( ( ip[ -1 ] & 0xf8 ) == 0x10 )	//	call *(%reg)
+					return ( ret - 2 );
 			}
 #endif
-			return ip;
-		}
-
-		_Unwind_Reason_Code helper( struct _Unwind_Context* ctx, void* arg )
-		{
-			int beforeInsn = 0;
-			_Unwind_Ptr ip = _Unwind_GetIPInfo( ctx, &beforeInsn );
-			unwind_point_type up( reinterpret_cast< unsigned char const* >( ip ), beforeInsn );
-			if ( !beforeInsn )
-				up.first = caller( reinterpret_cast< unsigned char const* >( up.first ) );
-			reinterpret_cast< raw_backtrace_type* >( arg )->push_back( up );
-			return _URC_NO_REASON;
+			return ret;
 		}
-	}
 
-	raw_backtrace_type scan()
-	{
-		raw_backtrace_type trace;
-		_Unwind_Backtrace( helper, &trace );
-		return trace;
-	}
+#if defined( __GNUC__ )
 
-	symbolic_backtrace_type symbols( raw_backtrace_type const& bt )
-	{
-		std::ostringstream os;
-		os.setf( std::ios_base::hex, std::ios_base::basefield );
-		os.setf( std::ios_base::showbase );
-		symbolic_backtrace_type sbt;
-		for ( raw_backtrace_type::const_iterator i = bt.begin(), e = bt.end(); i != e; ++i )
+		void lookupSymbol( Frame& frame )
 		{
-			os.str( std::string() );
 			Dl_info info;
-			unwind_point_type up = *i;
-			os << std::setw( 18 ) << up.first << " : ";
-			if ( dladdr( const_cast< void* >( up.first ), &info ) )
+			if ( ::dladdr( reinterpret_cast< void* >( frame.address ), &info ) )
 			{
-				if ( !info.dli_saddr )
-					// the image containing address is found, but no nearest symbol was found.
-					os << "??";
-				else
+				if ( info.dli_fname && strlen( info.dli_fname ) )
+					frame.module = info.dli_fname;
+				if ( info.dli_saddr )
 				{
+					frame.displacement = frame.address - reinterpret_cast< unsigned long >( info.dli_saddr );
 					int status;
 					char* demangled = abi::__cxa_demangle( info.dli_sname, 0, 0, &status );
 					if ( status != -1 )
 					{
-						long offset = reinterpret_cast< long >( up.first ) - reinterpret_cast< long >( info.dli_saddr );
-						os << ( ( status == 0 ) ? demangled : info.dli_sname ) << '+' << offset;
 						if ( status == 0 )
+						{
+							frame.symbol = demangled;
 							free( demangled );
+						}
+						else
+							frame.symbol = info.dli_sname;
 					}
 				}
-				if ( info.dli_fname && strlen( info.dli_fname ) )
-					os << " from " << info.dli_fname;
 			}
+		}
+
+		_Unwind_Reason_Code helper( struct _Unwind_Context* ctx, Trace* trace )
+		{
+			int beforeInsn;
+			_Unwind_Ptr ip = _Unwind_GetIPInfo( ctx, &beforeInsn );
+			Frame frame;
+			frame.address = ip;
+			if ( beforeInsn )
+				frame.signalTrampoline = true;
 			else
-				os << "??";
-			if ( up.second )
-				os << " [signal frame]";
-			sbt.push_back( os.str() );
+				frame.address = caller( frame.address );
+			lookupSymbol( frame );
+			trace->push_back( frame );
+			return _URC_NO_REASON;
 		}
-		return sbt;
+
+#elif defined( _MSC_VER ) && defined( WIN32 )
+
+#pragma warning( disable : 4312 )	//	'reinterpret_cast' : conversion from 'unsigned long' to 'LPCVOID' of greater size
+
+		void lookupSymbol( Frame& frame )
+		{
+			::MEMORY_BASIC_INFORMATION mbi;
+			::VirtualQuery( reinterpret_cast< ::LPCVOID >( frame.address ), &mbi, sizeof( mbi ) );
+			::CHAR moduleName[ MAX_PATH ];
+			::GetModuleFileNameA( reinterpret_cast< ::HMODULE >( mbi.AllocationBase ), moduleName, sizeof( moduleName ) );
+			frame.module = moduleName;
+			int const MaxSymbolNameLength = 8192;
+			::BYTE symbolBuffer[ sizeof( ::IMAGEHLP_SYMBOL64 ) + MaxSymbolNameLength ];
+			::PIMAGEHLP_SYMBOL64 symbol = reinterpret_cast< ::PIMAGEHLP_SYMBOL64 >( symbolBuffer );
+			symbol->SizeOfStruct = sizeof( symbolBuffer );
+			symbol->MaxNameLength = MaxSymbolNameLength - 1;
+			if ( ::SymLoadModule64( ::GetCurrentProcess(), 0, moduleName, 0,
+				reinterpret_cast< ::DWORD64 >( mbi.AllocationBase ), 0 ) )
+			{
+				::DWORD64 displacement;
+				if ( ::SymGetSymFromAddr64( ::GetCurrentProcess(), static_cast< ::DWORD64 >( frame.address ),
+					&displacement, symbol ) )
+				{
+					frame.symbol = symbol->Name;
+					frame.displacement = static_cast< unsigned long >( displacement );
+				}
+				::SymUnloadModule64( ::GetCurrentProcess(), reinterpret_cast< ::DWORD64 >( mbi.AllocationBase ) );
+			}
+		}
+
+#endif
+	}
+
+	Frame::Frame()
+	:
+		address(), displacement(), signalTrampoline()
+	{
+	}
+
+	Trace scan()
+	{
+		Trace trace;
+
+#if defined( __GNUC__ )
+
+		//
+		//	libgcc takes care about proper stack walking.
+		//
+		_Unwind_Backtrace( reinterpret_cast< _Unwind_Trace_Fn >( helper ), &trace );
+
+#elif defined( _MSC_VER ) && defined( WIN32 )
+
+		::SymInitialize( ::GetCurrentProcess(), 0, FALSE );
+		::SymSetOptions( ::SymGetOptions() | SYMOPT_UNDNAME );
+		struct StackFrame
+		{
+			StackFrame* previousFrame;
+			unsigned long returnAddress;
+		};
+		StackFrame const* stackFrame;
+		__asm mov stackFrame, ebp;
+		//
+		//	the deepest frame pointer and return address of the process
+		//	call chain are zeroed by kernel32.dll during process startup:
+		//
+		//	BaseProcessStartThunk:
+		//		xor		ebp,ebp
+		//		push	eax
+		//		push	0x0
+		//		jmp		KERNEL32!BaseProcessStart
+		//
+		while ( stackFrame->returnAddress )
+		{
+			Frame frame;
+			frame.address = stackFrame->returnAddress;
+			lookupSymbol( frame );
+			trace.push_back( frame );
+			stackFrame = stackFrame->previousFrame;
+		}
+		::SymCleanup( ::GetCurrentProcess() );
+
+#endif
+
+		return trace;
 	}
 }

Modified: backtracexx/backtracexx.hpp
==============================================================================
--- backtracexx/backtracexx.hpp	(original)
+++ backtracexx/backtracexx.hpp	Wed Jul 11 15:07:46 2007
@@ -1,20 +1,25 @@
 #ifndef backtracexx_hpp
 #define backtracexx_hpp
 
-#include <list>
 #include <string>
-#include <utility>
+#include <vector>
 
 namespace backtracexx
 {
-	typedef std::pair< void const*,
-		bool /* signal frame */ > unwind_point_type;
+	struct Frame
+	{
+		Frame();
 
-	typedef std::list< unwind_point_type > raw_backtrace_type;
-	typedef std::list< std::string > symbolic_backtrace_type;
+		unsigned long address;
+		std::string symbol;
+		unsigned long displacement;
+		std::string module;
+		bool signalTrampoline;
+	};
 
-	raw_backtrace_type scan();
-	symbolic_backtrace_type symbols( raw_backtrace_type const& );
+	typedef std::vector< Frame > Trace;
+
+	Trace scan();
 }
 
 #endif

Modified: backtracexx/example.cpp
==============================================================================
--- backtracexx/example.cpp	(original)
+++ backtracexx/example.cpp	Wed Jul 11 15:07:46 2007
@@ -10,8 +10,14 @@
 
 void signalHandler( int signalNumber )
 {
-	backtracexx::symbolic_backtrace_type s = backtracexx::symbols( backtracexx::scan() );
-	std::copy( s.begin(), s.end(), std::ostream_iterator< std::string >( std::cout, "\n" ) );
+	backtracexx::Trace t = backtracexx::scan();
+	for ( backtracexx::Trace::const_iterator i = t.begin(); i != t.end(); ++i )
+	{
+		backtracexx::Frame const& f = *i;
+		std::printf( "0x%016lx : %s+0x%lx [%s]\n", f.address,
+			( f.symbol.empty() ? "<unresolved symbol>" : f.symbol.c_str() ),
+			f.displacement, f.module.c_str() );
+	}
 	longjmp( context, 1 );
 }
 

Modified: backtracexx/makefile
==============================================================================
--- backtracexx/makefile	(original)
+++ backtracexx/makefile	Wed Jul 11 15:07:46 2007
@@ -1,15 +1,13 @@
 CXX := g++
-CXXFLAGS += -Wall -Werror -pedantic
+CXXFLAGS := -O1 -Wall -Werror -pedantic
+LDXXFLAGS := -Wl,-export-dynamic -s -ldl -static-libgcc
 
-all: libbacktracexx.so example
+all: example
 
-libbacktracexx.so: backtracexx.hpp backtracexx.cpp
-	$(CXX) backtracexx.cpp -o libbacktracexx.so -shared -ldl $(CXXFLAGS) \
-	-O3 -fpic -funwind-tables -fno-exceptions -fno-rtti -s
-
-example: example.cpp libbacktracexx.so
-	$(CXX) example.cpp -o example ./libbacktracexx.so $(CXXFLAGS) \
-	-O1 -Wl,-export-dynamic -s
+example: example.cpp backtracexx.hpp backtracexx.cpp
+	$(CXX) $(CXXFLAGS) backtracexx.cpp -c
+	$(CXX) $(CXXFLAGS) example.cpp -c
+	$(CXX) example.o backtracexx.o -o example $(LDXXFLAGS)
 
 clean:
-	rm -f libbacktracexx.so example
+	rm -f *.o *.s *.ii example


More information about the pld-cvs-commit mailing list