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