Merge branch 'vendor/TNFTP'
[dragonfly.git] / contrib / hostapd / src / utils / trace.c
1 /*
2  * Backtrace debugging
3  * Copyright (c) 2009, Jouni Malinen <j@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 #ifdef __linux__
22 #include <demangle.h>
23 #else /* __linux__ */
24 #include <libiberty/demangle.h>
25 #endif /* __linux__ */
26
27 static char *prg_fname = NULL;
28 static bfd *cached_abfd = NULL;
29 static asymbol **syms = NULL;
30
31 static 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
46 static 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
74 static 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
117 struct 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
126 static 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
154 static 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);
190
191                 data.found = bfd_find_inliner_info(abfd, &data.filename,
192                                                    &data.function, &data.line);
193         } while (data.found);
194 }
195
196
197 static const char * wpa_trace_bfd_addr2func(void *pc)
198 {
199         bfd *abfd = cached_abfd;
200         struct bfd_data data;
201
202         if (abfd == NULL)
203                 return NULL;
204
205         data.pc = (bfd_vma) pc;
206         data.found = FALSE;
207         bfd_map_over_sections(abfd, find_addr_sect, &data);
208
209         if (!data.found)
210                 return NULL;
211
212         return data.function;
213 }
214
215
216 static void wpa_trace_bfd_init(void)
217 {
218         if (!prg_fname) {
219                 get_prg_fname();
220                 if (!prg_fname)
221                         return;
222         }
223
224         if (!cached_abfd) {
225                 cached_abfd = open_bfd(prg_fname);
226                 if (!cached_abfd) {
227                         wpa_printf(MSG_INFO, "Failed to open bfd");
228                         return;
229                 }
230         }
231
232         read_syms(cached_abfd);
233         if (!syms) {
234                 wpa_printf(MSG_INFO, "Failed to read symbols");
235                 return;
236         }
237 }
238
239
240 void wpa_trace_dump_funcname(const char *title, void *pc)
241 {
242         wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
243         wpa_trace_bfd_init();
244         wpa_trace_bfd_addr(pc);
245 }
246
247 #else /* WPA_TRACE_BFD */
248
249 #define wpa_trace_bfd_init() do { } while (0)
250 #define wpa_trace_bfd_addr(pc) do { } while (0)
251 #define wpa_trace_bfd_addr2func(pc) NULL
252
253 #endif /* WPA_TRACE_BFD */
254
255 void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
256 {
257         char **sym;
258         int i;
259         enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
260
261         wpa_trace_bfd_init();
262         wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
263         sym = backtrace_symbols(btrace, btrace_num);
264         state = TRACE_HEAD;
265         for (i = 0; i < btrace_num; i++) {
266                 const char *func = wpa_trace_bfd_addr2func(btrace[i]);
267                 if (state == TRACE_HEAD && func &&
268                     (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
269                      os_strcmp(func, "wpa_trace_check_ref") == 0 ||
270                      os_strcmp(func, "wpa_trace_show") == 0))
271                         continue;
272                 if (state == TRACE_TAIL && sym && sym[i] &&
273                     os_strstr(sym[i], "__libc_start_main"))
274                         break;
275                 if (state == TRACE_HEAD)
276                         state = TRACE_RELEVANT;
277                 if (sym)
278                         wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
279                 else
280                         wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
281                 wpa_trace_bfd_addr(btrace[i]);
282                 if (state == TRACE_RELEVANT && func &&
283                     os_strcmp(func, "main") == 0)
284                         state = TRACE_TAIL;
285         }
286         free(sym);
287         wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
288 }
289
290
291 void wpa_trace_show(const char *title)
292 {
293         struct info {
294                 WPA_TRACE_INFO
295         } info;
296         wpa_trace_record(&info);
297         wpa_trace_dump(title, &info);
298 }
299
300
301 void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
302 {
303         if (addr == NULL)
304                 return;
305         ref->addr = addr;
306         wpa_trace_record(ref);
307         dl_list_add(&active_references, &ref->list);
308 }
309
310
311 void wpa_trace_check_ref(const void *addr)
312 {
313         struct wpa_trace_ref *ref;
314         dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
315                 if (addr != ref->addr)
316                         continue;
317                 wpa_trace_show("Freeing referenced memory");
318                 wpa_trace_dump("Reference registration", ref);
319                 abort();
320         }
321 }
322
323 #endif /* WPA_TRACE */