mirror of
https://github.com/the-tcpdump-group/tcpdump.git
synced 2024-11-23 18:14:29 +08:00
37ae1bef66
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]
251 lines
6.5 KiB
C
251 lines
6.5 KiB
C
/*
|
|
* 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 : */
|