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