summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancois-Xavier Le Bail <devel.fx.lebail@orange.fr>2022-01-24 09:56:21 +0100
committerFrancois-Xavier Le Bail <devel.fx.lebail@orange.fr>2022-01-27 16:05:58 +0100
commit65f4c88d27fa3a333b7f5303c34d8d10999e1d4d (patch)
tree0184c44a1aff098c0f97c54217ff1830e9122f6c
parent1af6dea4120b6d566f7c4e6eff0179b6fa819ba9 (diff)
downloadtcpdump-65f4c88d27fa3a333b7f5303c34d8d10999e1d4d.tar.gz
instrument functions: Enhance the output
If entering in a function, print also the calling function name with file name and line number. There may be a small shift in the line number. In some cases, with Clang 11, the file number is unknown (printed '??') or the line number is unknown (printed '?'). In this case, use GCC. To print nothing, like with no instrumentation: $ make instrument_off As before, the following commands are available: 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 The library libbfd is used, therefore the binutils-dev package is required.
-rw-r--r--.gitignore2
-rw-r--r--Makefile.in11
-rwxr-xr-xconfigure47
-rw-r--r--configure.ac10
-rw-r--r--instrument-functions.c273
5 files changed, 242 insertions, 101 deletions
diff --git a/.gitignore b/.gitignore
index 44294a09..a633695b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,5 @@ tcpdump.sln
.vs/
.passed
.failed
+tcpdump_instrument_functions_global.devel
+tcpdump_instrument_functions_off.devel
diff --git a/Makefile.in b/Makefile.in
index 2e3194e8..36122d5b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -389,13 +389,18 @@ all: $(PROG)
$(PROG): $(OBJ) @V_PCAPDEP@ $(LIBNETDISSECT)
@rm -f $@
$(CC) $(FULL_CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBNETDISSECT) $(LIBS)
- nm $(PROG) | grep ' [tT] ' > $(PROG)_instrument_functions.nm || :
instrument_all: $(PROG)
- nm $(PROG) | grep ' [tT] ' > $(PROG)_instrument_functions.nm || :
+ @rm -f $(PROG)_instrument_functions_global.devel
+ @rm -f $(PROG)_instrument_functions_off.devel
instrument_global: $(PROG)
- nm $(PROG) | grep ' [T] ' > $(PROG)_instrument_functions.nm || :
+ @touch $(PROG)_instrument_functions_global.devel
+ @rm -f $(PROG)_instrument_functions_off.devel
+
+instrument_off: $(PROG)
+ @touch $(PROG)_instrument_functions_off.devel
+ @rm -f $(PROG)_instrument_functions_global.devel
$(LIBNETDISSECT): $(LIBNETDISSECT_OBJ)
@rm -f $@
diff --git a/configure b/configure
index 4eaefa3c..4e84a433 100755
--- a/configure
+++ b/configure
@@ -4395,6 +4395,48 @@ fi
case "$enableval" in
yes) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for bfd_init in -lbfd" >&5
+$as_echo_n "checking for bfd_init in -lbfd... " >&6; }
+if ${ac_cv_lib_bfd_bfd_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lbfd $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char bfd_init ();
+int
+main ()
+{
+return bfd_init ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_bfd_bfd_init=yes
+else
+ ac_cv_lib_bfd_bfd_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bfd_bfd_init" >&5
+$as_echo "$ac_cv_lib_bfd_bfd_init" >&6; }
+if test "x$ac_cv_lib_bfd_bfd_init" = xyes; then :
+ true
+else
+ as_fn_error $? "--enable-instrument-functions was given, but test for library libbfd failed. Please install the 'binutils-dev' package." "$LINENO" 5
+fi
+
$as_echo "#define ENABLE_INSTRUMENT_FUNCTIONS 1" >>confdefs.h
@@ -4402,8 +4444,9 @@ $as_echo "#define ENABLE_INSTRUMENT_FUNCTIONS 1" >>confdefs.h
# Add '-finstrument-functions' instrumentation option to generate
# instrumentation calls for entry and exit to functions.
# Try to avoid Address Space Layout Randomization (ALSR).
- CFLAGS="$CFLAGS -finstrument-functions -fno-stack-protector -fno-pic"
- LDFLAGS="$LDFLAGS -fno-stack-protector -no-pie"
+ CFLAGS="$CFLAGS -O0 -ggdb -finstrument-functions -fno-stack-protector -fno-pic"
+ LDFLAGS="$LDFLAGS -O0 -ggdb -fno-stack-protector -no-pie"
+ LIBS="$LIBS -lbfd"
;;
*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
diff --git a/configure.ac b/configure.ac
index 5ed2db91..de00be56 100644
--- a/configure.ac
+++ b/configure.ac
@@ -154,14 +154,20 @@ AC_ARG_ENABLE([instrument-functions],
[enableval=no])
case "$enableval" in
yes) AC_MSG_RESULT(yes)
+ AC_CHECK_LIB([bfd], [bfd_init],
+ [true],
+ [AC_MSG_ERROR(
+ [--enable-instrument-functions was given, but test for library libbfd failed. Please install the 'binutils-dev' package.])],
+ [])
AC_DEFINE(ENABLE_INSTRUMENT_FUNCTIONS, 1,
[define if you want to build the instrument functions code])
LOCALSRC="$LOCALSRC instrument-functions.c"
# Add '-finstrument-functions' instrumentation option to generate
# instrumentation calls for entry and exit to functions.
# Try to avoid Address Space Layout Randomization (ALSR).
- CFLAGS="$CFLAGS -finstrument-functions -fno-stack-protector -fno-pic"
- LDFLAGS="$LDFLAGS -fno-stack-protector -no-pie"
+ CFLAGS="$CFLAGS -O0 -ggdb -finstrument-functions -fno-stack-protector -fno-pic"
+ LDFLAGS="$LDFLAGS -O0 -ggdb -fno-stack-protector -no-pie"
+ LIBS="$LIBS -lbfd"
;;
*) AC_MSG_RESULT(no)
;;
diff --git a/instrument-functions.c b/instrument-functions.c
index d85c7936..ba87be9a 100644
--- a/instrument-functions.c
+++ b/instrument-functions.c
@@ -11,14 +11,13 @@
* 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 : */