| /* |
| * 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 : */ |