]> The Tcpdump Group git mirrors - tcpdump/blobdiff - instrument-functions.c
instrument functions: Enhance the output
[tcpdump] / instrument-functions.c
index d85c7936aaf563d95b98719fc04bf02f3c2bc954..ba87be9ad8ed4213b3be324ba17f692a8397df0e 100644 (file)
  * 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.
@@ -29,117 +28,203 @@ 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 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 : */