diff options
Diffstat (limited to 'instrument-functions.c')
-rw-r--r-- | instrument-functions.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/instrument-functions.c b/instrument-functions.c new file mode 100644 index 0000000..ba0a56a --- /dev/null +++ b/instrument-functions.c @@ -0,0 +1,250 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code + * distributions retain the above copyright notice and this paragraph + * in its entirety, and (2) distributions including binary code include + * the above copyright notice and this paragraph in its entirety in + * the documentation or other materials provided with the distribution. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND + * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT + * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <bfd.h> + +/* + * Generate instrumentation calls for entry and exit to functions. + * Just after function entry and just before function exit, the + * following profiling functions are called with the address of the + * current function and its call site (currently not use). + * + * The attribute 'no_instrument_function' causes this instrumentation is + * not done. + * + * 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. + * + * 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 + */ + +#define ND_NO_INSTRUMENT __attribute__((no_instrument_function)) + +/* Store the function call level, used also in pretty_print_packet() */ +extern int profile_func_level; +int profile_func_level = -1; + +typedef enum { + ENTER, + EXIT +} action_type; + +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; + +static void print_debug(void *this_fn, void *call_site, action_type action) + ND_NO_INSTRUMENT; + +void +__cyg_profile_func_enter(void *this_fn, void *call_site) +{ + print_debug(this_fn, call_site, ENTER); +} + +void +__cyg_profile_func_exit(void *this_fn, void *call_site) +{ + 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; + } + + if (instrument_off) + return; + + /* 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'; + + bfd_init(); + + 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; + } + + if ((text = bfd_get_section_by_name(abfd, ".text")) == NULL) { + bfd_perror("bfd_get_section_by_name"); + return; + } + vma = text->vma; + } + + 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; + } + + /* 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 : */ |