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