35cb8bd2a5d014334393c1d039dca8e4e9b6b260
[libeap.git] / src / utils / trace.c
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
22 static struct dl_list active_references =
23 { &active_references, &active_references };
24
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
201 static void wpa_trace_bfd_init(void)
202 {
203         if (!prg_fname) {
204                 get_prg_fname();
205                 if (!prg_fname)
206                         return;
207                 wpa_printf(MSG_INFO, "BFD[%s]", prg_fname);
208         }
209
210         if (!cached_abfd) {
211                 cached_abfd = open_bfd(prg_fname);
212                 if (!cached_abfd) {
213                         wpa_printf(MSG_INFO, "Failed to open bfd");
214                         return;
215                 }
216         }
217
218         read_syms(cached_abfd);
219         if (!syms) {
220                 wpa_printf(MSG_INFO, "Failed to read symbols");
221                 return;
222         }
223 }
224
225 #else /* WPA_TRACE_BFD */
226
227 #define wpa_trace_bfd_init() do { } while (0)
228 #define wpa_trace_bfd_addr(pc) do { } while (0)
229
230 #endif /* WPA_TRACE_BFD */
231
232 void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
233 {
234         char **sym;
235         int i;
236
237         wpa_trace_bfd_init();
238         wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
239         sym = backtrace_symbols(btrace, btrace_num);
240         for (i = 0; i < btrace_num; i++) {
241                 if (sym)
242                         wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
243                 else
244                         wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
245                 wpa_trace_bfd_addr(btrace[i]);
246         }
247         free(sym);
248         wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
249 }
250
251
252 void wpa_trace_show(const char *title)
253 {
254         struct info {
255                 WPA_TRACE_INFO
256         } info;
257         wpa_trace_record(&info);
258         wpa_trace_dump(title, &info);
259 }
260
261
262 void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
263 {
264         if (addr == NULL)
265                 return;
266         ref->addr = addr;
267         wpa_trace_record(ref);
268         dl_list_add(&active_references, &ref->list);
269 }
270
271
272 void wpa_trace_check_ref(const void *addr)
273 {
274         struct wpa_trace_ref *ref;
275         dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
276                 if (addr != ref->addr)
277                         continue;
278                 wpa_trace_show("Freeing referenced memory");
279                 wpa_trace_dump("Reference registration", ref);
280                 abort();
281         }
282 }
283
284 #endif /* WPA_TRACE */