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