]> The Tcpdump Group git mirrors - tcpslice/blob - instrument-functions.c
instrument functions: Add a length check
[tcpslice] / instrument-functions.c
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 <sys/types.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include <bfd.h>
21
22 /*
23 * Generate instrumentation calls for entry and exit to functions.
24 * Just after function entry and just before function exit, the
25 * following profiling functions are called with the address of the
26 * current function and its call site (currently not use).
27 *
28 * The attribute 'no_instrument_function' causes this instrumentation is
29 * not done.
30 *
31 * These profiling functions call print_debug(). This function prints the
32 * current function name with indentation and call level.
33 * If entering in a function it prints also the calling function name with
34 * file name and line number.
35 *
36 * To configure the printing of only the global functions names:
37 * $ make instrument_global
38 *
39 * To go back to print all the functions names:
40 * $ make instrument_all
41 *
42 * To print nothing, like with no instrumentation:
43 * $ make instrument_off
44 */
45
46 #define ND_NO_INSTRUMENT __attribute__((no_instrument_function))
47
48 /* Store the function call level, used also in pretty_print_packet() */
49 extern int profile_func_level;
50 int profile_func_level = -1;
51
52 typedef enum {
53 ENTER,
54 EXIT
55 } action_type;
56
57 void __cyg_profile_func_enter(void *this_fn, void *call_site) ND_NO_INSTRUMENT;
58
59 void __cyg_profile_func_exit(void *this_fn, void *call_site) ND_NO_INSTRUMENT;
60
61 static void print_debug(void *this_fn, void *call_site, action_type action)
62 ND_NO_INSTRUMENT;
63
64 void
65 __cyg_profile_func_enter(void *this_fn, void *call_site)
66 {
67 print_debug(this_fn, call_site, ENTER);
68 }
69
70 void
71 __cyg_profile_func_exit(void *this_fn, void *call_site)
72 {
73 print_debug(this_fn, call_site, EXIT);
74 }
75
76 /* If this file exists, print only the global functions */
77 #define ND_FILE_FLAG_GLOBAL "instrument_functions_global.devel"
78
79 /* If this file exists, print nothing, like with no instrumentation */
80 #define ND_FILE_FLAG_OFF "instrument_functions_off.devel"
81
82 static void print_debug(void *this_fn, void *call_site, action_type action)
83 {
84 static bfd* abfd;
85 static asymbol **symtab;
86 static long symcount;
87 static asection *text;
88 static bfd_vma vma;
89 static int instrument_off;
90 static int print_only_global;
91 symbol_info syminfo;
92 struct stat statbuf;
93 int i;
94
95 if (!instrument_off) {
96 /* one-time test */
97 if (!stat(ND_FILE_FLAG_OFF, &statbuf)) {
98 instrument_off = 1;
99 return;
100 }
101 } else
102 return;
103
104 /* If no errors, this block should be executed one time */
105 if (!abfd) {
106 char pgm_name[1024];
107 long symsize;
108
109 if (!stat(ND_FILE_FLAG_GLOBAL, &statbuf))
110 print_only_global = 1;
111
112 ssize_t ret = readlink("/proc/self/exe", pgm_name, sizeof(pgm_name));
113 if (ret == -1) {
114 perror("failed to find executable\n");
115 return;
116 }
117 if (ret == sizeof(pgm_name)) {
118 /* no space for the '\0' */
119 printf("truncation may have occurred\n");
120 return;
121 }
122 pgm_name[ret] = '\0';
123
124 bfd_init();
125
126 abfd = bfd_openr(pgm_name, NULL);
127 if (!abfd) {
128 bfd_perror("bfd_openr");
129 return;
130 }
131
132 if (!bfd_check_format(abfd, bfd_object)) {
133 bfd_perror("bfd_check_format");
134 return;
135 }
136
137 if((symsize = bfd_get_symtab_upper_bound(abfd)) == -1) {
138 bfd_perror("bfd_get_symtab_upper_bound");
139 return;
140 }
141
142 symtab = (asymbol **)malloc(symsize);
143 symcount = bfd_canonicalize_symtab(abfd, symtab);
144 if (symcount < 0) {
145 free (symtab);
146 bfd_perror ("bfd_canonicalize_symtab");
147 return;
148 }
149
150 if ((text = bfd_get_section_by_name(abfd, ".text")) == NULL) {
151 bfd_perror("bfd_get_section_by_name");
152 return;
153 }
154 vma = text->vma;
155 }
156
157 if (print_only_global) {
158 int found;
159
160 i = 0;
161 found = 0;
162 while (i < symcount && !found) {
163 bfd_get_symbol_info(abfd, symtab[i], &syminfo);
164 if ((void *)syminfo.value == this_fn) {
165 found = 1;
166 }
167 i++;
168 }
169 /* type == 'T' for a global function */
170 if (found == 1 && syminfo.type != 'T')
171 return;
172 }
173
174 /* Current function */
175 if ((bfd_vma)this_fn < vma) {
176 printf("[ERROR address this_fn]");
177 } else {
178 const char *file;
179 const char *func;
180 unsigned int line;
181
182 if (!bfd_find_nearest_line(abfd, text, symtab, (bfd_vma)this_fn - vma,
183 &file, &func, &line)) {
184 printf("[ERROR bfd_find_nearest_line this_fn]");
185 } else {
186 if (action == ENTER)
187 profile_func_level += 1;
188 /* Indentation */
189 for (i = 0 ; i < profile_func_level ; i++)
190 putchar(' ');
191 if (action == ENTER)
192 printf("[>> ");
193 else
194 printf("[<< ");
195 /* Function name */
196 if (func == NULL || *func == '\0')
197 printf("???");
198 else
199 printf("%s", func);
200 printf(" (%d)", profile_func_level);
201 /* Print the "from" part except for the main function) */
202 if (action == ENTER && strncmp(func, "main", sizeof("main"))) {
203 /* Calling function */
204 if ((bfd_vma)call_site < vma) {
205 printf("[ERROR address call_site]");
206 } else {
207 if (!bfd_find_nearest_line(abfd, text, symtab,
208 (bfd_vma)call_site - vma, &file,
209 &func, &line)) {
210 printf("[ERROR bfd_find_nearest_line call_site]");
211 } else {
212 printf(" from ");
213 /* Function name */
214 if (func == NULL || *func == '\0')
215 printf("???");
216 else
217 printf("%s", func);
218 /* File name */
219 if (file == NULL || *file == '\0')
220 printf(" ??:");
221 else {
222 char *slashp = strrchr(file, '/');
223 if (slashp != NULL)
224 file = slashp + 1;
225 printf(" %s:", file);
226 }
227 /* Line number */
228 if (line == 0)
229 printf("?");
230 else
231 printf("%u", line);
232 printf("]");
233 }
234 }
235 }
236 putchar('\n');
237 if (action == EXIT)
238 profile_func_level -= 1;
239 }
240 }
241 fflush(stdout);
242 }
243
244 /* vi: set tabstop=4 softtabstop=0 shiftwidth=4 smarttab autoindent : */