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