/* (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) (c) Copyright 2000-2004 Convergence (integrated media) GmbH All rights reserved. Written by Denis Oliver Kropp , Andreas Hundt , Sven Neumann , Ville Syrjälä and Claudio Ciccani . This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PIC #define DYNAMIC_LINKING #endif #if DIRECT_BUILD_TRACE #ifdef DYNAMIC_LINKING #include #endif #define MAX_BUFFERS 200 #define MAX_LEVEL 200 #define NAME_LEN 92 typedef enum { TF_NONE = 0x00000000, TF_DEBUG = 0x00000001 } TraceFlags; typedef struct { void *addr; TraceFlags flags; } Trace; struct __D_DirectTraceBuffer { pid_t tid; char *name; int level; bool in_trace; Trace trace[MAX_LEVEL]; }; /**************************************************************************************************/ static DirectTraceBuffer *buffers[MAX_BUFFERS]; static int buffers_num = 0; static pthread_mutex_t buffers_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_key_t trace_key = -1; /**************************************************************************************************/ __attribute__((no_instrument_function)) static void buffer_destroy( void *arg ) { int i; DirectTraceBuffer *buffer = arg; pthread_mutex_lock( &buffers_lock ); /* Remove from list. */ for (i=0; itid = direct_gettid(); buffer->name = name ? strdup( name ) : NULL; buffers[buffers_num++] = buffer; pthread_mutex_unlock( &buffers_lock ); } return buffer; } /**************************************************************************************************/ typedef struct { long offset; char name[NAME_LEN]; } Symbol; typedef struct { DirectLink link; char *filename; Symbol *symbols; int capacity; int num_symbols; } SymbolTable; static DirectLink *tables = NULL; static pthread_mutex_t tables_lock = PTHREAD_MUTEX_INITIALIZER; __attribute__((no_instrument_function)) static void add_symbol( SymbolTable *table, long offset, const char *name ) { Symbol *symbol; if (table->num_symbols == table->capacity) { Symbol *symbols; int capacity = table->capacity * 2; if (!capacity) capacity = 256; symbols = malloc( capacity * sizeof(Symbol) ); if (!symbols) { D_WARN( "out of memory" ); return; } direct_memcpy( symbols, table->symbols, table->num_symbols * sizeof(Symbol) ); free( table->symbols ); table->symbols = symbols; table->capacity = capacity; } symbol = &table->symbols[ table->num_symbols++ ]; symbol->offset = offset; direct_snputs( symbol->name, name, NAME_LEN ); } __attribute__((no_instrument_function)) static SymbolTable * load_symbols( const char *filename ) { SymbolTable *table; FILE *fp = NULL; bool is_pipe = false; char file[1024]; char line[1024]; int command_len; char *command; const char *full_path = filename; char *tmp; if (filename) { if (access( filename, R_OK ) < 0 && errno == ENOENT) { int len; if ((len = readlink( "/proc/self/exe", file, sizeof(file) - 1 )) < 0) { D_PERROR( "Direct/Trace: readlink( \"/proc/self/exe\" ) failed!\n" ); return NULL; } file[len] = 0; tmp = strrchr( file, '/' ); if (!tmp) return NULL; if (strcmp( filename, tmp + 1 )) return NULL; full_path = file; } } else { int len; if ((len = readlink( "/proc/self/exe", file, sizeof(file) - 1 )) < 0) { D_PERROR( "Direct/Trace: readlink( \"/proc/self/exe\" ) failed!\n" ); return NULL; } file[len] = 0; full_path = file; } command_len = strlen( full_path ) + 32; command = alloca( command_len ); /* First check if there's an "nm-n" file. */ tmp = strrchr( full_path, '/' ); if (!tmp) return NULL; *tmp = 0; snprintf( command, command_len, "%s/nm-n.%s", full_path, tmp + 1 ); *tmp = '/'; if (access( command, R_OK ) == 0) { fp = fopen( command, "r" ); if (!fp) D_PERROR( "Direct/Trace: fopen( \"%s\", \"r\" ) failed!\n", command ); } else { snprintf( command, command_len, "%s.nm", full_path ); if (access( command, R_OK ) == 0) { fp = fopen( command, "r" ); if (!fp) D_PERROR( "Direct/Trace: fopen( \"%s\", \"r\" ) failed!\n", command ); } } /* Fallback to live mode. */ if (!fp) { snprintf( command, command_len, "nm -n %s", full_path ); fp = popen( command, "r" ); if (!fp) { D_PERROR( "Direct/Trace: popen( \"%s\", \"r\" ) failed!\n", command ); return NULL; } is_pipe = true; } table = calloc( 1, sizeof(SymbolTable) ); if (!table) { D_OOM(); goto out; } if (filename) table->filename = strdup( filename ); while (fgets( line, sizeof(line), fp )) { int n; int digits = sizeof(long) * 2; long offset = 0; int length = strlen(line); if (line[0] == ' ' || length < (digits + 5) || line[length-1] != '\n') continue; if (line[digits + 1] != 't' && line[digits + 1] != 'T' && line[digits + 1] != 'W') continue; if (line[digits] != ' ' || line[digits + 2] != ' ' || line[digits + 3] == '.') continue; for (n=0; n= '0' && c <= '9') offset |= c - '0'; else offset |= c - 'a' + 10; } line[length-1] = 0; add_symbol( table, offset, line + digits + 3 ); } out: if (is_pipe) pclose( fp ); else fclose( fp ); return table; } __attribute__((no_instrument_function)) static int compare_symbols(const void *x, const void *y) { return *((const long*) x) - *((const long*) y); } __attribute__((no_instrument_function)) static SymbolTable * find_table( const char *filename ) { SymbolTable *table; if (filename) { direct_list_foreach (table, tables) { if (table->filename && !strcmp( filename, table->filename )) return table; } } else { direct_list_foreach (table, tables) { if (!table->filename) return table; } } return NULL; } /**************************************************************************************************/ __attribute__((no_instrument_function)) const char * direct_trace_lookup_symbol( const char *filename, long offset ) { Symbol *symbol; SymbolTable *table; pthread_mutex_lock( &tables_lock ); table = find_table( filename ); if (!table) { table = load_symbols( filename ); if (!table) { pthread_mutex_unlock( &tables_lock ); return false; } direct_list_prepend( &tables, &table->link ); } pthread_mutex_unlock( &tables_lock ); symbol = bsearch( &offset, table->symbols, table->num_symbols, sizeof(Symbol), compare_symbols ); return symbol ? symbol->name : NULL; } __attribute__((no_instrument_function)) const char * direct_trace_lookup_file( void *address, void **ret_base ) { #ifdef DYNAMIC_LINKING Dl_info info; if (dladdr( address, &info )) { if (ret_base) *ret_base = info.dli_fbase; return info.dli_fname; } else #endif { if (ret_base) *ret_base = NULL; } return NULL; } __attribute__((no_instrument_function)) void direct_trace_print_stack( DirectTraceBuffer *buffer ) { #ifdef DYNAMIC_LINKING Dl_info info; #endif int i; int level; if (!direct_config->trace) return; if (!buffer) buffer = get_trace_buffer(); if (buffer->in_trace) return; buffer->in_trace = true; level = buffer->level; if (level > MAX_LEVEL) { D_WARN( "only showing %d of %d items", MAX_LEVEL, level ); level = MAX_LEVEL; } else if (level == 0) { buffer->in_trace = false; return; } direct_log_lock( NULL ); if (buffer->name) direct_log_printf( NULL, "(-) [%5d: -STACK- '%s']\n", buffer->tid, buffer->name ); else direct_log_printf( NULL, "(-) [%5d: -STACK- ]\n", buffer->tid ); for (i=level-1; i>=0; i--) { void *fn = buffer->trace[i].addr; #ifdef DYNAMIC_LINKING if (dladdr( fn, &info )) { if (info.dli_fname) { const char *symbol = NULL;//info.dli_sname; if (!symbol) { symbol = direct_trace_lookup_symbol(info.dli_fname, (long)(fn - info.dli_fbase)); if (!symbol) { symbol = direct_trace_lookup_symbol(info.dli_fname, (long)(fn)); if (!symbol) { if (info.dli_sname) symbol = info.dli_sname; else symbol = "??"; } } } direct_log_printf( NULL, " #%-2d 0x%08lx in %s () from %s [%p]\n", level - i - 1, (unsigned long) fn, symbol, info.dli_fname, info.dli_fbase ); } else if (info.dli_sname) { direct_log_printf( NULL, " #%-2d 0x%08lx in %s ()\n", level - i - 1, (unsigned long) fn, info.dli_sname ); } else direct_log_printf( NULL, " #%-2d 0x%08lx in ?? ()\n", level - i - 1, (unsigned long) fn ); } else #endif { const char *symbol = direct_trace_lookup_symbol(NULL, (long)(fn)); direct_log_printf( NULL, " #%-2d 0x%08lx in %s ()\n", level - i - 1, (unsigned long) fn, symbol ? symbol : "??" ); } } direct_log_printf( NULL, "\n" ); direct_log_unlock( NULL ); buffer->in_trace = false; } __attribute__((no_instrument_function)) void direct_trace_print_stacks( void ) { int i; DirectTraceBuffer *buffer = get_trace_buffer(); if (buffer->level) direct_trace_print_stack( buffer ); pthread_mutex_lock( &buffers_lock ); for (i=0; ilevel) direct_trace_print_stack( buffers[i] ); } pthread_mutex_unlock( &buffers_lock ); } __attribute__((no_instrument_function)) int direct_trace_debug_indent( void ) { int in; DirectTraceBuffer *buffer = get_trace_buffer(); int level = buffer->level - 1; if (level < 0) return 0; buffer->trace[level--].flags |= TF_DEBUG; for (in=0; level>=0; level--) { if (buffer->trace[level].flags & TF_DEBUG) in++; } return in; } __attribute__((no_instrument_function)) DirectTraceBuffer * direct_trace_copy_buffer( DirectTraceBuffer *buffer ) { int level; DirectTraceBuffer *copy; if (!buffer) buffer = get_trace_buffer(); level = buffer->level; if (level > MAX_LEVEL) { D_WARN( "only copying %d of %d items", MAX_LEVEL, level ); level = MAX_LEVEL; } copy = calloc( 1, sizeof(*buffer) - sizeof(buffer->trace) + sizeof(buffer->trace[0]) * level ); if (!copy) return NULL; if (buffer->name) copy->name = strdup( buffer->name ); copy->tid = buffer->tid; copy->level = buffer->level; direct_memcpy( copy->trace, buffer->trace, level * sizeof(buffer->trace[0]) ); return copy; } __attribute__((no_instrument_function)) void direct_trace_free_buffer( DirectTraceBuffer *buffer ) { if (buffer->name) free( buffer->name ); free( buffer ); } /**************************************************************************************************/ __attribute__((no_instrument_function)) void __cyg_profile_func_enter (void *this_fn, void *call_site) { if (direct_config->trace) { DirectTraceBuffer *buffer = get_trace_buffer(); int level = buffer->level++; Trace *trace = &buffer->trace[level]; if (level < MAX_LEVEL) { trace->addr = this_fn; trace->flags = TF_NONE; } } } __attribute__((no_instrument_function)) void __cyg_profile_func_exit (void *this_fn, void *call_site) { if (direct_config->trace) { DirectTraceBuffer *buffer = get_trace_buffer(); if (buffer->level > 0) buffer->level--; } } #else const char * direct_trace_lookup_symbol( const char *filename, long offset ) { return NULL; } const char * direct_trace_lookup_file( void *address, void **ret_base ) { if (ret_base) *ret_base = NULL; return NULL; } void direct_trace_print_stack( DirectTraceBuffer *buffer ) { } void direct_trace_print_stacks( void ) { } int direct_trace_debug_indent( void ) { return 0; } DirectTraceBuffer * direct_trace_copy_buffer( DirectTraceBuffer *buffer ) { return NULL; } void direct_trace_free_buffer( DirectTraceBuffer *buffer ) { } #endif