X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/6d854639ea6702adc7fcb077361ae00d2229f875..HEAD:/instrument-functions.c diff --git a/instrument-functions.c b/instrument-functions.c index bab32065..ba0a56a5 100644 --- a/instrument-functions.c +++ b/instrument-functions.c @@ -11,15 +11,11 @@ * FOR A PARTICULAR PURPOSE. */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - #include -#include - -extern int profile_func_level; -int profile_func_level = -1; +#include +#include +#include +#include /* * Generate instrumentation calls for entry and exit to functions. @@ -30,118 +26,225 @@ int profile_func_level = -1; * The attribute 'no_instrument_function' causes this instrumentation is * not done. * - * These profiling functions print the function names with indentation - * and call level. + * These profiling functions call print_debug(). This function prints the + * current function name with indentation and call level. + * If entering in a function it prints also the calling function name with + * file name and line number. * - * To instument a static function, remove temporarily the static specifier. + * If the environment variable INSTRUMENT is + * unset or set to an empty string, print nothing, like with no instrumentation + * set to "all" or "a", print all the functions names + * set to "global" or "g", print only the global functions names */ -#ifndef ND_NO_INSTRUMENT #define ND_NO_INSTRUMENT __attribute__((no_instrument_function)) -#endif - -void __cyg_profile_func_enter(void *this_fn, void *call_site) ND_NO_INSTRUMENT; -void __cyg_profile_func_exit(void *this_fn, void *call_site) ND_NO_INSTRUMENT; +/* Store the function call level, used also in pretty_print_packet() */ +extern int profile_func_level; +int profile_func_level = -1; -/* - * Structure table to store the functions data from FILE_NAME. - * FILE_NAME is generated via: - * $ nm $(PROG) | grep ' [tT] ' - * or - * $ nm $(PROG) | grep ' [T] ' - */ +typedef enum { + ENTER, + EXIT +} action_type; -#define MAX_FUNCTIONS 20000 -static struct { - void *addr; - char type; - char name[128]; -} functions[MAX_FUNCTIONS] ; -static int functions_count; -static int initialization_done; +void __cyg_profile_func_enter(void *this_fn, void *call_site) ND_NO_INSTRUMENT; -/* - * Read the result of nm in functions[] - */ +void __cyg_profile_func_exit(void *this_fn, void *call_site) ND_NO_INSTRUMENT; -#define FILE_NAME "tcpdump_instrument_functions.nm" +static void print_debug(void *this_fn, void *call_site, action_type action) + ND_NO_INSTRUMENT; -void read_functions_table(void) ND_NO_INSTRUMENT; +void +__cyg_profile_func_enter(void *this_fn, void *call_site) +{ + print_debug(this_fn, call_site, ENTER); +} void -read_functions_table(void) +__cyg_profile_func_exit(void *this_fn, void *call_site) { - FILE *fp; - int i = 0; - if ((fp = fopen(FILE_NAME, "r")) == NULL) { - printf("Warning: Cannot open \"%s\" file\n", FILE_NAME); - return; + print_debug(this_fn, call_site, EXIT); +} + +static void print_debug(void *this_fn, void *call_site, action_type action) +{ + static bfd* abfd; + static asymbol **symtab; + static long symcount; + static asection *text; + static bfd_vma vma; + static int instrument_set; + static int instrument_off; + static int instrument_global; + + if (!instrument_set) { + static char *instrument_type; + + /* Get the configuration environment variable INSTRUMENT value if any */ + instrument_type = getenv("INSTRUMENT"); + /* unset or set to an empty string ? */ + if (instrument_type == NULL || + !strncmp(instrument_type, "", sizeof(""))) { + instrument_off = 1; + } else { + /* set to "global" or "g" ? */ + if (!strncmp(instrument_type, "global", sizeof("global")) || + !strncmp(instrument_type, "g", sizeof("g"))) + instrument_global = 1; + else if (strncmp(instrument_type, "all", sizeof("all")) && + strncmp(instrument_type, "a", sizeof("a"))) { + fprintf(stderr, "INSTRUMENT can be only \"\", \"all\", \"a\", " + "\"global\" or \"g\".\n"); + exit(1); + } + } + instrument_set = 1; } - while (i < MAX_FUNCTIONS && fscanf(fp, "%p %c %s", &functions[i].addr, - &functions[i].type, functions[i].name) != EOF) - i++; - fclose(fp); - functions_count = i; -} + if (instrument_off) + return; -/* - * Get the function name by searching in functions[] - */ + /* If no errors, this block should be executed one time */ + if (!abfd) { + char pgm_name[1024]; + long symsize; + + ssize_t ret = readlink("/proc/self/exe", pgm_name, sizeof(pgm_name)); + if (ret == -1) { + perror("failed to find executable"); + return; + } + if (ret == sizeof(pgm_name)) { + /* no space for the '\0' */ + printf("truncation may have occurred\n"); + return; + } + pgm_name[ret] = '\0'; -static const char * get_function_name(void *func) ND_NO_INSTRUMENT; + bfd_init(); -static const char * -get_function_name(void *func) -{ - int i = 0; - int found = 0; - if (!initialization_done) { - read_functions_table(); - initialization_done = 1; - } - while (i < functions_count) { - if (functions[i].addr == func) { - found = 1; - break; + abfd = bfd_openr(pgm_name, NULL); + if (!abfd) { + bfd_perror("bfd_openr"); + return; + } + + if (!bfd_check_format(abfd, bfd_object)) { + bfd_perror("bfd_check_format"); + return; + } + + if((symsize = bfd_get_symtab_upper_bound(abfd)) == -1) { + bfd_perror("bfd_get_symtab_upper_bound"); + return; + } + + symtab = (asymbol **)malloc((size_t)symsize); + symcount = bfd_canonicalize_symtab(abfd, symtab); + if (symcount < 0) { + free(symtab); + bfd_perror("bfd_canonicalize_symtab"); + return; } - i++; + + if ((text = bfd_get_section_by_name(abfd, ".text")) == NULL) { + bfd_perror("bfd_get_section_by_name"); + return; + } + vma = text->vma; } - if (found) - return (functions[i].name); - else - return NULL; -} -void -__cyg_profile_func_enter(void *this_fn, - void *call_site __attribute__((unused))) -{ - int i; - const char *function_name; - - if ((function_name = get_function_name(this_fn)) != NULL) { - profile_func_level += 1; - for (i = 0 ; i < profile_func_level ; i++) - putchar(' '); - printf("[>> %s (%d)]\n", function_name, profile_func_level); + if (instrument_global) { + symbol_info syminfo; + int found; + long i; + + i = 0; + found = 0; + while (i < symcount && !found) { + bfd_get_symbol_info(abfd, symtab[i], &syminfo); + if ((void *)syminfo.value == this_fn) { + found = 1; + } + i++; + } + /* type == 'T' for a global function */ + if (found == 1 && syminfo.type != 'T') + return; } - fflush(stdout); -} -void -__cyg_profile_func_exit(void *this_fn, - void *call_site __attribute__((unused))) -{ - int i; - const char *function_name; - - if ((function_name = get_function_name(this_fn)) != NULL) { - for (i = 0 ; i < profile_func_level ; i++) - putchar(' '); - printf ("[<< %s (%d)]\n", function_name, profile_func_level); - profile_func_level -= 1; + /* Current function */ + if ((bfd_vma)this_fn < vma) { + printf("[ERROR address this_fn]"); + } else { + const char *file; + const char *func; + unsigned int line; + + if (!bfd_find_nearest_line(abfd, text, symtab, (bfd_vma)this_fn - vma, + &file, &func, &line)) { + printf("[ERROR bfd_find_nearest_line this_fn]"); + } else { + int i; + + if (action == ENTER) + profile_func_level += 1; + /* Indentation */ + for (i = 0 ; i < profile_func_level ; i++) + putchar(' '); + if (action == ENTER) + printf("[>> "); + else + printf("[<< "); + /* Function name */ + if (func == NULL || *func == '\0') + printf("???"); + else + printf("%s", func); + printf(" (%d)", profile_func_level); + /* Print the "from" part except for the main function) */ + if (action == ENTER && func != NULL && + strncmp(func, "main", sizeof("main"))) { + /* Calling function */ + if ((bfd_vma)call_site < vma) { + printf("[ERROR address call_site]"); + } else { + if (!bfd_find_nearest_line(abfd, text, symtab, + (bfd_vma)call_site - vma, &file, + &func, &line)) { + printf("[ERROR bfd_find_nearest_line call_site]"); + } else { + printf(" from "); + /* Function name */ + if (func == NULL || *func == '\0') + printf("???"); + else + printf("%s", func); + /* File name */ + if (file == NULL || *file == '\0') + printf(" ??:"); + else { + char *slashp = strrchr(file, '/'); + if (slashp != NULL) + file = slashp + 1; + printf(" %s:", file); + } + /* Line number */ + if (line == 0) + printf("?"); + else + printf("%u", line); + printf("]"); + } + } + } + putchar('\n'); + if (action == EXIT) + profile_func_level -= 1; + } } fflush(stdout); } + +/* vi: set tabstop=4 softtabstop=0 shiftwidth=4 smarttab autoindent : */