--------------------- PatchSet 991 Date: 2000/12/22 13:03:05 Author: nikitadanilov Branch: raid Tag: (none) Log: abend.c contains functions to handle abnormal program termination. Useful exported functions: void printBacktrace(): prints stack backtrace of current thread. Tries to resolve addresses to symbol names using either BDF library interfaces of dlsym and friends. If compiler has no support for stack traversing, interprets stack as array of pointers and tries to resolve each. This will dump garbage into output: all automatic variables will be treated as return addresses. void abend(): prints backtrace and either dumps core, attaches debugger or _exits. Choice is hardcoded in abendInit() for now. If you want to attach debugger you should arrange its stdout and stdin yourself. Members: src/abend.c:1.1->1.1.2.1 --- /dev/null Wed Feb 14 00:45:56 2007 +++ squid/src/abend.c Wed Feb 14 00:47:26 2007 @@ -0,0 +1,600 @@ +/* + * $Id: abend.c,v 1.1.2.1 2000/12/22 13:03:05 nikitadanilov Exp $ + * + * DEBUG: section 92 Support for abnormal termination hanlding + * AUTHOR: Nikita Danilov + * COPYRIGHT: This file is Copyright 2000 by Hans Reiser + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * the Regents of the University of California. Please see the + * COPYRIGHT file for full details. Squid incorporates software + * developed and/or copyrighted by other sources. Please see the + * CREDITS file for full details. + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +#define HAS_BFD +#define USE_DLADDR_DLSYM_FOR_BACKTRACE + +#if defined( HAS_BFD ) +#include +#endif + +#if defined( USE_DLADDR_DLSYM_FOR_BACKTRACE ) +#include +#endif + +#if defined( __GNUC__ ) +#define USE_BUILTIN_RETURN_ADDRESS +#endif + +/** debugging facilities */ +#define ABEND_OUTPUT( format, args... ) \ + ( CURRENT_FUNCTION_NAME " (%s:%i): " format "\n", \ + __FILE__, __LINE__ , ##args ) + +#define DEBUG( format, args... ) debug( 92, 9 ) ABEND_OUTPUT( format, ##args ) +#define ERROR( format, args... ) debug( 92, 1 ) ABEND_OUTPUT( format, ##args ) +#define PRINT( format, args... ) debug( 92, 2 ) ( format, ##args ) + +/** initialises abend sub-system. Should be called from main directly + (that is, not from function called from main. */ +void abendInit( int argc, char **argv ); + +/** prints current stack trace to the debug( 92, 2 ). Tries to resolve + address to symbol name either through bfd interfaces or through + dlsym. If you see spurious `sigaction' in backtrace, read it as + __restore() --- function call inserted into stack by kernel when + vectoring control to the signal handler. Problem is that __restore + is not exported by libc. */ +void printBacktrace(); + +/** abnormal end. Prints backtrace them either dumps core into + specified directory (/tmp by default --- XXX Unixism) or tries to + attach debugger or _exits */ +void abend(); + +/** resolves address to the symbol name. + If exactAddress is not NULL, address of the beginning of the + symbol in question returned there. */ +const char *getNameByAddress( void *address, void **exactAddress ); + +/** resolves symbol name to the address */ +void *getAddressFor( char *name ); + +static void *getFrame( int n ); +static int isValidFrame( void *frame ); +static int isLastFrame( void *frame ); +static int initBfd( char *image ); +static int compare_by_address( const void *symb1, const void *symb2 ); +static int stackGrowth(); +static const char *getBfdNameByAddress( void *address, void **exactAddress ); +static const char *getDlNameByAddress( void *address, void **exactAddress ); + +#define IMAGE "/home/god/run/squidng/bin/squid" +#define DEBUGGER_COMMAND "/usr/bin/gdb " IMAGE " %2$li > /dev/pts/1 < /dev/pts/1 2> /dev/pts/1" + +#define START_MAGIC 0xacc01ade +#define END_MAGIC 0x1abe11ed +static struct data_s +{ + unsigned long startMagic; + void *symbolJustBeforeMain; + void *symbolJustAfterMain; + void *mainEntryAddress; + unsigned int dumpCore :1; + unsigned int attachDebugger :1; + unsigned int stackGrowsUp :1; + char coredumpDir[ MAXPATHLEN + 1 ]; + char debuggerCommand[ 1024 ]; + struct + { + int tableLoaded; + int wasBFDError; + int tableSize; + void **addresses; + char **names; + } bfd; + unsigned long endMagic; +} data; + +void abendInit( int argc, char **argv ) +{ + extern void funJustBeforeMain(); + extern void funJustAfterMain(); + + DEBUG( "Initializing abend" ); + data.startMagic = START_MAGIC; + data.symbolJustBeforeMain = NULL; + data.symbolJustAfterMain = NULL; + data.mainEntryAddress = NULL; + data.stackGrowsUp = stackGrowth(); + data.endMagic = END_MAGIC; +#if defined( USE_BUILTIN_RETURN_ADDRESS ) + data.mainEntryAddress = __builtin_return_address( 1 ); +#endif + data.symbolJustBeforeMain = funJustBeforeMain; + data.symbolJustAfterMain = funJustAfterMain; + snprintf( data.debuggerCommand, sizeof data.debuggerCommand, + DEBUGGER_COMMAND, argv[ 0 ], ( long ) getpid() ); + strcpy( data.coredumpDir, "/tmp" ); + data.dumpCore = 1; + data.attachDebugger = 0; + /* this is to avoid inlining by smart compilers */ + if( ( ( char * ) &data ) + 1 == NULL ) + { + DEBUG( "dying early" ); + /* take address */ + argv[ 0 ] = ( char * ) abendInit; + abendInit( argc - 1, argv ); + /* tail recursive function can be inlined? */ + DEBUG( "hehe" ); + } + data.bfd.tableLoaded = 0; + data.bfd.wasBFDError = 0; + initBfd( IMAGE ); +} + +void printBacktrace() +{ + int i; + void *frame; + + PRINT( "Stack backtrace:\n" ); + for( i = 0 ; isValidFrame( frame = getFrame( i ) ) ; ++i ) + { + void *baseAddress; + const char *name; + + name = getNameByAddress( frame, &baseAddress ); + if( name != NULL ) + { + PRINT( "%i: %p: %s %c 0x%x\n", i, frame, name, + ( baseAddress != NULL ) ? '+' : ' ', + ( baseAddress != NULL ) ? frame - baseAddress : 0 ); + } + if( isLastFrame( frame ) ) + { + break; + } + } + PRINT( "End backtrace.\n" ); +} + +void abend() +{ + int endOfEternity; + + if( ( data.startMagic == START_MAGIC ) && + ( data.endMagic == END_MAGIC ) ) + { + printBacktrace(); + if( data.dumpCore ) + { + struct rlimit infiniteCore; + + /* we dont' care about errors from following calls, because + what can we do anyway? */ + signal( SIGABRT, SIG_DFL ); + /* this is to lay down ghosts of Linux */ + getrlimit( RLIMIT_CORE, &infiniteCore ); + infiniteCore.rlim_cur = infiniteCore.rlim_max = RLIM_INFINITY; + setrlimit( RLIMIT_CORE, &infiniteCore ); + chdir( data.coredumpDir ); + kill( getpid(), SIGABRT ); + } + else if( data.attachDebugger ) + { + if( !fork() ) + { + system( data.debuggerCommand ); + _exit( 1 ); + } + } + else + { + _exit( 1 ); + } + } + else + { + /* someone danced fandango on our core */ + } + endOfEternity = 0; + while( !endOfEternity ) + {} + /* yes, we _can_ get here */ +} + +static void *getFrame( int n ) +{ +#if defined( USE_BUILTIN_RETURN_ADDRESS ) +#define GET_FRAME( n ) case n: return __builtin_return_address( n ) + + switch( n ) + { + GET_FRAME( 0 ); + GET_FRAME( 1 ); + GET_FRAME( 2 ); + GET_FRAME( 3 ); + GET_FRAME( 4 ); + GET_FRAME( 5 ); + GET_FRAME( 6 ); + GET_FRAME( 7 ); + GET_FRAME( 8 ); + GET_FRAME( 9 ); + GET_FRAME( 10 ); + GET_FRAME( 11 ); + GET_FRAME( 12 ); + GET_FRAME( 13 ); + GET_FRAME( 14 ); + GET_FRAME( 15 ); + GET_FRAME( 16 ); + GET_FRAME( 17 ); + GET_FRAME( 18 ); + GET_FRAME( 19 ); + GET_FRAME( 20 ); + GET_FRAME( 21 ); + GET_FRAME( 22 ); + GET_FRAME( 23 ); + GET_FRAME( 24 ); + GET_FRAME( 25 ); + GET_FRAME( 26 ); + GET_FRAME( 27 ); + GET_FRAME( 28 ); + GET_FRAME( 29 ); + GET_FRAME( 30 ); + GET_FRAME( 31 ); + GET_FRAME( 32 ); + default: + return NULL; + } +#undef GET_FRAME +#else + void *local; + + return ( &local )[ data.stackGrowsUp ? -n : +n ]; +#endif +} + +static int isValidFrame( void *frame ) +{ +#if defined( USE_BUILTIN_RETURN_ADDRESS ) + return frame != NULL; +#else + return 1; +#endif +} + +static int isLastFrame( void *frame ) +{ +#if defined( USE_BUILTIN_RETURN_ADDRESS ) + if( frame == NULL ) + { + return 1; + } + else if( frame == data.mainEntryAddress ) + { + return 1; + } + else +#else + if( ( data.symbolJustBeforeMain <= frame ) && + ( frame <= data.symbolJustAfterMain ) ) + { + return 1; + } + else +#endif + { + return 0; + } +} + +const char *getNameByAddress( void *address, void **exactAddress ) +{ + if( address != NULL ) + { + const char *result; + + result = getBfdNameByAddress( address, exactAddress ); + if( result == NULL ) + { + result = getDlNameByAddress( address, exactAddress ); + } + return result; + } + else + { + return "NIL"; + } +} + +static const char *getBfdNameByAddress( void *address, void **exactAddress ) +{ +#if defined( HAS_BFD ) + int low; + int high; + + low = 0; + high = data.bfd.tableSize - 1; + + if( data.bfd.addresses[ high ] <= address ) + { + return NULL; + } + if( data.bfd.addresses[ 0 ] > address ) + { + return NULL; + } + + /* binary serach */ + while( high - low > 1 ) + { + int median; + + median = ( high + low ) / 2; + if( data.bfd.addresses[ median ] > address ) + { + high = median; + } + else + { + low = median; + } + assert( high > low ); + } + assert( ( high == low + 1 ) && + ( data.bfd.addresses[ low ] <= address ) && + ( address < data.bfd.addresses[ high ] ) ); + if( exactAddress != NULL ) + { + *exactAddress = data.bfd.addresses[ low ]; + } + return data.bfd.names[ low ]; +#else + return NULL; +#endif +} + +static char dlSymbolName[ 1024 ]; + +static const char *getDlNameByAddress( void *address, void **exactAddress ) +{ +#if defined( USE_DLADDR_DLSYM_FOR_BACKTRACE ) + Dl_info symbolInfo; + + if( dladdr( address, &symbolInfo ) == 0 ) + { + return NULL; + } + else if( symbolInfo.dli_sname != NULL ) + { + if( exactAddress != NULL ) + { + *exactAddress = symbolInfo.dli_saddr; + } + snprintf( dlSymbolName, sizeof dlSymbolName, "(%s@%p) %s", + ( symbolInfo.dli_fname != NULL ) ? symbolInfo.dli_fname : "", + symbolInfo.dli_fbase, + symbolInfo.dli_sname ); + return dlSymbolName; + } +#endif + return NULL; +} + +void *getAddressFor( char *name ) +{ + void *result; + + result = NULL; +#if defined( USE_DLADDR_DLSYM_FOR_BACKTRACE ) + + result = dlsym( RTLD_DEFAULT, name ); +#endif +#if defined( HAS_BFD ) + if( result == NULL ) + { + int i; + + for( i = 0 ; i < data.bfd.tableSize ; ++i ) + { + if( !strcmp( name, data.bfd.names[ i ] ) ) + { + result = data.bfd.addresses[ i ]; + break; + } + } + } +#endif + return result; +} + +static int initBfd( char *image ) +{ +#if defined( HAS_BFD ) +#define BFD_ERROR( text ) bfd_perror( text ) ; ERROR( text ) + + bfd *abfd; + int storage_needed = 0; + asymbol **symbol_table = ( asymbol ** ) NULL; + int number_of_symbols; + int i; + int j; + + if( data.bfd.tableLoaded ) + { + return 1; + } + if( data.bfd.wasBFDError ) + { + return 0; + } + + bfd_init(); + if( bfd_get_error() != bfd_error_no_error ) + { + BFD_ERROR( "bfd_init()" ); + data.bfd.wasBFDError = 1; + return 0; + } + + abfd = bfd_openr( image, "default" ); + if( bfd_get_error() != bfd_error_no_error ) + { + BFD_ERROR( "bfd_fdopenr()" ); + data.bfd.wasBFDError = 1; + return 0; + } + if( ! bfd_check_format( abfd, bfd_object ) ) + { + BFD_ERROR( "bfd_check_format()" ); + data.bfd.wasBFDError = 1; + return 0; + } + + storage_needed = bfd_get_symtab_upper_bound( abfd ); + + if( storage_needed < 0 ) + { + BFD_ERROR( "bfd_get_symtab_upper_bound(): storage_needed < 0" ); + data.bfd.wasBFDError = 1; + return 0; + } + if( storage_needed == 0 ) + { + DEBUG( "no symbols" ); + data.bfd.wasBFDError = 1; + return 0; + } + symbol_table = xmalloc( sizeof( asymbol ) *storage_needed ); + + number_of_symbols = bfd_canonicalize_symtab( abfd, symbol_table ); + if( number_of_symbols < 0 ) + { + BFD_ERROR( "bfd_canonicalize_symtab()" ); + xfree( symbol_table ); + data.bfd.wasBFDError = 1; + return 0; + } + + qsort( symbol_table, number_of_symbols, sizeof symbol_table[ 0 ], + compare_by_address ); + + data.bfd.tableSize = 0; + for( i = 0 ; i < number_of_symbols ; ++i ) + { + if( bfd_asymbol_bfd( symbol_table[ i ] ) == ( bfd * ) NULL ) + { + continue; + } + /* unnamed sections and other garbage */ + if( symbol_table[ i ] -> name[ 0 ] == ( char ) 0 ) + { + continue; + } + ++data.bfd.tableSize; + } + + data.bfd.addresses = xmalloc( data.bfd.tableSize * sizeof( void * ) ); + data.bfd.names = xmalloc( data.bfd.tableSize * sizeof( char * ) ); + + DEBUG( "bfd tables with %i entries allocated", data.bfd.tableSize ); + + j = 0; + for( i = 0 ; i < number_of_symbols ; ++i ) + { + if( bfd_asymbol_bfd( symbol_table[ i ] ) == NULL ) + { + continue; + } + if( symbol_table[ i ] -> name[ 0 ] == ( char ) 0 ) + { + continue; + } + data.bfd.addresses[ j ] = + ( void * ) bfd_asymbol_value( symbol_table[ i ] ); + data.bfd.names[ j ] = xstrdup( symbol_table[ i ] -> name ); + ++j; + } + + xfree( symbol_table ); + if( ! bfd_close( abfd ) ) + { + BFD_ERROR( "bfd_close()" ); + data.bfd.wasBFDError = 1; + /* return 1 anyway */ + } + data.bfd.tableLoaded = 1; +#undef BFD_ERROR +#endif + return 1; +} + +static int compare_by_address( const void *symb1, const void *symb2 ) +{ +#if defined( HAS_BFD ) + return( bfd_asymbol_value( *( asymbol ** ) symb1 ) - + bfd_asymbol_value( *( asymbol ** ) symb2 ) ); +#else + return 1; +#endif +} + +static int stackGrowthCalled( int *localOfTheCaller ) +{ + int bar; + + return &bar > localOfTheCaller; +} + +static int stackGrowth() +{ + int foo; + return stackGrowthCalled( &foo ); +} + + +/* + * $Log: abend.c,v $ + * Revision 1.1.2.1 2000/12/22 13:03:05 nikitadanilov + * abend.c contains functions to handle abnormal program termination. + * Useful exported functions: + * + * void printBacktrace(): prints stack backtrace of current + * thread. Tries to resolve addresses to symbol names using either + * BDF library interfaces of dlsym and friends. If compiler has no + * support for stack traversing, interprets stack as array of + * pointers and tries to resolve each. This will dump garbage into + * output: all automatic variables will be treated as return + * addresses. + * + * void abend(): prints backtrace and either dumps core, attaches debugger + * or _exits. Choice is hardcoded in abendInit() for now. If you want + * to attach debugger you should arrange its stdout and stdin yourself. + * + * + */