xref: /freebsd/contrib/wpa/src/utils/trace.c (revision 780fb4a2fa9a9aee5ac48a60b790f567c0dc13e9)
1e28a4053SRui Paulo /*
2e28a4053SRui Paulo  * Backtrace debugging
3e28a4053SRui Paulo  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7e28a4053SRui Paulo  */
8e28a4053SRui Paulo 
9e28a4053SRui Paulo #include "includes.h"
10e28a4053SRui Paulo 
11e28a4053SRui Paulo #include "common.h"
12e28a4053SRui Paulo #include "trace.h"
13e28a4053SRui Paulo 
14e28a4053SRui Paulo #ifdef WPA_TRACE
15e28a4053SRui Paulo 
16e28a4053SRui Paulo static struct dl_list active_references =
17e28a4053SRui Paulo { &active_references, &active_references };
18e28a4053SRui Paulo 
19e28a4053SRui Paulo #ifdef WPA_TRACE_BFD
20e28a4053SRui Paulo #include <bfd.h>
215b9c547cSRui Paulo 
225b9c547cSRui Paulo #define DMGL_PARAMS      (1 << 0)
235b9c547cSRui Paulo #define DMGL_ANSI        (1 << 1)
24e28a4053SRui Paulo 
25e28a4053SRui Paulo static char *prg_fname = NULL;
26e28a4053SRui Paulo static bfd *cached_abfd = NULL;
27e28a4053SRui Paulo static asymbol **syms = NULL;
28e28a4053SRui Paulo 
29e28a4053SRui Paulo static void get_prg_fname(void)
30e28a4053SRui Paulo {
31e28a4053SRui Paulo 	char exe[50], fname[512];
32e28a4053SRui Paulo 	int len;
33e28a4053SRui Paulo 	os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
34e28a4053SRui Paulo 	len = readlink(exe, fname, sizeof(fname) - 1);
35e28a4053SRui Paulo 	if (len < 0 || len >= (int) sizeof(fname)) {
365b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno));
37e28a4053SRui Paulo 		return;
38e28a4053SRui Paulo 	}
39e28a4053SRui Paulo 	fname[len] = '\0';
40e28a4053SRui Paulo 	prg_fname = strdup(fname);
41e28a4053SRui Paulo }
42e28a4053SRui Paulo 
43e28a4053SRui Paulo 
44e28a4053SRui Paulo static bfd * open_bfd(const char *fname)
45e28a4053SRui Paulo {
46e28a4053SRui Paulo 	bfd *abfd;
47e28a4053SRui Paulo 	char **matching;
48e28a4053SRui Paulo 
49e28a4053SRui Paulo 	abfd = bfd_openr(prg_fname, NULL);
50e28a4053SRui Paulo 	if (abfd == NULL) {
51e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "bfd_openr failed");
52e28a4053SRui Paulo 		return NULL;
53e28a4053SRui Paulo 	}
54e28a4053SRui Paulo 
55e28a4053SRui Paulo 	if (bfd_check_format(abfd, bfd_archive)) {
56e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "bfd_check_format failed");
57e28a4053SRui Paulo 		bfd_close(abfd);
58e28a4053SRui Paulo 		return NULL;
59e28a4053SRui Paulo 	}
60e28a4053SRui Paulo 
61e28a4053SRui Paulo 	if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
62e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
63e28a4053SRui Paulo 		free(matching);
64e28a4053SRui Paulo 		bfd_close(abfd);
65e28a4053SRui Paulo 		return NULL;
66e28a4053SRui Paulo 	}
67e28a4053SRui Paulo 
68e28a4053SRui Paulo 	return abfd;
69e28a4053SRui Paulo }
70e28a4053SRui Paulo 
71e28a4053SRui Paulo 
72e28a4053SRui Paulo static void read_syms(bfd *abfd)
73e28a4053SRui Paulo {
74e28a4053SRui Paulo 	long storage, symcount;
75e28a4053SRui Paulo 	bfd_boolean dynamic = FALSE;
76e28a4053SRui Paulo 
77e28a4053SRui Paulo 	if (syms)
78e28a4053SRui Paulo 		return;
79e28a4053SRui Paulo 
80e28a4053SRui Paulo 	if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
81e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "No symbols");
82e28a4053SRui Paulo 		return;
83e28a4053SRui Paulo 	}
84e28a4053SRui Paulo 
85e28a4053SRui Paulo 	storage = bfd_get_symtab_upper_bound(abfd);
86e28a4053SRui Paulo 	if (storage == 0) {
87e28a4053SRui Paulo 		storage = bfd_get_dynamic_symtab_upper_bound(abfd);
88e28a4053SRui Paulo 		dynamic = TRUE;
89e28a4053SRui Paulo 	}
90e28a4053SRui Paulo 	if (storage < 0) {
91e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "Unknown symtab upper bound");
92e28a4053SRui Paulo 		return;
93e28a4053SRui Paulo 	}
94e28a4053SRui Paulo 
95e28a4053SRui Paulo 	syms = malloc(storage);
96e28a4053SRui Paulo 	if (syms == NULL) {
97e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
98e28a4053SRui Paulo 			   "(%ld bytes)", storage);
99e28a4053SRui Paulo 		return;
100e28a4053SRui Paulo 	}
101e28a4053SRui Paulo 	if (dynamic)
102e28a4053SRui Paulo 		symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
103e28a4053SRui Paulo 	else
104e28a4053SRui Paulo 		symcount = bfd_canonicalize_symtab(abfd, syms);
105e28a4053SRui Paulo 	if (symcount < 0) {
106e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
107e28a4053SRui Paulo 			   dynamic ? "dynamic " : "");
108e28a4053SRui Paulo 		free(syms);
109e28a4053SRui Paulo 		syms = NULL;
110e28a4053SRui Paulo 		return;
111e28a4053SRui Paulo 	}
112e28a4053SRui Paulo }
113e28a4053SRui Paulo 
114e28a4053SRui Paulo 
115e28a4053SRui Paulo struct bfd_data {
116e28a4053SRui Paulo 	bfd_vma pc;
117e28a4053SRui Paulo 	bfd_boolean found;
118e28a4053SRui Paulo 	const char *filename;
119e28a4053SRui Paulo 	const char *function;
120e28a4053SRui Paulo 	unsigned int line;
121e28a4053SRui Paulo };
122e28a4053SRui Paulo 
123e28a4053SRui Paulo 
124e28a4053SRui Paulo static void find_addr_sect(bfd *abfd, asection *section, void *obj)
125e28a4053SRui Paulo {
126e28a4053SRui Paulo 	struct bfd_data *data = obj;
127e28a4053SRui Paulo 	bfd_vma vma;
128e28a4053SRui Paulo 	bfd_size_type size;
129e28a4053SRui Paulo 
130e28a4053SRui Paulo 	if (data->found)
131e28a4053SRui Paulo 		return;
132e28a4053SRui Paulo 
133e28a4053SRui Paulo 	if (!(bfd_get_section_vma(abfd, section)))
134e28a4053SRui Paulo 		return;
135e28a4053SRui Paulo 
136e28a4053SRui Paulo 	vma = bfd_get_section_vma(abfd, section);
137e28a4053SRui Paulo 	if (data->pc < vma)
138e28a4053SRui Paulo 		return;
139e28a4053SRui Paulo 
140e28a4053SRui Paulo 	size = bfd_get_section_size(section);
141e28a4053SRui Paulo 	if (data->pc >= vma + size)
142e28a4053SRui Paulo 		return;
143e28a4053SRui Paulo 
144e28a4053SRui Paulo 	data->found = bfd_find_nearest_line(abfd, section, syms,
145e28a4053SRui Paulo 					    data->pc - vma,
146e28a4053SRui Paulo 					    &data->filename,
147e28a4053SRui Paulo 					    &data->function,
148e28a4053SRui Paulo 					    &data->line);
149e28a4053SRui Paulo }
150e28a4053SRui Paulo 
151e28a4053SRui Paulo 
152e28a4053SRui Paulo static void wpa_trace_bfd_addr(void *pc)
153e28a4053SRui Paulo {
154e28a4053SRui Paulo 	bfd *abfd = cached_abfd;
155e28a4053SRui Paulo 	struct bfd_data data;
156e28a4053SRui Paulo 	const char *name;
157e28a4053SRui Paulo 	char *aname = NULL;
158e28a4053SRui Paulo 	const char *filename;
159e28a4053SRui Paulo 
160e28a4053SRui Paulo 	if (abfd == NULL)
161e28a4053SRui Paulo 		return;
162e28a4053SRui Paulo 
1635b9c547cSRui Paulo 	data.pc = (bfd_hostptr_t) pc;
164e28a4053SRui Paulo 	data.found = FALSE;
165e28a4053SRui Paulo 	bfd_map_over_sections(abfd, find_addr_sect, &data);
166e28a4053SRui Paulo 
167e28a4053SRui Paulo 	if (!data.found)
168e28a4053SRui Paulo 		return;
169e28a4053SRui Paulo 
170e28a4053SRui Paulo 	do {
171e28a4053SRui Paulo 		if (data.function)
172e28a4053SRui Paulo 			aname = bfd_demangle(abfd, data.function,
173e28a4053SRui Paulo 					     DMGL_ANSI | DMGL_PARAMS);
174e28a4053SRui Paulo 		name = aname ? aname : data.function;
175e28a4053SRui Paulo 		filename = data.filename;
176e28a4053SRui Paulo 		if (filename) {
177e28a4053SRui Paulo 			char *end = os_strrchr(filename, '/');
178e28a4053SRui Paulo 			int i = 0;
179e28a4053SRui Paulo 			while (*filename && *filename == prg_fname[i] &&
180e28a4053SRui Paulo 			       filename <= end) {
181e28a4053SRui Paulo 				filename++;
182e28a4053SRui Paulo 				i++;
183e28a4053SRui Paulo 			}
184e28a4053SRui Paulo 		}
185e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "     %s() %s:%u",
186e28a4053SRui Paulo 			   name, filename, data.line);
187e28a4053SRui Paulo 		free(aname);
1885b9c547cSRui Paulo 		aname = NULL;
189e28a4053SRui Paulo 
190e28a4053SRui Paulo 		data.found = bfd_find_inliner_info(abfd, &data.filename,
191e28a4053SRui Paulo 						   &data.function, &data.line);
192e28a4053SRui Paulo 	} while (data.found);
193e28a4053SRui Paulo }
194e28a4053SRui Paulo 
195e28a4053SRui Paulo 
196e28a4053SRui Paulo static const char * wpa_trace_bfd_addr2func(void *pc)
197e28a4053SRui Paulo {
198e28a4053SRui Paulo 	bfd *abfd = cached_abfd;
199e28a4053SRui Paulo 	struct bfd_data data;
200e28a4053SRui Paulo 
201e28a4053SRui Paulo 	if (abfd == NULL)
202e28a4053SRui Paulo 		return NULL;
203e28a4053SRui Paulo 
2045b9c547cSRui Paulo 	data.pc = (bfd_hostptr_t) pc;
205e28a4053SRui Paulo 	data.found = FALSE;
206e28a4053SRui Paulo 	bfd_map_over_sections(abfd, find_addr_sect, &data);
207e28a4053SRui Paulo 
208e28a4053SRui Paulo 	if (!data.found)
209e28a4053SRui Paulo 		return NULL;
210e28a4053SRui Paulo 
211e28a4053SRui Paulo 	return data.function;
212e28a4053SRui Paulo }
213e28a4053SRui Paulo 
214e28a4053SRui Paulo 
215e28a4053SRui Paulo static void wpa_trace_bfd_init(void)
216e28a4053SRui Paulo {
217e28a4053SRui Paulo 	if (!prg_fname) {
218e28a4053SRui Paulo 		get_prg_fname();
219e28a4053SRui Paulo 		if (!prg_fname)
220e28a4053SRui Paulo 			return;
221e28a4053SRui Paulo 	}
222e28a4053SRui Paulo 
223e28a4053SRui Paulo 	if (!cached_abfd) {
224e28a4053SRui Paulo 		cached_abfd = open_bfd(prg_fname);
225e28a4053SRui Paulo 		if (!cached_abfd) {
226e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "Failed to open bfd");
227e28a4053SRui Paulo 			return;
228e28a4053SRui Paulo 		}
229e28a4053SRui Paulo 	}
230e28a4053SRui Paulo 
231e28a4053SRui Paulo 	read_syms(cached_abfd);
232e28a4053SRui Paulo 	if (!syms) {
233e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "Failed to read symbols");
234e28a4053SRui Paulo 		return;
235e28a4053SRui Paulo 	}
236e28a4053SRui Paulo }
237e28a4053SRui Paulo 
238e28a4053SRui Paulo 
239e28a4053SRui Paulo void wpa_trace_dump_funcname(const char *title, void *pc)
240e28a4053SRui Paulo {
241e28a4053SRui Paulo 	wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
242e28a4053SRui Paulo 	wpa_trace_bfd_init();
243e28a4053SRui Paulo 	wpa_trace_bfd_addr(pc);
244e28a4053SRui Paulo }
245e28a4053SRui Paulo 
2465b9c547cSRui Paulo 
2475b9c547cSRui Paulo size_t wpa_trace_calling_func(const char *buf[], size_t len)
2485b9c547cSRui Paulo {
2495b9c547cSRui Paulo 	bfd *abfd;
2505b9c547cSRui Paulo 	void *btrace_res[WPA_TRACE_LEN];
2515b9c547cSRui Paulo 	int i, btrace_num;
2525b9c547cSRui Paulo 	size_t pos = 0;
2535b9c547cSRui Paulo 
2545b9c547cSRui Paulo 	if (len == 0)
2555b9c547cSRui Paulo 		return 0;
2565b9c547cSRui Paulo 	if (len > WPA_TRACE_LEN)
2575b9c547cSRui Paulo 		len = WPA_TRACE_LEN;
2585b9c547cSRui Paulo 
2595b9c547cSRui Paulo 	wpa_trace_bfd_init();
2605b9c547cSRui Paulo 	abfd = cached_abfd;
2615b9c547cSRui Paulo 	if (!abfd)
2625b9c547cSRui Paulo 		return 0;
2635b9c547cSRui Paulo 
2645b9c547cSRui Paulo 	btrace_num = backtrace(btrace_res, len);
2655b9c547cSRui Paulo 	if (btrace_num < 1)
2665b9c547cSRui Paulo 		return 0;
2675b9c547cSRui Paulo 
2685b9c547cSRui Paulo 	for (i = 0; i < btrace_num; i++) {
2695b9c547cSRui Paulo 		struct bfd_data data;
2705b9c547cSRui Paulo 
2715b9c547cSRui Paulo 		data.pc = (bfd_hostptr_t) btrace_res[i];
2725b9c547cSRui Paulo 		data.found = FALSE;
2735b9c547cSRui Paulo 		bfd_map_over_sections(abfd, find_addr_sect, &data);
2745b9c547cSRui Paulo 
2755b9c547cSRui Paulo 		while (data.found) {
2765b9c547cSRui Paulo 			if (data.function &&
2775b9c547cSRui Paulo 			    (pos > 0 ||
2785b9c547cSRui Paulo 			     os_strcmp(data.function, __func__) != 0)) {
2795b9c547cSRui Paulo 				buf[pos++] = data.function;
2805b9c547cSRui Paulo 				if (pos == len)
2815b9c547cSRui Paulo 					return pos;
2825b9c547cSRui Paulo 			}
2835b9c547cSRui Paulo 
2845b9c547cSRui Paulo 			data.found = bfd_find_inliner_info(abfd, &data.filename,
2855b9c547cSRui Paulo 							   &data.function,
2865b9c547cSRui Paulo 							   &data.line);
2875b9c547cSRui Paulo 		}
2885b9c547cSRui Paulo 	}
2895b9c547cSRui Paulo 
2905b9c547cSRui Paulo 	return pos;
2915b9c547cSRui Paulo }
2925b9c547cSRui Paulo 
293e28a4053SRui Paulo #else /* WPA_TRACE_BFD */
294e28a4053SRui Paulo 
295e28a4053SRui Paulo #define wpa_trace_bfd_init() do { } while (0)
296e28a4053SRui Paulo #define wpa_trace_bfd_addr(pc) do { } while (0)
297e28a4053SRui Paulo #define wpa_trace_bfd_addr2func(pc) NULL
298e28a4053SRui Paulo 
299e28a4053SRui Paulo #endif /* WPA_TRACE_BFD */
300e28a4053SRui Paulo 
301e28a4053SRui Paulo void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
302e28a4053SRui Paulo {
303e28a4053SRui Paulo 	char **sym;
304e28a4053SRui Paulo 	int i;
305e28a4053SRui Paulo 	enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
306e28a4053SRui Paulo 
307e28a4053SRui Paulo 	wpa_trace_bfd_init();
308e28a4053SRui Paulo 	wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
309e28a4053SRui Paulo 	sym = backtrace_symbols(btrace, btrace_num);
310e28a4053SRui Paulo 	state = TRACE_HEAD;
311e28a4053SRui Paulo 	for (i = 0; i < btrace_num; i++) {
312e28a4053SRui Paulo 		const char *func = wpa_trace_bfd_addr2func(btrace[i]);
313e28a4053SRui Paulo 		if (state == TRACE_HEAD && func &&
314e28a4053SRui Paulo 		    (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
315e28a4053SRui Paulo 		     os_strcmp(func, "wpa_trace_check_ref") == 0 ||
316e28a4053SRui Paulo 		     os_strcmp(func, "wpa_trace_show") == 0))
317e28a4053SRui Paulo 			continue;
318e28a4053SRui Paulo 		if (state == TRACE_TAIL && sym && sym[i] &&
319e28a4053SRui Paulo 		    os_strstr(sym[i], "__libc_start_main"))
320e28a4053SRui Paulo 			break;
321e28a4053SRui Paulo 		if (state == TRACE_HEAD)
322e28a4053SRui Paulo 			state = TRACE_RELEVANT;
323e28a4053SRui Paulo 		if (sym)
324e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
325e28a4053SRui Paulo 		else
326e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
327e28a4053SRui Paulo 		wpa_trace_bfd_addr(btrace[i]);
328e28a4053SRui Paulo 		if (state == TRACE_RELEVANT && func &&
329e28a4053SRui Paulo 		    os_strcmp(func, "main") == 0)
330e28a4053SRui Paulo 			state = TRACE_TAIL;
331e28a4053SRui Paulo 	}
332e28a4053SRui Paulo 	free(sym);
333e28a4053SRui Paulo 	wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
334e28a4053SRui Paulo }
335e28a4053SRui Paulo 
336e28a4053SRui Paulo 
337e28a4053SRui Paulo void wpa_trace_show(const char *title)
338e28a4053SRui Paulo {
339e28a4053SRui Paulo 	struct info {
340e28a4053SRui Paulo 		WPA_TRACE_INFO
341e28a4053SRui Paulo 	} info;
342e28a4053SRui Paulo 	wpa_trace_record(&info);
343e28a4053SRui Paulo 	wpa_trace_dump(title, &info);
344e28a4053SRui Paulo }
345e28a4053SRui Paulo 
346e28a4053SRui Paulo 
347e28a4053SRui Paulo void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
348e28a4053SRui Paulo {
349e28a4053SRui Paulo 	if (addr == NULL)
350e28a4053SRui Paulo 		return;
351e28a4053SRui Paulo 	ref->addr = addr;
352e28a4053SRui Paulo 	wpa_trace_record(ref);
353e28a4053SRui Paulo 	dl_list_add(&active_references, &ref->list);
354e28a4053SRui Paulo }
355e28a4053SRui Paulo 
356e28a4053SRui Paulo 
357e28a4053SRui Paulo void wpa_trace_check_ref(const void *addr)
358e28a4053SRui Paulo {
359e28a4053SRui Paulo 	struct wpa_trace_ref *ref;
360e28a4053SRui Paulo 	dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
361e28a4053SRui Paulo 		if (addr != ref->addr)
362e28a4053SRui Paulo 			continue;
363e28a4053SRui Paulo 		wpa_trace_show("Freeing referenced memory");
364e28a4053SRui Paulo 		wpa_trace_dump("Reference registration", ref);
365e28a4053SRui Paulo 		abort();
366e28a4053SRui Paulo 	}
367e28a4053SRui Paulo }
368e28a4053SRui Paulo 
369*780fb4a2SCy Schubert 
370*780fb4a2SCy Schubert void wpa_trace_deinit(void)
371*780fb4a2SCy Schubert {
372*780fb4a2SCy Schubert #ifdef WPA_TRACE_BFD
373*780fb4a2SCy Schubert 	free(syms);
374*780fb4a2SCy Schubert 	syms = NULL;
375*780fb4a2SCy Schubert #endif /* WPA_TRACE_BFD */
376*780fb4a2SCy Schubert }
377*780fb4a2SCy Schubert 
378e28a4053SRui Paulo #endif /* WPA_TRACE */
379