2 * Copyright (C) 2006-2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
20 #endif /* HAVE_DLADDR */
23 # include <execinfo.h>
24 #endif /* HAVE_BACKTRACE */
28 #include "backtrace.h"
30 #include <utils/debug.h>
32 typedef struct private_backtrace_t private_backtrace_t
;
35 * Private data of an backtrace_t object.
37 struct private_backtrace_t
{
40 * Public backtrace_t interface.
45 * Number of stacks frames obtained in stack_frames
50 * Recorded stack frames.
56 * Write a format string with arguments to a FILE line, if it is NULL to DBG
58 static void println(FILE *file
, char *format
, ...)
63 va_start(args
, format
);
66 vfprintf(file
, format
, args
);
71 vsnprintf(buf
, sizeof(buf
), format
, args
);
72 DBG1(DBG_LIB
, "%s", buf
);
80 * Same as tty_escape_get(), but for a potentially NULL FILE*
82 static char* esc(FILE *file
, tty_escape_t escape
)
86 return tty_escape_get(fileno(file
), escape
);
94 #include <collections/hashtable.h>
95 #include <threading/mutex.h>
98 * Hashtable-cached bfd handle
101 /** binary file name on disk */
105 /** loaded symbols */
110 * Destroy a bfd_entry
112 static void bfd_entry_destroy(bfd_entry_t
*this)
114 free(this->filename
);
116 bfd_close(this->abfd
);
121 * Data to pass to find_addr()
124 /** used bfd entry */
126 /** backtrace address */
128 /** stream to log to */
130 /** TRUE if complete */
137 static hashtable_t
*bfds
;
139 static mutex_t
*bfd_mutex
;
142 * Hashtable hash function
144 static u_int
bfd_hash(char *key
)
146 return chunk_hash(chunk_create(key
, strlen(key
)));
150 * Hashtable equals function
152 static bool bfd_equals(char *a
, char *b
)
160 void backtrace_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
);
171 void backtrace_deinit()
173 enumerator_t
*enumerator
;
177 enumerator
= bfds
->create_enumerator(bfds
);
178 while (enumerator
->enumerate(enumerator
, &key
, &entry
))
180 bfds
->remove_at(bfds
, enumerator
);
181 bfd_entry_destroy(entry
);
183 enumerator
->destroy(enumerator
);
186 bfd_mutex
->destroy(bfd_mutex
);
190 * Find and print information to an address
192 static void find_addr(bfd
*abfd
, asection
*section
, bfd_find_data_t
*data
)
197 const char *function
;
198 char fbuf
[512] = "", sbuf
[512] = "";
201 if (!data
->found
|| (bfd_get_section_flags(abfd
, section
) & SEC_ALLOC
) != 0)
203 vma
= bfd_get_section_vma(abfd
, section
);
204 if (data
->vma
>= vma
)
206 size
= bfd_get_section_size(section
);
207 if (data
->vma
< vma
+ size
)
209 data
->found
= bfd_find_nearest_line(abfd
, section
,
210 data
->entry
->syms
, data
->vma
- vma
,
211 &source
, &function
, &line
);
214 if (source
|| function
)
218 snprintf(fbuf
, sizeof(fbuf
), "%s%s() ",
219 esc(data
->file
, TTY_FG_BLUE
), function
);
223 snprintf(sbuf
, sizeof(sbuf
), "%s@ %s:%d",
224 esc(data
->file
, TTY_FG_GREEN
), source
, line
);
226 println(data
->file
, " -> %s%s%s", fbuf
, sbuf
,
227 esc(data
->file
, TTY_FG_DEF
));
236 * Find a cached bfd entry, create'n'cache if not found
238 static bfd_entry_t
*get_bfd_entry(char *filename
)
240 bool dynamic
= FALSE
, ok
= FALSE
;
245 entry
= bfds
->get(bfds
, filename
);
252 .abfd
= bfd_openr(filename
, NULL
),
260 #ifdef BFD_DECOMPRESS
261 entry
->abfd
->flags
|= BFD_DECOMPRESS
;
263 if (bfd_check_format(entry
->abfd
, bfd_archive
) == 0 &&
264 bfd_check_format_matches(entry
->abfd
, bfd_object
, NULL
))
266 if (bfd_get_file_flags(entry
->abfd
) & HAS_SYMS
)
268 size
= bfd_get_symtab_upper_bound(entry
->abfd
);
271 size
= bfd_get_dynamic_symtab_upper_bound(entry
->abfd
);
275 entry
->syms
= malloc(size
);
278 ok
= bfd_canonicalize_dynamic_symtab(entry
->abfd
,
283 ok
= bfd_canonicalize_symtab(entry
->abfd
,
291 entry
->filename
= strdup(filename
);
292 bfds
->put(bfds
, entry
->filename
, entry
);
295 bfd_entry_destroy(entry
);
300 * Print the source file with line number to file, libbfd variant
302 static void print_sourceline(FILE *file
, char *filename
, void *ptr
)
305 bfd_find_data_t data
= {
307 .vma
= (uintptr_t)ptr
,
311 bfd_mutex
->lock(bfd_mutex
);
312 if (lib
->leak_detective
)
314 old
= lib
->leak_detective
->set_state(lib
->leak_detective
, FALSE
);
316 entry
= get_bfd_entry(filename
);
320 bfd_map_over_sections(entry
->abfd
, (void*)find_addr
, &data
);
322 if (lib
->leak_detective
)
324 lib
->leak_detective
->set_state(lib
->leak_detective
, old
);
326 bfd_mutex
->unlock(bfd_mutex
);
329 #else /* !HAVE_BFD_H */
331 void backtrace_init() {}
332 void backtrace_deinit() {}
335 * Print the source file with line number to file, slow addr2line variant
337 static void print_sourceline(FILE *file
, char *filename
, void *ptr
)
343 snprintf(buf
, sizeof(buf
), "addr2line -e %s %p", filename
, ptr
);
344 output
= popen(buf
, "r");
347 while (i
< sizeof(buf
))
350 if (c
== '\n' || c
== EOF
)
359 println(file
, " -> %s%s%s", esc(file
, TTY_FG_GREEN
), buf
,
360 esc(file
, TTY_FG_DEF
));
364 #endif /* HAVE_BFD_H */
366 #else /* !HAVE_DLADDR */
368 void backtrace_init() {}
369 void backtrace_deinit() {}
371 #endif /* HAVE_DLADDR */
373 METHOD(backtrace_t
, log_
, void,
374 private_backtrace_t
*this, FILE *file
, bool detailed
)
376 #ifdef HAVE_BACKTRACE
380 strings
= backtrace_symbols(this->frames
, this->frame_count
);
382 println(file
, " dumping %d stack frame addresses:", this->frame_count
);
383 for (i
= 0; i
< this->frame_count
; i
++)
388 if (dladdr(this->frames
[i
], &info
))
390 void *ptr
= this->frames
[i
];
392 if (strstr(info
.dli_fname
, ".so"))
394 ptr
= (void*)(this->frames
[i
] - info
.dli_fbase
);
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
,
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
]);
411 if (detailed
&& info
.dli_fname
[0])
413 print_sourceline(file
, (char*)info
.dli_fname
, ptr
);
417 #endif /* HAVE_DLADDR */
419 println(file
, " %s", strings
[i
]);
423 #else /* !HAVE_BACKTRACE */
424 println(file
, "C library does not support backtrace().");
425 #endif /* HAVE_BACKTRACE */
428 METHOD(backtrace_t
, contains_function
, bool,
429 private_backtrace_t
*this, char *function
[], int count
)
434 for (i
= 0; i
< this->frame_count
; i
++)
438 if (dladdr(this->frames
[i
], &info
) && info
.dli_sname
)
440 for (j
= 0; j
< count
; j
++)
442 if (streq(info
.dli_sname
, function
[j
]))
449 #endif /* HAVE_DLADDR */
453 METHOD(backtrace_t
, equals
, bool,
454 private_backtrace_t
*this, backtrace_t
*other_public
)
456 private_backtrace_t
*other
= (private_backtrace_t
*)other_public
;
463 if (this->frame_count
!= other
->frame_count
)
467 for (i
= 0; i
< this->frame_count
; i
++)
469 if (this->frames
[i
] != other
->frames
[i
])
481 /** implements enumerator_t */
483 /** reference to backtrace */
484 private_backtrace_t
*bt
;
485 /** current position */
487 } frame_enumerator_t
;
489 METHOD(enumerator_t
, frame_enumerate
, bool,
490 frame_enumerator_t
*this, void **addr
)
492 if (this->i
< this->bt
->frame_count
)
494 *addr
= this->bt
->frames
[this->i
++];
500 METHOD(backtrace_t
, create_frame_enumerator
, enumerator_t
*,
501 private_backtrace_t
*this)
503 frame_enumerator_t
*enumerator
;
507 .enumerate
= (void*)_frame_enumerate
,
508 .destroy
= (void*)free
,
512 return &enumerator
->public;
515 METHOD(backtrace_t
, destroy
, void,
516 private_backtrace_t
*this)
524 backtrace_t
*backtrace_create(int skip
)
526 private_backtrace_t
*this;
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
;
538 this->public = (backtrace_t
) {
540 .contains_function
= _contains_function
,
542 .create_frame_enumerator
= _create_frame_enumerator
,
546 return &this->public;
552 void backtrace_dump(char *label
, FILE *file
, bool detailed
)
554 backtrace_t
*backtrace
;
556 backtrace
= backtrace_create(2);
560 println(file
, "Debug backtrace: %s", label
);
562 backtrace
->log(backtrace
, file
, detailed
);
563 backtrace
->destroy(backtrace
);