/* LD_PRELOAD library that gives statistic on number of roundtrips in an application. $XREPLY_BACKTRACE defines whether and how backtraces will be printed for every roundtrip. If not set, only total number of roundtrips is printed after the process exits. If set to a number, backtrace for every roundtrip will be printed, and the backtraces will be as deep as the given number. If set to C (e.g. C10), the backtraces will be "compressed" - every backtrace will be printed only once after the process exits, together with number of times it occured. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include /* Since these symbols are weak, the apps can provide their own, and therefore e.g. temporarily suspend counting of roundtrips. At least theoretically, I haven't really tried it. */ __attribute((weak)) long ___xreply_reply_count = 0; __attribute((weak)) int ___xreply_reply_enabled = 1; #define MAX_BACKTRACES 1024 extern long ___xreply_reply_count; extern int ___xreply_reply_enabled; typedef tqStatus (*xreply_ptr_t)(Display*,xReply*,int,Bool); static xreply_ptr_t xreply_ptr = NULL; static int xreply_backtrace_set = 0; static int xreply_backtrace_type = 0; struct xreply_struct { char* key; char* text; int count; }; static struct xreply_struct backtraces[ MAX_BACKTRACES ]; static int backtraces_size = 0; static int xreply_compare( const void* left, const void* right ) { int left_count = ((struct xreply_struct*)left)->count; int right_count = ((struct xreply_struct*)right)->count; return right_count - left_count; } static void xreply_print(void) { char tmp[ 1024 ]; int fd; fd = open( "/proc/self/cmdline", O_RDONLY ); if( fd >= 0 ) { read( fd, tmp, 1024 ); tmp[ 1023 ] = '\0'; close( fd ); } fprintf( stderr, "XREPLY (%d : %s): %ld\n", getpid(), tmp, ___xreply_reply_count ); if( xreply_backtrace_type < 0 ) { int i; qsort( backtraces, backtraces_size, sizeof( struct xreply_struct ), xreply_compare ); for( i = 0; i < backtraces_size; ++i ) fprintf( stderr, "%d:%s\n\n", backtraces[ i ].count, backtraces[ i ].text ); } } static void xreply_backtrace() { void* trace[256]; int n = backtrace(trace, 256); char** strings = backtrace_symbols (trace, n); if( xreply_backtrace_type > 0 ) { fprintf( stderr, "%ld [\n", ___xreply_reply_count ); if( n > xreply_backtrace_type ) n = xreply_backtrace_type; int i; for( i = 0; i < n; ++i ) fprintf( stderr, "%d: %s\n", i, strings[ i ] ); fprintf( stderr, "]\n" ); } else { char stack[ 256 * 20 ]; int pos = 0; int i; stack[ 0 ] = '\0'; if( n > -xreply_backtrace_type ) n = -xreply_backtrace_type; for( i = 0; i < n; ++i ) { const char* start = strrchr( strings[ i ], '[' ); if( start == NULL ) assert( !"No [ in address." ); long addr; if( sscanf( start + 1, "0x%lx", &addr ) != 1 ) assert( !"Failed to parse address." ); if( sizeof( void* ) == 4 ) { sprintf( stack + pos, "0x%8lx", addr ); pos += 10; } else if( sizeof( void* ) == 8 ) { sprintf( stack + pos, "0x%16lx", addr ); pos += 18; } else assert( !"Unknown sizeof( void* )." ); } for( i = 0; i < backtraces_size; ++i ) if( strcmp( backtraces[ i ].key, stack ) == 0 ) { ++backtraces[ i ].count; break; } if( i == backtraces_size ) { int stack_text_size = 10; char* stack_text; char* stack_text_pos; for( i = 0; i < n; ++i ) stack_text_size += strlen( strings[ i ] ) + 5; stack_text = stack_text_pos = malloc( stack_text_size ); for( i = 0; i < n; ++i ) { stack_text_pos = stpcpy( stack_text_pos, "\n" ); stack_text_pos = stpcpy( stack_text_pos, strings[ i ] ); } backtraces[ backtraces_size ].key = strdup( stack ); backtraces[ backtraces_size ].text = stack_text; backtraces[ backtraces_size ].count = 1; ++backtraces_size; if( backtraces_size >= MAX_BACKTRACES ) assert( !"MAX_BACKTRACES reached." ); } } free (strings); } tqStatus _XReply (dpy, rep, extra, discard) register Display *dpy; register xReply *rep; int extra; /* number of 32-bit words expected after the reply */ Bool discard; /* should I discard data following "extra" words? */ { if( ___xreply_reply_enabled ) ++___xreply_reply_count; if( xreply_backtrace_set == 0 ) { if( getenv( "XREPLY_BACKTRACE" ) != NULL ) { // C - compress backtraces, saved as negative value in xreply_backtrace_type if( getenv( "XREPLY_BACKTRACE" )[ 0 ] == 'C' ) xreply_backtrace_type = -atoi( getenv( "XREPLY_BACKTRACE" ) + 1 ); else // - print the backtrace every time xreply_backtrace_type = atoi( getenv( "XREPLY_BACKTRACE" )); } else xreply_backtrace_type = 0; } if( xreply_backtrace_type != 0 ) xreply_backtrace(); if( xreply_ptr == NULL ) { xreply_ptr = (xreply_ptr_t)dlsym( RTLD_NEXT, "_XReply" ); if( xreply_ptr == NULL ) assert( !"dlsym() failed." ); atexit( xreply_print ); } return xreply_ptr( dpy, rep, extra, discard ); }