#if !defined(_INLINE)
static char cvsid[] = "@(#)$Id$";
static char debugFileId[] = __FILE__;
#endif
#include "Debug.hpp"
#include "OGuard.hpp"
#include "Backtrace.hpp"
#include "StringBuffer.hpp"
#include "LinkManager.hpp"
#include "DataWrapper.hpp"
#include "platform/Platform.hpp"
#include "containers/lists/List.hpp"
#include "containers/dicts/DumbDictionary.hpp"
#if defined( __GNUC__ )
#define boolean bfd_boolean_kludge
#define TRUE_FALSE_ALREADY_DEFINED
#include <bfd.h>
#include <stdlib.h>
#undef boolean
#endif
#include "defines.h"
Backtrace::Backtrace()
{
frames = new List();
LinkManager::reg( this, frames );
truncated = false;
tableLoaded = false;
table = new DumbDictionary();
LinkManager::reg( this, table );
}
Backtrace::~Backtrace()
{
LinkManager::free( this, frames );
LinkManager::free( this, table );
}
void Backtrace::fill()
{
void *fp;
Index frame = 0;
do
{
fp = getAddressAtFrame( frame );
if( fp == NULL )
{
truncated = true;
return;
}
else
{
frames -> append( new DataWrapper( fp ) );
++frame;
}
}
while( ! isInitialFrame( fp ) );
}
void *Backtrace::getFrame( Index aFrame ) const
{
preC_( isValidFrame( aFrame ) );
return TCAST( getFrames() -> getAt( aFrame ), DataWrapper ) -> getValue();
}
boolean Backtrace::isTruncated() const
{
return truncated;
}
Index Backtrace::getDepth() const
{
return getFrames() -> length();
}
void Backtrace::setAddress( Index aFrame, void *anAddress )
{
preC_( isValidFrame( aFrame ) );
TCAST( getFrames() -> getAt( aFrame ), DataWrapper ) -> setValue( anAddress );
}
boolean Backtrace::isValidFrame( Index aFrame ) const
{
return getFrames() -> isValidIndex( aFrame );
}
List *Backtrace::getFrames() const
{
return frames;
}
boolean Backtrace::isInitialFrame( void *anAddress )
{
preC_( initialised );
return( anAddress == crt0Address );
}
void Backtrace::initBacktrace()
{
crt0Address = getAddressAtFrame( 2 );
initialised = true;
}
String Backtrace::toString() const
{
Index depth;
StringBuffer result( StringBuffer::defaultLength,
StringBuffer::defaultDelta );
result.add( Object::toString().getUNIChars() );
result.add( getFrames() -> toString().getUNIChars() );
depth = 0;
for( getFrames() -> toFirst() ; ! getFrames() -> atEnd() ; getFrames() -> next() )
{
DataWrapper *frame;
frame = DCAST( getFrames() -> getCurrentValue(), DataWrapper );
OGuard _frame( frame, this );
result.add( String( "[ %li ]: 0x%x (%s)\n" ).
sprintf( 1, depth++, frame -> getValue(),
getNameFor( frame -> getValue() ).getChars() ).
getUNIChars() );
}
return result.asString();
}
String Backtrace::getString() const
{
return Object::getString() +
getFrames() -> getString();
}
String Backtrace::getClassName() const
{
return "Backtrace";
}
#define RA( i ) __builtin_return_address( i )
void *Backtrace::getAddressAtFrame( Index aFrame )
{
preC_( aFrame >= 0 );
#if defined( __GNUC__ )
switch( aFrame )
{
case 0: return RA( 0 );
case 1: return RA( 1 );
case 2: return RA( 2 );
case 3: return RA( 3 );
case 4: return RA( 4 );
case 5: return RA( 5 );
case 6: return RA( 6 );
case 7: return RA( 7 );
case 8: return RA( 8 );
case 9: return RA( 9 );
case 10: return RA( 10 );
case 11: return RA( 11 );
case 12: return RA( 12 );
case 13: return RA( 13 );
case 14: return RA( 14 );
case 15: return RA( 15 );
case 16: return RA( 16 );
case 17: return RA( 17 );
case 18: return RA( 18 );
case 19: return RA( 19 );
case 20: return RA( 20 );
case 21: return RA( 21 );
case 22: return RA( 22 );
case 23: return RA( 23 );
case 24: return RA( 24 );
case 25: return RA( 25 );
case 26: return RA( 26 );
case 27: return RA( 27 );
case 28: return RA( 28 );
case 29: return RA( 29 );
case 30: return RA( 30 );
default: return ( void * ) NULL;
}
#else
return ( void * ) NULL;
#endif
}
#undef RA
#if defined( __GNUC__ )
static int compare_by_address( const void *symb1, const void *symb2 )
{
return( bfd_asymbol_value( *( asymbol ** ) symb1 ) -
bfd_asymbol_value( *( asymbol ** ) symb2 ) );
}
#endif
boolean Backtrace::loadSymTableFor( const char *anImage ) const
{
#if !defined( __GNUC__ )
return false;
#else
preC_( anImage != NULL );
bfd *abfd;
long storage_needed = 0;
static asymbol **symbol_table = NULL;
long number_of_symbols;
if( tableLoaded )
{
return true;
}
bfd_init();
if( bfd_get_error() != bfd_error_no_error )
{
bfd_perror( "bfd_init()" );
return false;
}
abfd = bfd_openr( anImage, "default" );
if( bfd_get_error() != bfd_error_no_error )
{
bfd_perror( "bfd_fdopenr()" );
return false;
}
if( ! bfd_check_format( abfd, bfd_object ) )
{
bfd_perror( "bfd_check_format()" );
return false;
}
storage_needed = bfd_get_symtab_upper_bound( abfd );
if( storage_needed < 0 )
{
bfd_perror( "bfd_get_symtab_upper_bound(): storage_needed < 0" );
return false;
}
if( storage_needed == 0 )
{
Debug::getLogger() -> log( "no symbols\n" );
return false;
}
symbol_table = new asymbol *[ storage_needed ];
number_of_symbols = bfd_canonicalize_symtab( abfd, symbol_table );
if( number_of_symbols < 0 )
{
bfd_perror( "bfd_canonicalize_symtab()" );
delete []symbol_table;
return false;
}
qsort( symbol_table, number_of_symbols, sizeof symbol_table[ 0 ],
compare_by_address );
for( Index i = 0 ; i < number_of_symbols ; ++i )
{
if( bfd_asymbol_bfd( symbol_table[ i ] ) == NULL )
{
continue;
}
table -> addKey
( new DataWrapper( ( void * ) bfd_asymbol_value( symbol_table[ i ] ) ),
new String( symbol_table[ i ] -> name ) );
}
if( ! bfd_close( abfd ) )
{
bfd_perror( "bfd_close()" );
delete []symbol_table;
LinkManager::free( this, table );
MUTATE_MEMBER( table, Backtrace ) = new DumbDictionary();
return false;
}
MUTATE_MEMBER( tableLoaded, Backtrace ) = true;
return true;
#endif
}
String Backtrace::getNameFor( void *anAddress ) const
{
Enumeration *en;
DataWrapper *current;
if( !loadSymTableFor( Platform::getInstance() -> getImageName() ) )
{
return "no symbol table";
}
test_( table != ( DumbDictionary * ) NULL );
en = table -> getValues();
current = ( DataWrapper * ) NULL;
while( en -> hasMoreElements() )
{
DataWrapper *symbol;
symbol = TCAST( en -> getNextElement(), DataWrapper );
if( ( current <= anAddress ) &&
( anAddress < symbol -> getValue() ) )
{
if( current == NULL )
{
return "unexistent";
}
else
{
return *TCAST( table -> getValueAtKey( current ), String );
}
}
else
{
current = symbol;
}
}
if( current != ( DataWrapper * ) NULL )
{
return *TCAST( table -> getValueAtKey( current ), String );
}
else
{
return "not found";
}
}
void *Backtrace::crt0Address = ( void * ) NULL;
boolean Backtrace::initialised = false;
#if defined( TESTING )
boolean Backtrace::tester( int aParam ) const
{
{
Backtrace *trace;
trace = new Backtrace();
OGuard _trace( trace, this );
trace -> fill();
Debug::getLogger() -> logObject( "trace: %s", trace );
}
return true;
}
#endif
#if defined(_INLINE)
#include "../src/Debug.ipp"
#endif