* FOR A PARTICULAR PURPOSE.
*/
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
#include <stdio.h>
-#include <dlfcn.h>
-
-extern int profile_func_level;
-int profile_func_level = -1;
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <bfd.h>
/*
* Generate instrumentation calls for entry and exit to functions.
* 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 : */