blob: 94440f2f9726d502b5f59ed52b0f50f346d75c6c [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * Backtrace debugging
3 * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
4 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "trace.h"
13
14#ifdef WPA_TRACE
15
16static 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
27static char *prg_fname = NULL;
28static bfd *cached_abfd = NULL;
29static asymbol **syms = NULL;
30
31static 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
46static 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
74static 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
117struct 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
126static 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
154static 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);
Dmitry Shmidt7d5c8f22014-03-03 13:53:28 -0800190 aname = NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700191
192 data.found = bfd_find_inliner_info(abfd, &data.filename,
193 &data.function, &data.line);
194 } while (data.found);
195}
196
197
198static const char * wpa_trace_bfd_addr2func(void *pc)
199{
200 bfd *abfd = cached_abfd;
201 struct bfd_data data;
202
203 if (abfd == NULL)
204 return NULL;
205
206 data.pc = (bfd_vma) pc;
207 data.found = FALSE;
208 bfd_map_over_sections(abfd, find_addr_sect, &data);
209
210 if (!data.found)
211 return NULL;
212
213 return data.function;
214}
215
216
217static void wpa_trace_bfd_init(void)
218{
219 if (!prg_fname) {
220 get_prg_fname();
221 if (!prg_fname)
222 return;
223 }
224
225 if (!cached_abfd) {
226 cached_abfd = open_bfd(prg_fname);
227 if (!cached_abfd) {
228 wpa_printf(MSG_INFO, "Failed to open bfd");
229 return;
230 }
231 }
232
233 read_syms(cached_abfd);
234 if (!syms) {
235 wpa_printf(MSG_INFO, "Failed to read symbols");
236 return;
237 }
238}
239
240
241void wpa_trace_dump_funcname(const char *title, void *pc)
242{
243 wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
244 wpa_trace_bfd_init();
245 wpa_trace_bfd_addr(pc);
246}
247
248#else /* WPA_TRACE_BFD */
249
250#define wpa_trace_bfd_init() do { } while (0)
251#define wpa_trace_bfd_addr(pc) do { } while (0)
252#define wpa_trace_bfd_addr2func(pc) NULL
253
254#endif /* WPA_TRACE_BFD */
255
256void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
257{
258 char **sym;
259 int i;
260 enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
261
262 wpa_trace_bfd_init();
263 wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
264 sym = backtrace_symbols(btrace, btrace_num);
265 state = TRACE_HEAD;
266 for (i = 0; i < btrace_num; i++) {
267 const char *func = wpa_trace_bfd_addr2func(btrace[i]);
268 if (state == TRACE_HEAD && func &&
269 (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
270 os_strcmp(func, "wpa_trace_check_ref") == 0 ||
271 os_strcmp(func, "wpa_trace_show") == 0))
272 continue;
273 if (state == TRACE_TAIL && sym && sym[i] &&
274 os_strstr(sym[i], "__libc_start_main"))
275 break;
276 if (state == TRACE_HEAD)
277 state = TRACE_RELEVANT;
278 if (sym)
279 wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
280 else
281 wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
282 wpa_trace_bfd_addr(btrace[i]);
283 if (state == TRACE_RELEVANT && func &&
284 os_strcmp(func, "main") == 0)
285 state = TRACE_TAIL;
286 }
287 free(sym);
288 wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
289}
290
291
292void wpa_trace_show(const char *title)
293{
294 struct info {
295 WPA_TRACE_INFO
296 } info;
297 wpa_trace_record(&info);
298 wpa_trace_dump(title, &info);
299}
300
301
302void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
303{
304 if (addr == NULL)
305 return;
306 ref->addr = addr;
307 wpa_trace_record(ref);
308 dl_list_add(&active_references, &ref->list);
309}
310
311
312void wpa_trace_check_ref(const void *addr)
313{
314 struct wpa_trace_ref *ref;
315 dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
316 if (addr != ref->addr)
317 continue;
318 wpa_trace_show("Freeing referenced memory");
319 wpa_trace_dump("Reference registration", ref);
320 abort();
321 }
322}
323
324#endif /* WPA_TRACE */