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