Home | History | Annotate | Download | only in utils
      1 /*
      2  * Backtrace debugging
      3  * Copyright (c) 2009, Jouni Malinen <j (at) 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 
     16 static 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 
     27 static char *prg_fname = NULL;
     28 static bfd *cached_abfd = NULL;
     29 static asymbol **syms = NULL;
     30 
     31 static 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 
     46 static 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 
     74 static 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 
    117 struct 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 
    126 static 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 
    154 static 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 
    197 static 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 
    216 static 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 
    240 void 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 
    255 void 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 
    291 void 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 
    301 void 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 
    311 void 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