1 /* 2 * Redistribution and use in source and binary forms, with or without 3 * modification, are permitted provided that: (1) source code 4 * distributions retain the above copyright notice and this paragraph 5 * in its entirety, and (2) distributions including binary code include 6 * the above copyright notice and this paragraph in its entirety in 7 * the documentation or other materials provided with the distribution. 8 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND 9 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT 10 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 11 * FOR A PARTICULAR PURPOSE. 12 */ 13 14 #include <stdio.h> 15 #include <string.h> 16 #include <stdlib.h> 17 #include <unistd.h> 18 #include <bfd.h> 19 20 /* 21 * Generate instrumentation calls for entry and exit to functions. 22 * Just after function entry and just before function exit, the 23 * following profiling functions are called with the address of the 24 * current function and its call site (currently not use). 25 * 26 * The attribute 'no_instrument_function' causes this instrumentation is 27 * not done. 28 * 29 * These profiling functions call print_debug(). This function prints the 30 * current function name with indentation and call level. 31 * If entering in a function it prints also the calling function name with 32 * file name and line number. 33 * 34 * If the environment variable INSTRUMENT is 35 * unset or set to an empty string, print nothing, like with no instrumentation 36 * set to "all" or "a", print all the functions names 37 * set to "global" or "g", print only the global functions names 38 */ 39 40 #define ND_NO_INSTRUMENT __attribute__((no_instrument_function)) 41 42 /* Store the function call level, used also in pretty_print_packet() */ 43 extern int profile_func_level; 44 int profile_func_level = -1; 45 46 typedef enum { 47 ENTER, 48 EXIT 49 } action_type; 50 51 void __cyg_profile_func_enter(void *this_fn, void *call_site) ND_NO_INSTRUMENT; 52 53 void __cyg_profile_func_exit(void *this_fn, void *call_site) ND_NO_INSTRUMENT; 54 55 static void print_debug(void *this_fn, void *call_site, action_type action) 56 ND_NO_INSTRUMENT; 57 58 void 59 __cyg_profile_func_enter(void *this_fn, void *call_site) 60 { 61 print_debug(this_fn, call_site, ENTER); 62 } 63 64 void 65 __cyg_profile_func_exit(void *this_fn, void *call_site) 66 { 67 print_debug(this_fn, call_site, EXIT); 68 } 69 70 static void print_debug(void *this_fn, void *call_site, action_type action) 71 { 72 static bfd* abfd; 73 static asymbol **symtab; 74 static long symcount; 75 static asection *text; 76 static bfd_vma vma; 77 static int instrument_set; 78 static int instrument_off; 79 static int instrument_global; 80 81 if (!instrument_set) { 82 static char *instrument_type; 83 84 /* Get the configuration environment variable INSTRUMENT value if any */ 85 instrument_type = getenv("INSTRUMENT"); 86 /* unset or set to an empty string ? */ 87 if (instrument_type == NULL || 88 !strncmp(instrument_type, "", sizeof(""))) { 89 instrument_off = 1; 90 } else { 91 /* set to "global" or "g" ? */ 92 if (!strncmp(instrument_type, "global", sizeof("global")) || 93 !strncmp(instrument_type, "g", sizeof("g"))) 94 instrument_global = 1; 95 else if (strncmp(instrument_type, "all", sizeof("all")) && 96 strncmp(instrument_type, "a", sizeof("a"))) { 97 fprintf(stderr, "INSTRUMENT can be only \"\", \"all\", \"a\", " 98 "\"global\" or \"g\".\n"); 99 exit(1); 100 } 101 } 102 instrument_set = 1; 103 } 104 105 if (instrument_off) 106 return; 107 108 /* If no errors, this block should be executed one time */ 109 if (!abfd) { 110 char pgm_name[1024]; 111 long symsize; 112 113 ssize_t ret = readlink("/proc/self/exe", pgm_name, sizeof(pgm_name)); 114 if (ret == -1) { 115 perror("failed to find executable"); 116 return; 117 } 118 if (ret == sizeof(pgm_name)) { 119 /* no space for the '\0' */ 120 printf("truncation may have occurred\n"); 121 return; 122 } 123 pgm_name[ret] = '\0'; 124 125 bfd_init(); 126 127 abfd = bfd_openr(pgm_name, NULL); 128 if (!abfd) { 129 bfd_perror("bfd_openr"); 130 return; 131 } 132 133 if (!bfd_check_format(abfd, bfd_object)) { 134 bfd_perror("bfd_check_format"); 135 return; 136 } 137 138 if((symsize = bfd_get_symtab_upper_bound(abfd)) == -1) { 139 bfd_perror("bfd_get_symtab_upper_bound"); 140 return; 141 } 142 143 symtab = (asymbol **)malloc((size_t)symsize); 144 symcount = bfd_canonicalize_symtab(abfd, symtab); 145 if (symcount < 0) { 146 free(symtab); 147 bfd_perror("bfd_canonicalize_symtab"); 148 return; 149 } 150 151 if ((text = bfd_get_section_by_name(abfd, ".text")) == NULL) { 152 bfd_perror("bfd_get_section_by_name"); 153 return; 154 } 155 vma = text->vma; 156 } 157 158 if (instrument_global) { 159 symbol_info syminfo; 160 int found; 161 long i; 162 163 i = 0; 164 found = 0; 165 while (i < symcount && !found) { 166 bfd_get_symbol_info(abfd, symtab[i], &syminfo); 167 if ((void *)syminfo.value == this_fn) { 168 found = 1; 169 } 170 i++; 171 } 172 /* type == 'T' for a global function */ 173 if (found == 1 && syminfo.type != 'T') 174 return; 175 } 176 177 /* Current function */ 178 if ((bfd_vma)this_fn < vma) { 179 printf("[ERROR address this_fn]"); 180 } else { 181 const char *file; 182 const char *func; 183 unsigned int line; 184 185 if (!bfd_find_nearest_line(abfd, text, symtab, (bfd_vma)this_fn - vma, 186 &file, &func, &line)) { 187 printf("[ERROR bfd_find_nearest_line this_fn]"); 188 } else { 189 int i; 190 191 if (action == ENTER) 192 profile_func_level += 1; 193 /* Indentation */ 194 for (i = 0 ; i < profile_func_level ; i++) 195 putchar(' '); 196 if (action == ENTER) 197 printf("[>> "); 198 else 199 printf("[<< "); 200 /* Function name */ 201 if (func == NULL || *func == '\0') 202 printf("???"); 203 else 204 printf("%s", func); 205 printf(" (%d)", profile_func_level); 206 /* Print the "from" part except for the main function) */ 207 if (action == ENTER && func != NULL && 208 strncmp(func, "main", sizeof("main"))) { 209 /* Calling function */ 210 if ((bfd_vma)call_site < vma) { 211 printf("[ERROR address call_site]"); 212 } else { 213 if (!bfd_find_nearest_line(abfd, text, symtab, 214 (bfd_vma)call_site - vma, &file, 215 &func, &line)) { 216 printf("[ERROR bfd_find_nearest_line call_site]"); 217 } else { 218 printf(" from "); 219 /* Function name */ 220 if (func == NULL || *func == '\0') 221 printf("???"); 222 else 223 printf("%s", func); 224 /* File name */ 225 if (file == NULL || *file == '\0') 226 printf(" ??:"); 227 else { 228 char *slashp = strrchr(file, '/'); 229 if (slashp != NULL) 230 file = slashp + 1; 231 printf(" %s:", file); 232 } 233 /* Line number */ 234 if (line == 0) 235 printf("?"); 236 else 237 printf("%u", line); 238 printf("]"); 239 } 240 } 241 } 242 putchar('\n'); 243 if (action == EXIT) 244 profile_func_level -= 1; 245 } 246 } 247 fflush(stdout); 248 } 249 250 /* vi: set tabstop=4 softtabstop=0 shiftwidth=4 smarttab autoindent : */ 251