]> The Tcpdump Group git mirrors - tcpdump/blobdiff - instrument-functions.c
Update the GitHub issue template
[tcpdump] / instrument-functions.c
index 39ec762788a4f900624d6814af208f9d48dc774e..ba0a56a5309ff572e6a94d24c2d3d7c7ec31ef74 100644 (file)
  * 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.
@@ -30,66 +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
  */
 
-void __cyg_profile_func_enter(void *this_fn, void *call_site)
-                             __attribute__((no_instrument_function));
+#define ND_NO_INSTRUMENT __attribute__((no_instrument_function))
 
-void __cyg_profile_func_exit(void *this_fn, void *call_site)
-                            __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;
 
-/*
- * The get_function_name() get the function name by calling dladdr()
- */
+typedef enum {
+       ENTER,
+       EXIT
+} action_type;
+
+void __cyg_profile_func_enter(void *this_fn, void *call_site) ND_NO_INSTRUMENT;
 
-static const char *get_function_name(void *func)
-                             __attribute__((no_instrument_function));
+void __cyg_profile_func_exit(void *this_fn, void *call_site) ND_NO_INSTRUMENT;
 
-static const char *
-get_function_name(void *func)
+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)
 {
-       Dl_info info;
-       const char *function_name;
-
-       if (dladdr(func, &info))
-               function_name = info.dli_sname;
-       else
-               function_name = NULL;
-       return function_name;
+       print_debug(this_fn, call_site, ENTER);
 }
 
 void
-__cyg_profile_func_enter(void *this_fn,
-                             void *call_site __attribute__((unused)))
+__cyg_profile_func_exit(void *this_fn, void *call_site)
 {
-       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);
-       }
-       fflush(stdout);
+       print_debug(this_fn, call_site, EXIT);
 }
 
-void
-__cyg_profile_func_exit(void *this_fn,
-                            void *call_site __attribute__((unused)))
+static void print_debug(void *this_fn, void *call_site, action_type action)
 {
-       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;
+       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 : */