Generate instrumentation calls for entry and exit to functions.
Just after function entry and just before function exit, these
profiling functions are called and print the function names with
indentation and call level.
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.
Usage:
$ ./configure --enable-instrument-functions
$ make -s clean all
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
This allows to run:
$ INSTRUMENT=a ./tcpdump ...
$ INSTRUMENT=g ./tcpdump ...
$ INSTRUMENT= ./tcpdump ...
or
$ export INSTRUMENT=global
$ ./tcpdump ...
The library libbfd is used, therefore the binutils-dev package is required.
(backported from main)
[skip ci]
## Code style and generic remarks
1) A thorough reading of some other printers code is useful.
-2) Put the normative reference if any as comments (RFC, etc.).
+2) To help learn how tcpdump works or to help debugging:
+ You can configure and build tcpdump with the instrumentation of functions:
+ ```
+ $ ./configure --enable-instrument-functions
+ $ make -s clean all
+ ```
+
+ This generates instrumentation calls for entry and exit to functions.
+ Just after function entry and just before function exit, these
+ profiling functions are called and print the function names with
+ indentation and call level.
+
+ If entering in a function, it prints 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.
+
+ 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
+
+ This allows to run:
+ ```
+ $ INSTRUMENT=a ./tcpdump ...
+ $ INSTRUMENT=g ./tcpdump ...
+ $ INSTRUMENT= ./tcpdump ...
+ ```
+ or
+ ```
+ $ export INSTRUMENT=global
+ $ ./tcpdump ...
+ ```
+
+ The library libbfd is used, therefore the binutils-dev package is required.
+
+3) Put the normative reference if any as comments (RFC, etc.).
-3) Put the format of packets/headers/options as comments if there is no
+4) Put the format of packets/headers/options as comments if there is no
published normative reference.
-4) The printer may receive incomplete packet in the buffer, truncated at any
+5) The printer may receive incomplete packet in the buffer, truncated at any
random position, for example by capturing with `-s size` option.
This means that an attempt to fetch packet data based on the expected
format of the packet may run the risk of overrunning the buffer.
the string is a sequence of XX:XX:... values for the bytes
of the address.
-5) When defining a structure corresponding to a packet or part of a
+6) When defining a structure corresponding to a packet or part of a
packet, so that a pointer to packet data can be cast to a pointer to
that structure and that structure pointer used to refer to fields in
the packet, use the `nd_*` types for the structure members.
The `nd_*` type for a byte in a sequence of bytes is `nd_byte`; an
*N*-byte sequence should be declared as `nd_byte[N]`.
-6) Do invalid packet checks in code: Think that your code can receive in input
+7) Do invalid packet checks in code: Think that your code can receive in input
not only a valid packet but any arbitrary random sequence of octets (packet
* built malformed originally by the sender or by a fuzz tester,
* became corrupted in transit or for some other reason).
Print with: `nd_print_invalid(ndo); /* to print " (invalid)" */`
-7) Use `struct tok` for indexed strings and print them with
+8) Use `struct tok` for indexed strings and print them with
`tok2str()` or `bittok2str()` (for flags).
All `struct tok` must end with `{ 0, NULL }`.
-8) Avoid empty lines in output of printers.
+9) Avoid empty lines in output of printers.
-9) A commit message must have:
+10) A commit message must have:
```
First line: Capitalized short summary in the imperative (50 chars or less)
the body.
```
-10) Avoid non-ASCII characters in code and commit messages.
+11) Avoid non-ASCII characters in code and commit messages.
-11) Use the style of the modified sources.
+12) Use the style of the modified sources.
-12) Don't mix declarations and code.
+13) Don't mix declarations and code.
-13) tcpdump requires a compiler that supports C99 or later, so C99
+14) tcpdump requires a compiler that supports C99 or later, so C99
features may be used in code, but C11 or later features should not be
used.
-14) Avoid trailing tabs/spaces
+15) Avoid trailing tabs/spaces
TAGFILES = $(SRC) $(HDR) $(TAGHDR) $(LIBNETDISSECT_SRC) \
print-smb.c smbutil.c
-CLEANFILES = $(PROG) $(OBJ) $(LIBNETDISSECT_OBJ) print-smb.o smbutil.o
+CLEANFILES = $(PROG) $(OBJ) $(LIBNETDISSECT_OBJ) \
+ print-smb.o smbutil.o instrument-functions.o
EXTRA_DIST = \
CHANGES \
doc/README.solaris.md \
doc/README.Win32.md \
install-sh \
+ instrument-functions.c \
lbl/os-osf4.h \
lbl/os-solaris2.h \
lbl/os-sunos4.h \
/* Define to 1 if arpa/inet.h declares `ether_ntohost' */
#undef ARPA_INET_H_DECLARES_ETHER_NTOHOST
+/* define if you want to build the instrument functions code */
+#undef ENABLE_INSTRUMENT_FUNCTIONS
+
/* define if you want to build the possibly-buggy SMB printer */
#undef ENABLE_SMB
with_gcc
enable_universal
with_smi
+enable_instrument_functions
enable_smb
with_user
with_chroot
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--disable-universal don't build universal on macOS
+ --enable-instrument-functions
+ enable instrument functions code [default=no]
--enable-smb enable possibly-buggy SMB printer [default=no]
--disable-local-libpcap don't look for a local libpcap [default=check for a
local libpcap]
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the instrument functions code" >&5
+$as_echo_n "checking whether to enable the instrument functions code... " >&6; }
+# Check whether --enable-instrument-functions was given.
+if test "${enable_instrument_functions+set}" = set; then :
+ enableval=$enable_instrument_functions;
+else
+ enableval=no
+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
+
+ 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 -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; }
+ ;;
+esac
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the possibly-buggy SMB printer" >&5
$as_echo_n "checking whether to enable the possibly-buggy SMB printer... " >&6; }
# Check whether --enable-smb was given.
])
fi
+AC_MSG_CHECKING([whether to enable the instrument functions code])
+AC_ARG_ENABLE([instrument-functions],
+ [AS_HELP_STRING([--enable-instrument-functions],
+ [enable instrument functions code [default=no]])],
+ [],
+ [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 -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)
+ ;;
+esac
+
AC_MSG_CHECKING([whether to enable the possibly-buggy SMB printer])
AC_ARG_ENABLE([smb],
[AS_HELP_STRING([--enable-smb],
--- /dev/null
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <bfd.h>
+
+/*
+ * Generate instrumentation calls for entry and exit to functions.
+ * Just after function entry and just before function exit, the
+ * following profiling functions are called with the address of the
+ * current function and its call site (currently not use).
+ *
+ * The attribute 'no_instrument_function' causes this instrumentation is
+ * not done.
+ *
+ * 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.
+ *
+ * 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
+ */
+
+#define ND_NO_INSTRUMENT __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;
+
+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;
+
+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)
+{
+ print_debug(this_fn, call_site, ENTER);
+}
+
+void
+__cyg_profile_func_exit(void *this_fn, void *call_site)
+{
+ 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;
+ }
+
+ 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 : */
return printer;
}
+#ifdef ENABLE_INSTRUMENT_FUNCTIONS
+extern int profile_func_level;
+static int pretty_print_packet_level = -1;
+#endif
+
void
pretty_print_packet(netdissect_options *ndo, const struct pcap_pkthdr *h,
const u_char *sp, u_int packets_captured)
u_int hdrlen = 0;
int invalid_header = 0;
+#ifdef ENABLE_INSTRUMENT_FUNCTIONS
+ if (pretty_print_packet_level == -1)
+ pretty_print_packet_level = profile_func_level;
+#endif
+
if (ndo->ndo_packet_number)
ND_PRINT("%5u ", packets_captured);
nd_print_trunc(ndo);
/* Print the full packet */
ndo->ndo_ll_hdr_len = 0;
+#ifdef ENABLE_INSTRUMENT_FUNCTIONS
+ /* truncation => reassignment */
+ profile_func_level = pretty_print_packet_level;
+#endif
break;
}
hdrlen = ndo->ndo_ll_hdr_len;