* FOR A PARTICULAR PURPOSE.
*/
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
#include <stdio.h>
-
-extern int profile_func_level;
-int profile_func_level = -1;
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.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 configure the printing of only the global functions names:
+ * $ make instrument_global
+ *
+ * To go back to print all the functions names:
+ * $ make instrument_all
*
- * To instument a static function, remove temporarily the static specifier.
+ * To print nothing, like with no instrumentation:
+ * $ make instrument_off
*/
-#ifndef ND_NO_INSTRUMENT
#define ND_NO_INSTRUMENT __attribute__((no_instrument_function))
-#endif
+
+/* 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;
-/*
- * Structure table to store the functions data from FILE_NAME.
- * FILE_NAME is generated via:
- * $ nm $(PROG) | grep ' [tT] '
- * or
- * $ nm $(PROG) | grep ' [T] '
- */
+static void print_debug(void *this_fn, void *call_site, action_type action)
+ ND_NO_INSTRUMENT;
-#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)
+{
+ print_debug(this_fn, call_site, ENTER);
+}
-/*
- * Read the result of nm in functions[]
- */
+void
+__cyg_profile_func_exit(void *this_fn, void *call_site)
+{
+ print_debug(this_fn, call_site, EXIT);
+}
-#define FILE_NAME "tcpdump_instrument_functions.nm"
+/* If this file exists, print only the global functions */
+#define ND_FILE_FLAG_GLOBAL "tcpdump_instrument_functions_global.devel"
-void read_functions_table(void) ND_NO_INSTRUMENT;
+/* If this file exists, print nothing, like with no instrumentation */
+#define ND_FILE_FLAG_OFF "tcpdump_instrument_functions_off.devel"
-void
-read_functions_table(void)
+static void print_debug(void *this_fn, void *call_site, action_type action)
{
- FILE *fp;
- int i = 0;
- if ((fp = fopen(FILE_NAME, "r")) == NULL) {
- printf("Warning: Cannot open \"%s\" file\n", FILE_NAME);
+ static bfd* abfd;
+ static asymbol **symtab;
+ static long symcount;
+ static asection *text;
+ static bfd_vma vma;
+ static int print_only_global;
+ symbol_info syminfo;
+ struct stat statbuf;
+ int i, found;
+
+ if (!stat(ND_FILE_FLAG_OFF, &statbuf))
return;
- }
- 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;
-}
-/*
- * 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;
-static const char * get_function_name(void *func) ND_NO_INSTRUMENT;
+ if (!stat(ND_FILE_FLAG_GLOBAL, &statbuf))
+ print_only_global = 1;
-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;
+ ssize_t ret = readlink("/proc/self/exe", pgm_name, sizeof(pgm_name));
+ if (ret == -1) {
+ perror("failed to find executable\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(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 (print_only_global) {
+ 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 {
+ 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 && 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 : */