$search
00001 /* 00002 * Backtrace debugging 00003 * Copyright (c) 2009, Jouni Malinen <j@w1.fi> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License version 2 as 00007 * published by the Free Software Foundation. 00008 * 00009 * Alternatively, this software may be distributed under the terms of BSD 00010 * license. 00011 * 00012 * See README and COPYING for more details. 00013 */ 00014 00015 #include "includes.h" 00016 00017 #include "common.h" 00018 #include "trace.h" 00019 00020 #ifdef WPA_TRACE 00021 00022 static struct dl_list active_references = 00023 { &active_references, &active_references }; 00024 00025 #ifdef WPA_TRACE_BFD 00026 #include <bfd.h> 00027 #ifdef __linux__ 00028 #include <demangle.h> 00029 #else /* __linux__ */ 00030 #include <libiberty/demangle.h> 00031 #endif /* __linux__ */ 00032 00033 static char *prg_fname = NULL; 00034 static bfd *cached_abfd = NULL; 00035 static asymbol **syms = NULL; 00036 00037 static void get_prg_fname(void) 00038 { 00039 char exe[50], fname[512]; 00040 int len; 00041 os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid()); 00042 len = readlink(exe, fname, sizeof(fname) - 1); 00043 if (len < 0 || len >= (int) sizeof(fname)) { 00044 perror("readlink"); 00045 return; 00046 } 00047 fname[len] = '\0'; 00048 prg_fname = strdup(fname); 00049 } 00050 00051 00052 static bfd * open_bfd(const char *fname) 00053 { 00054 bfd *abfd; 00055 char **matching; 00056 00057 abfd = bfd_openr(prg_fname, NULL); 00058 if (abfd == NULL) { 00059 wpa_printf(MSG_INFO, "bfd_openr failed"); 00060 return NULL; 00061 } 00062 00063 if (bfd_check_format(abfd, bfd_archive)) { 00064 wpa_printf(MSG_INFO, "bfd_check_format failed"); 00065 bfd_close(abfd); 00066 return NULL; 00067 } 00068 00069 if (!bfd_check_format_matches(abfd, bfd_object, &matching)) { 00070 wpa_printf(MSG_INFO, "bfd_check_format_matches failed"); 00071 free(matching); 00072 bfd_close(abfd); 00073 return NULL; 00074 } 00075 00076 return abfd; 00077 } 00078 00079 00080 static void read_syms(bfd *abfd) 00081 { 00082 long storage, symcount; 00083 bfd_boolean dynamic = FALSE; 00084 00085 if (syms) 00086 return; 00087 00088 if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) { 00089 wpa_printf(MSG_INFO, "No symbols"); 00090 return; 00091 } 00092 00093 storage = bfd_get_symtab_upper_bound(abfd); 00094 if (storage == 0) { 00095 storage = bfd_get_dynamic_symtab_upper_bound(abfd); 00096 dynamic = TRUE; 00097 } 00098 if (storage < 0) { 00099 wpa_printf(MSG_INFO, "Unknown symtab upper bound"); 00100 return; 00101 } 00102 00103 syms = malloc(storage); 00104 if (syms == NULL) { 00105 wpa_printf(MSG_INFO, "Failed to allocate memory for symtab " 00106 "(%ld bytes)", storage); 00107 return; 00108 } 00109 if (dynamic) 00110 symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); 00111 else 00112 symcount = bfd_canonicalize_symtab(abfd, syms); 00113 if (symcount < 0) { 00114 wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab", 00115 dynamic ? "dynamic " : ""); 00116 free(syms); 00117 syms = NULL; 00118 return; 00119 } 00120 } 00121 00122 00123 struct bfd_data { 00124 bfd_vma pc; 00125 bfd_boolean found; 00126 const char *filename; 00127 const char *function; 00128 unsigned int line; 00129 }; 00130 00131 00132 static void find_addr_sect(bfd *abfd, asection *section, void *obj) 00133 { 00134 struct bfd_data *data = obj; 00135 bfd_vma vma; 00136 bfd_size_type size; 00137 00138 if (data->found) 00139 return; 00140 00141 if (!(bfd_get_section_vma(abfd, section))) 00142 return; 00143 00144 vma = bfd_get_section_vma(abfd, section); 00145 if (data->pc < vma) 00146 return; 00147 00148 size = bfd_get_section_size(section); 00149 if (data->pc >= vma + size) 00150 return; 00151 00152 data->found = bfd_find_nearest_line(abfd, section, syms, 00153 data->pc - vma, 00154 &data->filename, 00155 &data->function, 00156 &data->line); 00157 } 00158 00159 00160 static void wpa_trace_bfd_addr(void *pc) 00161 { 00162 bfd *abfd = cached_abfd; 00163 struct bfd_data data; 00164 const char *name; 00165 char *aname = NULL; 00166 const char *filename; 00167 00168 if (abfd == NULL) 00169 return; 00170 00171 data.pc = (bfd_vma) pc; 00172 data.found = FALSE; 00173 bfd_map_over_sections(abfd, find_addr_sect, &data); 00174 00175 if (!data.found) 00176 return; 00177 00178 do { 00179 if (data.function) 00180 aname = bfd_demangle(abfd, data.function, 00181 DMGL_ANSI | DMGL_PARAMS); 00182 name = aname ? aname : data.function; 00183 filename = data.filename; 00184 if (filename) { 00185 char *end = os_strrchr(filename, '/'); 00186 int i = 0; 00187 while (*filename && *filename == prg_fname[i] && 00188 filename <= end) { 00189 filename++; 00190 i++; 00191 } 00192 } 00193 wpa_printf(MSG_INFO, " %s() %s:%u", 00194 name, filename, data.line); 00195 free(aname); 00196 00197 data.found = bfd_find_inliner_info(abfd, &data.filename, 00198 &data.function, &data.line); 00199 } while (data.found); 00200 } 00201 00202 00203 static const char * wpa_trace_bfd_addr2func(void *pc) 00204 { 00205 bfd *abfd = cached_abfd; 00206 struct bfd_data data; 00207 00208 if (abfd == NULL) 00209 return NULL; 00210 00211 data.pc = (bfd_vma) pc; 00212 data.found = FALSE; 00213 bfd_map_over_sections(abfd, find_addr_sect, &data); 00214 00215 if (!data.found) 00216 return NULL; 00217 00218 return data.function; 00219 } 00220 00221 00222 static void wpa_trace_bfd_init(void) 00223 { 00224 if (!prg_fname) { 00225 get_prg_fname(); 00226 if (!prg_fname) 00227 return; 00228 } 00229 00230 if (!cached_abfd) { 00231 cached_abfd = open_bfd(prg_fname); 00232 if (!cached_abfd) { 00233 wpa_printf(MSG_INFO, "Failed to open bfd"); 00234 return; 00235 } 00236 } 00237 00238 read_syms(cached_abfd); 00239 if (!syms) { 00240 wpa_printf(MSG_INFO, "Failed to read symbols"); 00241 return; 00242 } 00243 } 00244 00245 00246 void wpa_trace_dump_funcname(const char *title, void *pc) 00247 { 00248 wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc); 00249 wpa_trace_bfd_init(); 00250 wpa_trace_bfd_addr(pc); 00251 } 00252 00253 #else /* WPA_TRACE_BFD */ 00254 00255 #define wpa_trace_bfd_init() do { } while (0) 00256 #define wpa_trace_bfd_addr(pc) do { } while (0) 00257 #define wpa_trace_bfd_addr2func(pc) NULL 00258 00259 #endif /* WPA_TRACE_BFD */ 00260 00261 void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) 00262 { 00263 char **sym; 00264 int i; 00265 enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state; 00266 00267 wpa_trace_bfd_init(); 00268 wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title); 00269 sym = backtrace_symbols(btrace, btrace_num); 00270 state = TRACE_HEAD; 00271 for (i = 0; i < btrace_num; i++) { 00272 const char *func = wpa_trace_bfd_addr2func(btrace[i]); 00273 if (state == TRACE_HEAD && func && 00274 (os_strcmp(func, "wpa_trace_add_ref_func") == 0 || 00275 os_strcmp(func, "wpa_trace_check_ref") == 0 || 00276 os_strcmp(func, "wpa_trace_show") == 0)) 00277 continue; 00278 if (state == TRACE_TAIL && sym && sym[i] && 00279 os_strstr(sym[i], "__libc_start_main")) 00280 break; 00281 if (state == TRACE_HEAD) 00282 state = TRACE_RELEVANT; 00283 if (sym) 00284 wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]); 00285 else 00286 wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]); 00287 wpa_trace_bfd_addr(btrace[i]); 00288 if (state == TRACE_RELEVANT && func && 00289 os_strcmp(func, "main") == 0) 00290 state = TRACE_TAIL; 00291 } 00292 free(sym); 00293 wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title); 00294 } 00295 00296 00297 void wpa_trace_show(const char *title) 00298 { 00299 struct info { 00300 WPA_TRACE_INFO 00301 } info; 00302 wpa_trace_record(&info); 00303 wpa_trace_dump(title, &info); 00304 } 00305 00306 00307 void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr) 00308 { 00309 if (addr == NULL) 00310 return; 00311 ref->addr = addr; 00312 wpa_trace_record(ref); 00313 dl_list_add(&active_references, &ref->list); 00314 } 00315 00316 00317 void wpa_trace_check_ref(const void *addr) 00318 { 00319 struct wpa_trace_ref *ref; 00320 dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) { 00321 if (addr != ref->addr) 00322 continue; 00323 wpa_trace_show("Freeing referenced memory"); 00324 wpa_trace_dump("Reference registration", ref); 00325 abort(); 00326 } 00327 } 00328 00329 #endif /* WPA_TRACE */