/* * 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 #include #include #include #include /* * 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; long i; 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(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; 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 { 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 : */