xref: /freebsd/contrib/wpa/src/utils/trace.c (revision e28a4053b110e06768631ac8401ed4a3c05e68a5)
1*e28a4053SRui Paulo /*
2*e28a4053SRui Paulo  * Backtrace debugging
3*e28a4053SRui Paulo  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
4*e28a4053SRui Paulo  *
5*e28a4053SRui Paulo  * This program is free software; you can redistribute it and/or modify
6*e28a4053SRui Paulo  * it under the terms of the GNU General Public License version 2 as
7*e28a4053SRui Paulo  * published by the Free Software Foundation.
8*e28a4053SRui Paulo  *
9*e28a4053SRui Paulo  * Alternatively, this software may be distributed under the terms of BSD
10*e28a4053SRui Paulo  * license.
11*e28a4053SRui Paulo  *
12*e28a4053SRui Paulo  * See README and COPYING for more details.
13*e28a4053SRui Paulo  */
14*e28a4053SRui Paulo 
15*e28a4053SRui Paulo #include "includes.h"
16*e28a4053SRui Paulo 
17*e28a4053SRui Paulo #include "common.h"
18*e28a4053SRui Paulo #include "trace.h"
19*e28a4053SRui Paulo 
20*e28a4053SRui Paulo #ifdef WPA_TRACE
21*e28a4053SRui Paulo 
22*e28a4053SRui Paulo static struct dl_list active_references =
23*e28a4053SRui Paulo { &active_references, &active_references };
24*e28a4053SRui Paulo 
25*e28a4053SRui Paulo #ifdef WPA_TRACE_BFD
26*e28a4053SRui Paulo #include <bfd.h>
27*e28a4053SRui Paulo #ifdef __linux__
28*e28a4053SRui Paulo #include <demangle.h>
29*e28a4053SRui Paulo #else /* __linux__ */
30*e28a4053SRui Paulo #include <libiberty/demangle.h>
31*e28a4053SRui Paulo #endif /* __linux__ */
32*e28a4053SRui Paulo 
33*e28a4053SRui Paulo static char *prg_fname = NULL;
34*e28a4053SRui Paulo static bfd *cached_abfd = NULL;
35*e28a4053SRui Paulo static asymbol **syms = NULL;
36*e28a4053SRui Paulo 
37*e28a4053SRui Paulo static void get_prg_fname(void)
38*e28a4053SRui Paulo {
39*e28a4053SRui Paulo 	char exe[50], fname[512];
40*e28a4053SRui Paulo 	int len;
41*e28a4053SRui Paulo 	os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
42*e28a4053SRui Paulo 	len = readlink(exe, fname, sizeof(fname) - 1);
43*e28a4053SRui Paulo 	if (len < 0 || len >= (int) sizeof(fname)) {
44*e28a4053SRui Paulo 		perror("readlink");
45*e28a4053SRui Paulo 		return;
46*e28a4053SRui Paulo 	}
47*e28a4053SRui Paulo 	fname[len] = '\0';
48*e28a4053SRui Paulo 	prg_fname = strdup(fname);
49*e28a4053SRui Paulo }
50*e28a4053SRui Paulo 
51*e28a4053SRui Paulo 
52*e28a4053SRui Paulo static bfd * open_bfd(const char *fname)
53*e28a4053SRui Paulo {
54*e28a4053SRui Paulo 	bfd *abfd;
55*e28a4053SRui Paulo 	char **matching;
56*e28a4053SRui Paulo 
57*e28a4053SRui Paulo 	abfd = bfd_openr(prg_fname, NULL);
58*e28a4053SRui Paulo 	if (abfd == NULL) {
59*e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "bfd_openr failed");
60*e28a4053SRui Paulo 		return NULL;
61*e28a4053SRui Paulo 	}
62*e28a4053SRui Paulo 
63*e28a4053SRui Paulo 	if (bfd_check_format(abfd, bfd_archive)) {
64*e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "bfd_check_format failed");
65*e28a4053SRui Paulo 		bfd_close(abfd);
66*e28a4053SRui Paulo 		return NULL;
67*e28a4053SRui Paulo 	}
68*e28a4053SRui Paulo 
69*e28a4053SRui Paulo 	if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
70*e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
71*e28a4053SRui Paulo 		free(matching);
72*e28a4053SRui Paulo 		bfd_close(abfd);
73*e28a4053SRui Paulo 		return NULL;
74*e28a4053SRui Paulo 	}
75*e28a4053SRui Paulo 
76*e28a4053SRui Paulo 	return abfd;
77*e28a4053SRui Paulo }
78*e28a4053SRui Paulo 
79*e28a4053SRui Paulo 
80*e28a4053SRui Paulo static void read_syms(bfd *abfd)
81*e28a4053SRui Paulo {
82*e28a4053SRui Paulo 	long storage, symcount;
83*e28a4053SRui Paulo 	bfd_boolean dynamic = FALSE;
84*e28a4053SRui Paulo 
85*e28a4053SRui Paulo 	if (syms)
86*e28a4053SRui Paulo 		return;
87*e28a4053SRui Paulo 
88*e28a4053SRui Paulo 	if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
89*e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "No symbols");
90*e28a4053SRui Paulo 		return;
91*e28a4053SRui Paulo 	}
92*e28a4053SRui Paulo 
93*e28a4053SRui Paulo 	storage = bfd_get_symtab_upper_bound(abfd);
94*e28a4053SRui Paulo 	if (storage == 0) {
95*e28a4053SRui Paulo 		storage = bfd_get_dynamic_symtab_upper_bound(abfd);
96*e28a4053SRui Paulo 		dynamic = TRUE;
97*e28a4053SRui Paulo 	}
98*e28a4053SRui Paulo 	if (storage < 0) {
99*e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "Unknown symtab upper bound");
100*e28a4053SRui Paulo 		return;
101*e28a4053SRui Paulo 	}
102*e28a4053SRui Paulo 
103*e28a4053SRui Paulo 	syms = malloc(storage);
104*e28a4053SRui Paulo 	if (syms == NULL) {
105*e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
106*e28a4053SRui Paulo 			   "(%ld bytes)", storage);
107*e28a4053SRui Paulo 		return;
108*e28a4053SRui Paulo 	}
109*e28a4053SRui Paulo 	if (dynamic)
110*e28a4053SRui Paulo 		symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
111*e28a4053SRui Paulo 	else
112*e28a4053SRui Paulo 		symcount = bfd_canonicalize_symtab(abfd, syms);
113*e28a4053SRui Paulo 	if (symcount < 0) {
114*e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
115*e28a4053SRui Paulo 			   dynamic ? "dynamic " : "");
116*e28a4053SRui Paulo 		free(syms);
117*e28a4053SRui Paulo 		syms = NULL;
118*e28a4053SRui Paulo 		return;
119*e28a4053SRui Paulo 	}
120*e28a4053SRui Paulo }
121*e28a4053SRui Paulo 
122*e28a4053SRui Paulo 
123*e28a4053SRui Paulo struct bfd_data {
124*e28a4053SRui Paulo 	bfd_vma pc;
125*e28a4053SRui Paulo 	bfd_boolean found;
126*e28a4053SRui Paulo 	const char *filename;
127*e28a4053SRui Paulo 	const char *function;
128*e28a4053SRui Paulo 	unsigned int line;
129*e28a4053SRui Paulo };
130*e28a4053SRui Paulo 
131*e28a4053SRui Paulo 
132*e28a4053SRui Paulo static void find_addr_sect(bfd *abfd, asection *section, void *obj)
133*e28a4053SRui Paulo {
134*e28a4053SRui Paulo 	struct bfd_data *data = obj;
135*e28a4053SRui Paulo 	bfd_vma vma;
136*e28a4053SRui Paulo 	bfd_size_type size;
137*e28a4053SRui Paulo 
138*e28a4053SRui Paulo 	if (data->found)
139*e28a4053SRui Paulo 		return;
140*e28a4053SRui Paulo 
141*e28a4053SRui Paulo 	if (!(bfd_get_section_vma(abfd, section)))
142*e28a4053SRui Paulo 		return;
143*e28a4053SRui Paulo 
144*e28a4053SRui Paulo 	vma = bfd_get_section_vma(abfd, section);
145*e28a4053SRui Paulo 	if (data->pc < vma)
146*e28a4053SRui Paulo 		return;
147*e28a4053SRui Paulo 
148*e28a4053SRui Paulo 	size = bfd_get_section_size(section);
149*e28a4053SRui Paulo 	if (data->pc >= vma + size)
150*e28a4053SRui Paulo 		return;
151*e28a4053SRui Paulo 
152*e28a4053SRui Paulo 	data->found = bfd_find_nearest_line(abfd, section, syms,
153*e28a4053SRui Paulo 					    data->pc - vma,
154*e28a4053SRui Paulo 					    &data->filename,
155*e28a4053SRui Paulo 					    &data->function,
156*e28a4053SRui Paulo 					    &data->line);
157*e28a4053SRui Paulo }
158*e28a4053SRui Paulo 
159*e28a4053SRui Paulo 
160*e28a4053SRui Paulo static void wpa_trace_bfd_addr(void *pc)
161*e28a4053SRui Paulo {
162*e28a4053SRui Paulo 	bfd *abfd = cached_abfd;
163*e28a4053SRui Paulo 	struct bfd_data data;
164*e28a4053SRui Paulo 	const char *name;
165*e28a4053SRui Paulo 	char *aname = NULL;
166*e28a4053SRui Paulo 	const char *filename;
167*e28a4053SRui Paulo 
168*e28a4053SRui Paulo 	if (abfd == NULL)
169*e28a4053SRui Paulo 		return;
170*e28a4053SRui Paulo 
171*e28a4053SRui Paulo 	data.pc = (bfd_vma) pc;
172*e28a4053SRui Paulo 	data.found = FALSE;
173*e28a4053SRui Paulo 	bfd_map_over_sections(abfd, find_addr_sect, &data);
174*e28a4053SRui Paulo 
175*e28a4053SRui Paulo 	if (!data.found)
176*e28a4053SRui Paulo 		return;
177*e28a4053SRui Paulo 
178*e28a4053SRui Paulo 	do {
179*e28a4053SRui Paulo 		if (data.function)
180*e28a4053SRui Paulo 			aname = bfd_demangle(abfd, data.function,
181*e28a4053SRui Paulo 					     DMGL_ANSI | DMGL_PARAMS);
182*e28a4053SRui Paulo 		name = aname ? aname : data.function;
183*e28a4053SRui Paulo 		filename = data.filename;
184*e28a4053SRui Paulo 		if (filename) {
185*e28a4053SRui Paulo 			char *end = os_strrchr(filename, '/');
186*e28a4053SRui Paulo 			int i = 0;
187*e28a4053SRui Paulo 			while (*filename && *filename == prg_fname[i] &&
188*e28a4053SRui Paulo 			       filename <= end) {
189*e28a4053SRui Paulo 				filename++;
190*e28a4053SRui Paulo 				i++;
191*e28a4053SRui Paulo 			}
192*e28a4053SRui Paulo 		}
193*e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "     %s() %s:%u",
194*e28a4053SRui Paulo 			   name, filename, data.line);
195*e28a4053SRui Paulo 		free(aname);
196*e28a4053SRui Paulo 
197*e28a4053SRui Paulo 		data.found = bfd_find_inliner_info(abfd, &data.filename,
198*e28a4053SRui Paulo 						   &data.function, &data.line);
199*e28a4053SRui Paulo 	} while (data.found);
200*e28a4053SRui Paulo }
201*e28a4053SRui Paulo 
202*e28a4053SRui Paulo 
203*e28a4053SRui Paulo static const char * wpa_trace_bfd_addr2func(void *pc)
204*e28a4053SRui Paulo {
205*e28a4053SRui Paulo 	bfd *abfd = cached_abfd;
206*e28a4053SRui Paulo 	struct bfd_data data;
207*e28a4053SRui Paulo 
208*e28a4053SRui Paulo 	if (abfd == NULL)
209*e28a4053SRui Paulo 		return NULL;
210*e28a4053SRui Paulo 
211*e28a4053SRui Paulo 	data.pc = (bfd_vma) pc;
212*e28a4053SRui Paulo 	data.found = FALSE;
213*e28a4053SRui Paulo 	bfd_map_over_sections(abfd, find_addr_sect, &data);
214*e28a4053SRui Paulo 
215*e28a4053SRui Paulo 	if (!data.found)
216*e28a4053SRui Paulo 		return NULL;
217*e28a4053SRui Paulo 
218*e28a4053SRui Paulo 	return data.function;
219*e28a4053SRui Paulo }
220*e28a4053SRui Paulo 
221*e28a4053SRui Paulo 
222*e28a4053SRui Paulo static void wpa_trace_bfd_init(void)
223*e28a4053SRui Paulo {
224*e28a4053SRui Paulo 	if (!prg_fname) {
225*e28a4053SRui Paulo 		get_prg_fname();
226*e28a4053SRui Paulo 		if (!prg_fname)
227*e28a4053SRui Paulo 			return;
228*e28a4053SRui Paulo 	}
229*e28a4053SRui Paulo 
230*e28a4053SRui Paulo 	if (!cached_abfd) {
231*e28a4053SRui Paulo 		cached_abfd = open_bfd(prg_fname);
232*e28a4053SRui Paulo 		if (!cached_abfd) {
233*e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "Failed to open bfd");
234*e28a4053SRui Paulo 			return;
235*e28a4053SRui Paulo 		}
236*e28a4053SRui Paulo 	}
237*e28a4053SRui Paulo 
238*e28a4053SRui Paulo 	read_syms(cached_abfd);
239*e28a4053SRui Paulo 	if (!syms) {
240*e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "Failed to read symbols");
241*e28a4053SRui Paulo 		return;
242*e28a4053SRui Paulo 	}
243*e28a4053SRui Paulo }
244*e28a4053SRui Paulo 
245*e28a4053SRui Paulo 
246*e28a4053SRui Paulo void wpa_trace_dump_funcname(const char *title, void *pc)
247*e28a4053SRui Paulo {
248*e28a4053SRui Paulo 	wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
249*e28a4053SRui Paulo 	wpa_trace_bfd_init();
250*e28a4053SRui Paulo 	wpa_trace_bfd_addr(pc);
251*e28a4053SRui Paulo }
252*e28a4053SRui Paulo 
253*e28a4053SRui Paulo #else /* WPA_TRACE_BFD */
254*e28a4053SRui Paulo 
255*e28a4053SRui Paulo #define wpa_trace_bfd_init() do { } while (0)
256*e28a4053SRui Paulo #define wpa_trace_bfd_addr(pc) do { } while (0)
257*e28a4053SRui Paulo #define wpa_trace_bfd_addr2func(pc) NULL
258*e28a4053SRui Paulo 
259*e28a4053SRui Paulo #endif /* WPA_TRACE_BFD */
260*e28a4053SRui Paulo 
261*e28a4053SRui Paulo void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
262*e28a4053SRui Paulo {
263*e28a4053SRui Paulo 	char **sym;
264*e28a4053SRui Paulo 	int i;
265*e28a4053SRui Paulo 	enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
266*e28a4053SRui Paulo 
267*e28a4053SRui Paulo 	wpa_trace_bfd_init();
268*e28a4053SRui Paulo 	wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
269*e28a4053SRui Paulo 	sym = backtrace_symbols(btrace, btrace_num);
270*e28a4053SRui Paulo 	state = TRACE_HEAD;
271*e28a4053SRui Paulo 	for (i = 0; i < btrace_num; i++) {
272*e28a4053SRui Paulo 		const char *func = wpa_trace_bfd_addr2func(btrace[i]);
273*e28a4053SRui Paulo 		if (state == TRACE_HEAD && func &&
274*e28a4053SRui Paulo 		    (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
275*e28a4053SRui Paulo 		     os_strcmp(func, "wpa_trace_check_ref") == 0 ||
276*e28a4053SRui Paulo 		     os_strcmp(func, "wpa_trace_show") == 0))
277*e28a4053SRui Paulo 			continue;
278*e28a4053SRui Paulo 		if (state == TRACE_TAIL && sym && sym[i] &&
279*e28a4053SRui Paulo 		    os_strstr(sym[i], "__libc_start_main"))
280*e28a4053SRui Paulo 			break;
281*e28a4053SRui Paulo 		if (state == TRACE_HEAD)
282*e28a4053SRui Paulo 			state = TRACE_RELEVANT;
283*e28a4053SRui Paulo 		if (sym)
284*e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
285*e28a4053SRui Paulo 		else
286*e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
287*e28a4053SRui Paulo 		wpa_trace_bfd_addr(btrace[i]);
288*e28a4053SRui Paulo 		if (state == TRACE_RELEVANT && func &&
289*e28a4053SRui Paulo 		    os_strcmp(func, "main") == 0)
290*e28a4053SRui Paulo 			state = TRACE_TAIL;
291*e28a4053SRui Paulo 	}
292*e28a4053SRui Paulo 	free(sym);
293*e28a4053SRui Paulo 	wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
294*e28a4053SRui Paulo }
295*e28a4053SRui Paulo 
296*e28a4053SRui Paulo 
297*e28a4053SRui Paulo void wpa_trace_show(const char *title)
298*e28a4053SRui Paulo {
299*e28a4053SRui Paulo 	struct info {
300*e28a4053SRui Paulo 		WPA_TRACE_INFO
301*e28a4053SRui Paulo 	} info;
302*e28a4053SRui Paulo 	wpa_trace_record(&info);
303*e28a4053SRui Paulo 	wpa_trace_dump(title, &info);
304*e28a4053SRui Paulo }
305*e28a4053SRui Paulo 
306*e28a4053SRui Paulo 
307*e28a4053SRui Paulo void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
308*e28a4053SRui Paulo {
309*e28a4053SRui Paulo 	if (addr == NULL)
310*e28a4053SRui Paulo 		return;
311*e28a4053SRui Paulo 	ref->addr = addr;
312*e28a4053SRui Paulo 	wpa_trace_record(ref);
313*e28a4053SRui Paulo 	dl_list_add(&active_references, &ref->list);
314*e28a4053SRui Paulo }
315*e28a4053SRui Paulo 
316*e28a4053SRui Paulo 
317*e28a4053SRui Paulo void wpa_trace_check_ref(const void *addr)
318*e28a4053SRui Paulo {
319*e28a4053SRui Paulo 	struct wpa_trace_ref *ref;
320*e28a4053SRui Paulo 	dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
321*e28a4053SRui Paulo 		if (addr != ref->addr)
322*e28a4053SRui Paulo 			continue;
323*e28a4053SRui Paulo 		wpa_trace_show("Freeing referenced memory");
324*e28a4053SRui Paulo 		wpa_trace_dump("Reference registration", ref);
325*e28a4053SRui Paulo 		abort();
326*e28a4053SRui Paulo 	}
327*e28a4053SRui Paulo }
328*e28a4053SRui Paulo 
329*e28a4053SRui Paulo #endif /* WPA_TRACE */
330