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