blob: ba0a56a5309ff572e6a94d24c2d3d7c7ec31ef74 [file] [log] [blame]
/*
* 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 : */