04b50d5709eac8ee57a761abb5977f38334dd8c1
[strongswan.git] / src / libstrongswan / utils / backtrace.c
1 /*
2 * Copyright (C) 2006-2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17
18 #ifdef HAVE_DLADDR
19 # include <dlfcn.h>
20 #endif /* HAVE_DLADDR */
21
22 #ifdef HAVE_BACKTRACE
23 # include <execinfo.h>
24 #endif /* HAVE_BACKTRACE */
25
26 #include <string.h>
27
28 #include "backtrace.h"
29
30 typedef struct private_backtrace_t private_backtrace_t;
31
32 /**
33 * Private data of an backtrace_t object.
34 */
35 struct private_backtrace_t {
36
37 /**
38 * Public backtrace_t interface.
39 */
40 backtrace_t public;
41
42 /**
43 * Number of stacks frames obtained in stack_frames
44 */
45 int frame_count;
46
47 /**
48 * Recorded stack frames.
49 */
50 void *frames[];
51 };
52
53 #ifdef HAVE_DLADDR
54 #ifdef HAVE_BFD_H
55
56 #include <bfd.h>
57 #include <utils/hashtable.h>
58 #include <threading/mutex.h>
59
60 /**
61 * Hashtable-cached bfd handle
62 */
63 typedef struct {
64 /** binary file name on disk */
65 char *filename;
66 /** bfd handle */
67 bfd *abfd;
68 /** loaded symbols */
69 asymbol **syms;
70 } bfd_entry_t;
71
72 /**
73 * Destroy a bfd_entry
74 */
75 static void bfd_entry_destroy(bfd_entry_t *this)
76 {
77 free(this->filename);
78 free(this->syms);
79 bfd_close(this->abfd);
80 free(this);
81 }
82
83 /**
84 * Data to pass to find_addr()
85 */
86 typedef struct {
87 /** used bfd entry */
88 bfd_entry_t *entry;
89 /** backtrace address */
90 bfd_vma vma;
91 /** stream to log to */
92 FILE *file;
93 /** TRUE if complete */
94 bool found;
95 } bfd_find_data_t;
96
97 /**
98 * bfd entry cache
99 */
100 static hashtable_t *bfds;
101
102 static mutex_t *bfd_mutex;
103
104 /**
105 * Hashtable hash function
106 */
107 static u_int bfd_hash(char *key)
108 {
109 return chunk_hash(chunk_create(key, strlen(key)));
110 }
111
112 /**
113 * Hashtable equals function
114 */
115 static bool bfd_equals(char *a, char *b)
116 {
117 return streq(a, b);
118 }
119
120 /**
121 * See header.
122 */
123 void backtrace_init()
124 {
125 bfd_init();
126 bfds = hashtable_create((hashtable_hash_t)bfd_hash,
127 (hashtable_equals_t)bfd_equals, 8);
128 bfd_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
129 }
130
131 /**
132 * See header.
133 */
134 void backtrace_deinit()
135 {
136 enumerator_t *enumerator;
137 bfd_entry_t *entry;
138 char *key;
139
140 enumerator = bfds->create_enumerator(bfds);
141 while (enumerator->enumerate(enumerator, &key, &entry))
142 {
143 bfds->remove_at(bfds, enumerator);
144 bfd_entry_destroy(entry);
145 }
146 enumerator->destroy(enumerator);
147
148 bfds->destroy(bfds);
149 bfd_mutex->destroy(bfd_mutex);
150 }
151
152 /**
153 * Find and print information to an address
154 */
155 static void find_addr(bfd *abfd, asection *section, bfd_find_data_t *data)
156 {
157 bfd_size_type size;
158 bfd_vma vma;
159 const char *source;
160 const char *function;
161 u_int line;
162
163 if (!data->found || (bfd_get_section_flags(abfd, section) & SEC_ALLOC) != 0)
164 {
165 vma = bfd_get_section_vma(abfd, section);
166 if (data->vma >= vma)
167 {
168 size = bfd_get_section_size(section);
169 if (data->vma < vma + size)
170 {
171 data->found = bfd_find_nearest_line(abfd, section,
172 data->entry->syms, data->vma - vma,
173 &source, &function, &line);
174 if (data->found)
175 {
176 if (source || function)
177 {
178 fprintf(data->file, " -> ");
179 if (function)
180 {
181 fprintf(data->file, "\e[34m%s() ", function);
182 }
183 if (source)
184 {
185 fprintf(data->file, "\e[32m@ %s:%d", source, line);
186 }
187 fprintf(data->file, "\e[0m\n");
188 }
189 }
190 }
191 }
192 }
193 }
194
195 /**
196 * Find a cached bfd entry, create'n'cache if not found
197 */
198 static bfd_entry_t *get_bfd_entry(char *filename)
199 {
200 bool dynamic = FALSE, ok = FALSE;
201 bfd_entry_t *entry;
202 long size;
203
204 /* check cache */
205 entry = bfds->get(bfds, filename);
206 if (entry)
207 {
208 return entry;
209 }
210
211 INIT(entry,
212 .abfd = bfd_openr(filename, NULL),
213 );
214
215 if (!entry->abfd)
216 {
217 free(entry);
218 return NULL;
219 }
220 #ifdef BFD_DECOMPRESS
221 entry->abfd->flags |= BFD_DECOMPRESS;
222 #endif
223 if (bfd_check_format(entry->abfd, bfd_archive) == 0 &&
224 bfd_check_format_matches(entry->abfd, bfd_object, NULL))
225 {
226 if (bfd_get_file_flags(entry->abfd) & HAS_SYMS)
227 {
228 size = bfd_get_symtab_upper_bound(entry->abfd);
229 if (size == 0)
230 {
231 size = bfd_get_dynamic_symtab_upper_bound(entry->abfd);
232 }
233 if (size >= 0)
234 {
235 entry->syms = malloc(size);
236 if (dynamic)
237 {
238 ok = bfd_canonicalize_dynamic_symtab(entry->abfd,
239 entry->syms) >= 0;
240 }
241 else
242 {
243 ok = bfd_canonicalize_symtab(entry->abfd,
244 entry->syms) >= 0;
245 }
246 }
247 }
248 }
249 if (ok)
250 {
251 entry->filename = strdup(filename);
252 bfds->put(bfds, entry->filename, entry);
253 return entry;
254 }
255 bfd_entry_destroy(entry);
256 return NULL;
257 }
258
259 /**
260 * Print the source file with line number to file, libbfd variant
261 */
262 static void print_sourceline(FILE *file, char *filename, void *ptr)
263 {
264 bfd_entry_t *entry;
265 bfd_find_data_t data = {
266 .file = file,
267 .vma = (bfd_vma)ptr,
268 };
269 bool old = FALSE;
270
271 bfd_mutex->lock(bfd_mutex);
272 if (lib->leak_detective)
273 {
274 old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
275 }
276 entry = get_bfd_entry(filename);
277 if (entry)
278 {
279 data.entry = entry;
280 bfd_map_over_sections(entry->abfd, (void*)find_addr, &data);
281 }
282 if (lib->leak_detective)
283 {
284 lib->leak_detective->set_state(lib->leak_detective, old);
285 }
286 bfd_mutex->unlock(bfd_mutex);
287 }
288
289 #else /* !HAVE_BFD_H */
290
291 void backtrace_init() {}
292 void backtrace_deinit() {}
293
294 /**
295 * Print the source file with line number to file, slow addr2line variant
296 */
297 static void print_sourceline(FILE *file, char *filename, void *ptr)
298 {
299 char cmd[1024];
300 FILE *output;
301 int c;
302
303 snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", filename, ptr);
304 output = popen(cmd, "r");
305 if (output)
306 {
307 fprintf(file, " -> \e[32m");
308 while (TRUE)
309 {
310 c = getc(output);
311 if (c == '\n' || c == EOF)
312 {
313 break;
314 }
315 fputc(c, file);
316 }
317 pclose(output);
318 fprintf(file, "\e[0m\n");
319 }
320 }
321
322 #endif /* HAVE_BFD_H */
323 #endif /* HAVE_DLADDR */
324
325 METHOD(backtrace_t, log_, void,
326 private_backtrace_t *this, FILE *file, bool detailed)
327 {
328 #ifdef HAVE_BACKTRACE
329 size_t i;
330 char **strings;
331
332 strings = backtrace_symbols(this->frames, this->frame_count);
333
334 fprintf(file, " dumping %d stack frame addresses:\n", this->frame_count);
335 for (i = 0; i < this->frame_count; i++)
336 {
337 #ifdef HAVE_DLADDR
338 Dl_info info;
339
340 if (dladdr(this->frames[i], &info))
341 {
342 void *ptr = this->frames[i];
343
344 if (strstr(info.dli_fname, ".so"))
345 {
346 ptr = (void*)(this->frames[i] - info.dli_fbase);
347 }
348 if (info.dli_sname)
349 {
350 fprintf(file, " \e[33m%s\e[0m @ %p (\e[31m%s\e[0m+0x%tx) [%p]\n",
351 info.dli_fname, info.dli_fbase, info.dli_sname,
352 this->frames[i] - info.dli_saddr, this->frames[i]);
353 }
354 else
355 {
356 fprintf(file, " \e[33m%s\e[0m @ %p [%p]\n", info.dli_fname,
357 info.dli_fbase, this->frames[i]);
358 }
359 if (detailed)
360 {
361 print_sourceline(file, (char*)info.dli_fname, ptr);
362 }
363 }
364 else
365 #endif /* HAVE_DLADDR */
366 {
367 fprintf(file, " %s\n", strings[i]);
368 }
369 }
370 free (strings);
371 #else /* !HAVE_BACKTRACE */
372 fprintf(file, "C library does not support backtrace().\n");
373 #endif /* HAVE_BACKTRACE */
374 }
375
376 METHOD(backtrace_t, contains_function, bool,
377 private_backtrace_t *this, char *function[], int count)
378 {
379 #ifdef HAVE_DLADDR
380 int i, j;
381
382 for (i = 0; i< this->frame_count; i++)
383 {
384 Dl_info info;
385
386 if (dladdr(this->frames[i], &info) && info.dli_sname)
387 {
388 for (j = 0; j < count; j++)
389 {
390 if (streq(info.dli_sname, function[j]))
391 {
392 return TRUE;
393 }
394 }
395 }
396 }
397 #endif /* HAVE_DLADDR */
398 return FALSE;
399 }
400
401 METHOD(backtrace_t, equals, bool,
402 private_backtrace_t *this, backtrace_t *other_public)
403 {
404 private_backtrace_t *other = (private_backtrace_t*)other_public;
405 int i;
406
407 if (this == other)
408 {
409 return TRUE;
410 }
411 if (this->frame_count != other->frame_count)
412 {
413 return FALSE;
414 }
415 for (i = 0; i < this->frame_count; i++)
416 {
417 if (this->frames[i] != other->frames[i])
418 {
419 return FALSE;
420 }
421 }
422 return TRUE;
423 }
424
425 /**
426 * Frame enumerator
427 */
428 typedef struct {
429 /** implements enumerator_t */
430 enumerator_t public;
431 /** reference to backtrace */
432 private_backtrace_t *bt;
433 /** current position */
434 int i;
435 } frame_enumerator_t;
436
437 METHOD(enumerator_t, frame_enumerate, bool,
438 frame_enumerator_t *this, void **addr)
439 {
440 if (this->i < this->bt->frame_count)
441 {
442 *addr = this->bt->frames[this->i++];
443 return TRUE;
444 }
445 return FALSE;
446 }
447
448 METHOD(backtrace_t, create_frame_enumerator, enumerator_t*,
449 private_backtrace_t *this)
450 {
451 frame_enumerator_t *enumerator;
452
453 INIT(enumerator,
454 .public = {
455 .enumerate = (void*)_frame_enumerate,
456 .destroy = (void*)free,
457 },
458 .bt = this,
459 );
460 return &enumerator->public;
461 }
462
463 METHOD(backtrace_t, destroy, void,
464 private_backtrace_t *this)
465 {
466 free(this);
467 }
468
469 /**
470 * See header
471 */
472 backtrace_t *backtrace_create(int skip)
473 {
474 private_backtrace_t *this;
475 void *frames[50];
476 int frame_count = 0;
477
478 #ifdef HAVE_BACKTRACE
479 frame_count = backtrace(frames, countof(frames));
480 #endif /* HAVE_BACKTRACE */
481 frame_count = max(frame_count - skip, 0);
482 this = malloc(sizeof(private_backtrace_t) + frame_count * sizeof(void*));
483 memcpy(this->frames, frames + skip, frame_count * sizeof(void*));
484 this->frame_count = frame_count;
485
486 this->public = (backtrace_t) {
487 .log = _log_,
488 .contains_function = _contains_function,
489 .equals = _equals,
490 .create_frame_enumerator = _create_frame_enumerator,
491 .destroy = _destroy,
492 };
493
494 return &this->public;
495 }
496
497 /**
498 * See header
499 */
500 void backtrace_dump(char *label, FILE *file, bool detailed)
501 {
502 backtrace_t *backtrace;
503
504 backtrace = backtrace_create(2);
505
506 if (label)
507 {
508 fprintf(file, "Debug backtrace: %s\n", label);
509 }
510 backtrace->log(backtrace, file, detailed);
511 backtrace->destroy(backtrace);
512 }
513