xref: /freebsd/contrib/tcpdump/instrument-functions.c (revision 0a7e5f1f02aad2ff5fff1c60f44c6975fd07e1d9)
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