trace.c revision 351611
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#ifdef WPA_TRACE_BFD
10#define _GNU_SOURCE
11#include <link.h>
12#endif /* WPA_TRACE_BCD */
13#include "includes.h"
14
15#include "common.h"
16#include "trace.h"
17
18#ifdef WPA_TRACE
19
20static struct dl_list active_references =
21{ &active_references, &active_references };
22
23#ifdef WPA_TRACE_BFD
24#include <bfd.h>
25
26#define DMGL_PARAMS      (1 << 0)
27#define DMGL_ANSI        (1 << 1)
28
29static char *prg_fname = NULL;
30static bfd *cached_abfd = NULL;
31static asymbol **syms = NULL;
32static unsigned long start_offset;
33static int start_offset_looked_up;
34
35
36static int callback(struct dl_phdr_info *info, size_t size, void *data)
37{
38	/*
39	 * dl_iterate_phdr(3):
40	 * "The first object visited by callback is the main program."
41	 */
42	start_offset = info->dlpi_addr;
43
44	/*
45	 * dl_iterate_phdr(3):
46	 * "The dl_iterate_phdr() function walks through the list of an
47	 *  application's shared objects and calls the function callback
48	 *  once for each object, until either all shared objects have
49	 *  been processed or callback returns a nonzero value."
50	 */
51	return 1;
52}
53
54
55static void get_prg_fname(void)
56{
57	char exe[50], fname[512];
58	int len;
59	os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
60	len = readlink(exe, fname, sizeof(fname) - 1);
61	if (len < 0 || len >= (int) sizeof(fname)) {
62		wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno));
63		return;
64	}
65	fname[len] = '\0';
66	prg_fname = strdup(fname);
67}
68
69
70static bfd * open_bfd(const char *fname)
71{
72	bfd *abfd;
73	char **matching;
74
75	abfd = bfd_openr(prg_fname, NULL);
76	if (abfd == NULL) {
77		wpa_printf(MSG_INFO, "bfd_openr failed");
78		return NULL;
79	}
80
81	if (bfd_check_format(abfd, bfd_archive)) {
82		wpa_printf(MSG_INFO, "bfd_check_format failed");
83		bfd_close(abfd);
84		return NULL;
85	}
86
87	if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
88		wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
89		free(matching);
90		bfd_close(abfd);
91		return NULL;
92	}
93
94	return abfd;
95}
96
97
98static void read_syms(bfd *abfd)
99{
100	long storage, symcount;
101	bfd_boolean dynamic = FALSE;
102
103	if (syms)
104		return;
105
106	if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
107		wpa_printf(MSG_INFO, "No symbols");
108		return;
109	}
110
111	storage = bfd_get_symtab_upper_bound(abfd);
112	if (storage == 0) {
113		storage = bfd_get_dynamic_symtab_upper_bound(abfd);
114		dynamic = TRUE;
115	}
116	if (storage < 0) {
117		wpa_printf(MSG_INFO, "Unknown symtab upper bound");
118		return;
119	}
120
121	syms = malloc(storage);
122	if (syms == NULL) {
123		wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
124			   "(%ld bytes)", storage);
125		return;
126	}
127	if (dynamic)
128		symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
129	else
130		symcount = bfd_canonicalize_symtab(abfd, syms);
131	if (symcount < 0) {
132		wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
133			   dynamic ? "dynamic " : "");
134		free(syms);
135		syms = NULL;
136		return;
137	}
138}
139
140
141struct bfd_data {
142	bfd_vma pc;
143	bfd_boolean found;
144	const char *filename;
145	const char *function;
146	unsigned int line;
147};
148
149
150static void find_addr_sect(bfd *abfd, asection *section, void *obj)
151{
152	struct bfd_data *data = obj;
153	bfd_vma vma;
154	bfd_size_type size;
155
156	if (data->found)
157		return;
158
159	if (!(bfd_get_section_vma(abfd, section)))
160		return;
161
162	vma = bfd_get_section_vma(abfd, section);
163	if (data->pc < vma)
164		return;
165
166	size = bfd_get_section_size(section);
167	if (data->pc >= vma + size)
168		return;
169
170	data->found = bfd_find_nearest_line(abfd, section, syms,
171					    data->pc - vma,
172					    &data->filename,
173					    &data->function,
174					    &data->line);
175}
176
177
178static void wpa_trace_bfd_addr(void *pc)
179{
180	bfd *abfd = cached_abfd;
181	struct bfd_data data;
182	const char *name;
183	char *aname = NULL;
184	const char *filename;
185
186	if (abfd == NULL)
187		return;
188
189	data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
190	data.found = FALSE;
191	bfd_map_over_sections(abfd, find_addr_sect, &data);
192
193	if (!data.found)
194		return;
195
196	do {
197		if (data.function)
198			aname = bfd_demangle(abfd, data.function,
199					     DMGL_ANSI | DMGL_PARAMS);
200		name = aname ? aname : data.function;
201		filename = data.filename;
202		if (filename) {
203			char *end = os_strrchr(filename, '/');
204			int i = 0;
205			while (*filename && *filename == prg_fname[i] &&
206			       filename <= end) {
207				filename++;
208				i++;
209			}
210		}
211		wpa_printf(MSG_INFO, "     %s() %s:%u",
212			   name, filename, data.line);
213		free(aname);
214		aname = NULL;
215
216		data.found = bfd_find_inliner_info(abfd, &data.filename,
217						   &data.function, &data.line);
218	} while (data.found);
219}
220
221
222static const char * wpa_trace_bfd_addr2func(void *pc)
223{
224	bfd *abfd = cached_abfd;
225	struct bfd_data data;
226
227	if (abfd == NULL)
228		return NULL;
229
230	data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
231	data.found = FALSE;
232	bfd_map_over_sections(abfd, find_addr_sect, &data);
233
234	if (!data.found)
235		return NULL;
236
237	return data.function;
238}
239
240
241static void wpa_trace_bfd_init(void)
242{
243	if (!prg_fname) {
244		get_prg_fname();
245		if (!prg_fname)
246			return;
247	}
248
249	if (!cached_abfd) {
250		cached_abfd = open_bfd(prg_fname);
251		if (!cached_abfd) {
252			wpa_printf(MSG_INFO, "Failed to open bfd");
253			return;
254		}
255	}
256
257	read_syms(cached_abfd);
258	if (!syms) {
259		wpa_printf(MSG_INFO, "Failed to read symbols");
260		return;
261	}
262
263	if (!start_offset_looked_up) {
264		dl_iterate_phdr(callback, NULL);
265		start_offset_looked_up = 1;
266	}
267}
268
269
270void wpa_trace_dump_funcname(const char *title, void *pc)
271{
272	wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
273	wpa_trace_bfd_init();
274	wpa_trace_bfd_addr(pc);
275}
276
277
278size_t wpa_trace_calling_func(const char *buf[], size_t len)
279{
280	bfd *abfd;
281	void *btrace_res[WPA_TRACE_LEN];
282	int i, btrace_num;
283	size_t pos = 0;
284
285	if (len == 0)
286		return 0;
287	if (len > WPA_TRACE_LEN)
288		len = WPA_TRACE_LEN;
289
290	wpa_trace_bfd_init();
291	abfd = cached_abfd;
292	if (!abfd)
293		return 0;
294
295	btrace_num = backtrace(btrace_res, len);
296	if (btrace_num < 1)
297		return 0;
298
299	for (i = 0; i < btrace_num; i++) {
300		struct bfd_data data;
301
302		data.pc = (bfd_hostptr_t) ((u8 *) btrace_res[i] - start_offset);
303		data.found = FALSE;
304		bfd_map_over_sections(abfd, find_addr_sect, &data);
305
306		while (data.found) {
307			if (data.function &&
308			    (pos > 0 ||
309			     os_strcmp(data.function, __func__) != 0)) {
310				buf[pos++] = data.function;
311				if (pos == len)
312					return pos;
313			}
314
315			data.found = bfd_find_inliner_info(abfd, &data.filename,
316							   &data.function,
317							   &data.line);
318		}
319	}
320
321	return pos;
322}
323
324#else /* WPA_TRACE_BFD */
325
326#define wpa_trace_bfd_init() do { } while (0)
327#define wpa_trace_bfd_addr(pc) do { } while (0)
328#define wpa_trace_bfd_addr2func(pc) NULL
329
330#endif /* WPA_TRACE_BFD */
331
332void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
333{
334	char **sym;
335	int i;
336	enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
337
338	wpa_trace_bfd_init();
339	wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
340	sym = backtrace_symbols(btrace, btrace_num);
341	state = TRACE_HEAD;
342	for (i = 0; i < btrace_num; i++) {
343		const char *func = wpa_trace_bfd_addr2func(btrace[i]);
344		if (state == TRACE_HEAD && func &&
345		    (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
346		     os_strcmp(func, "wpa_trace_check_ref") == 0 ||
347		     os_strcmp(func, "wpa_trace_show") == 0))
348			continue;
349		if (state == TRACE_TAIL && sym && sym[i] &&
350		    os_strstr(sym[i], "__libc_start_main"))
351			break;
352		if (state == TRACE_HEAD)
353			state = TRACE_RELEVANT;
354		if (sym)
355			wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
356		else
357			wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
358		wpa_trace_bfd_addr(btrace[i]);
359		if (state == TRACE_RELEVANT && func &&
360		    os_strcmp(func, "main") == 0)
361			state = TRACE_TAIL;
362	}
363	free(sym);
364	wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
365}
366
367
368void wpa_trace_show(const char *title)
369{
370	struct info {
371		WPA_TRACE_INFO
372	} info;
373	wpa_trace_record(&info);
374	wpa_trace_dump(title, &info);
375}
376
377
378void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
379{
380	if (addr == NULL)
381		return;
382	ref->addr = addr;
383	wpa_trace_record(ref);
384	dl_list_add(&active_references, &ref->list);
385}
386
387
388void wpa_trace_check_ref(const void *addr)
389{
390	struct wpa_trace_ref *ref;
391	dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
392		if (addr != ref->addr)
393			continue;
394		wpa_trace_show("Freeing referenced memory");
395		wpa_trace_dump("Reference registration", ref);
396		abort();
397	}
398}
399
400
401void wpa_trace_deinit(void)
402{
403#ifdef WPA_TRACE_BFD
404	free(syms);
405	syms = NULL;
406#endif /* WPA_TRACE_BFD */
407}
408
409#endif /* WPA_TRACE */
410