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