xref: /netbsd-src/external/bsd/tcpdump/dist/instrument-functions.c (revision c41df9f6167ea7cd2f761f0a97783c8267cb8847)
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