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
__cyg_profile_func_enter(void * this_fn,void * call_site)59 __cyg_profile_func_enter(void *this_fn, void *call_site)
60 {
61 print_debug(this_fn, call_site, ENTER);
62 }
63
64 void
__cyg_profile_func_exit(void * this_fn,void * call_site)65 __cyg_profile_func_exit(void *this_fn, void *call_site)
66 {
67 print_debug(this_fn, call_site, EXIT);
68 }
69
print_debug(void * this_fn,void * call_site,action_type action)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