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 * Same as tty_escape_get(), but for a potentially NULL FILE*
58 static char* esc(FILE *file
, tty_escape_t escape
)
62 return tty_escape_get(fileno(file
), escape
);
68 * Write a format string with arguments to a FILE line, if it is NULL to DBG
70 static void println(FILE *file
, char *format
, ...)
75 va_start(args
, format
);
78 vfprintf(file
, format
, args
);
83 vsnprintf(buf
, sizeof(buf
), format
, args
);
84 DBG1(DBG_LIB
, "%s", buf
);
93 #include <collections/hashtable.h>
94 #include <threading/mutex.h>
97 * Hashtable-cached bfd handle
100 /** binary file name on disk */
104 /** loaded symbols */
109 * Destroy a bfd_entry
111 static void bfd_entry_destroy(bfd_entry_t
*this)
113 free(this->filename
);
115 bfd_close(this->abfd
);
120 * Data to pass to find_addr()
123 /** used bfd entry */
125 /** backtrace address */
127 /** stream to log to */
129 /** TRUE if complete */
136 static hashtable_t
*bfds
;
138 static mutex_t
*bfd_mutex
;
141 * Hashtable hash function
143 static u_int
bfd_hash(char *key
)
145 return chunk_hash(chunk_create(key
, strlen(key
)));
149 * Hashtable equals function
151 static bool bfd_equals(char *a
, char *b
)
159 void backtrace_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
);
170 void backtrace_deinit()
172 enumerator_t
*enumerator
;
176 enumerator
= bfds
->create_enumerator(bfds
);
177 while (enumerator
->enumerate(enumerator
, &key
, &entry
))
179 bfds
->remove_at(bfds
, enumerator
);
180 bfd_entry_destroy(entry
);
182 enumerator
->destroy(enumerator
);
185 bfd_mutex
->destroy(bfd_mutex
);
189 * Find and print information to an address
191 static void find_addr(bfd
*abfd
, asection
*section
, bfd_find_data_t
*data
)
196 const char *function
;
197 char fbuf
[512] = "", sbuf
[512] = "";
200 if (!data
->found
|| (bfd_get_section_flags(abfd
, section
) & SEC_ALLOC
) != 0)
202 vma
= bfd_get_section_vma(abfd
, section
);
203 if (data
->vma
>= vma
)
205 size
= bfd_get_section_size(section
);
206 if (data
->vma
< vma
+ size
)
208 data
->found
= bfd_find_nearest_line(abfd
, section
,
209 data
->entry
->syms
, data
->vma
- vma
,
210 &source
, &function
, &line
);
213 if (source
|| function
)
217 snprintf(fbuf
, sizeof(fbuf
), "%s%s() ",
218 esc(data
->file
, TTY_FG_BLUE
), function
);
222 snprintf(sbuf
, sizeof(sbuf
), "%s@ %s:%d",
223 esc(data
->file
, TTY_FG_GREEN
), source
, line
);
225 println(data
->file
, " -> %s%s%s", fbuf
, sbuf
,
226 esc(data
->file
, TTY_FG_DEF
));
235 * Find a cached bfd entry, create'n'cache if not found
237 static bfd_entry_t
*get_bfd_entry(char *filename
)
239 bool dynamic
= FALSE
, ok
= FALSE
;
244 entry
= bfds
->get(bfds
, filename
);
251 .abfd
= bfd_openr(filename
, NULL
),
259 #ifdef BFD_DECOMPRESS
260 entry
->abfd
->flags
|= BFD_DECOMPRESS
;
262 if (bfd_check_format(entry
->abfd
, bfd_archive
) == 0 &&
263 bfd_check_format_matches(entry
->abfd
, bfd_object
, NULL
))
265 if (bfd_get_file_flags(entry
->abfd
) & HAS_SYMS
)
267 size
= bfd_get_symtab_upper_bound(entry
->abfd
);
270 size
= bfd_get_dynamic_symtab_upper_bound(entry
->abfd
);
274 entry
->syms
= malloc(size
);
277 ok
= bfd_canonicalize_dynamic_symtab(entry
->abfd
,
282 ok
= bfd_canonicalize_symtab(entry
->abfd
,
290 entry
->filename
= strdup(filename
);
291 bfds
->put(bfds
, entry
->filename
, entry
);
294 bfd_entry_destroy(entry
);
299 * Print the source file with line number to file, libbfd variant
301 static void print_sourceline(FILE *file
, char *filename
, void *ptr
)
304 bfd_find_data_t data
= {
306 .vma
= (uintptr_t)ptr
,
310 bfd_mutex
->lock(bfd_mutex
);
311 if (lib
->leak_detective
)
313 old
= lib
->leak_detective
->set_state(lib
->leak_detective
, FALSE
);
315 entry
= get_bfd_entry(filename
);
319 bfd_map_over_sections(entry
->abfd
, (void*)find_addr
, &data
);
321 if (lib
->leak_detective
)
323 lib
->leak_detective
->set_state(lib
->leak_detective
, old
);
325 bfd_mutex
->unlock(bfd_mutex
);
328 #else /* !HAVE_BFD_H */
330 void backtrace_init() {}
331 void backtrace_deinit() {}
334 * Print the source file with line number to file, slow addr2line variant
336 static void print_sourceline(FILE *file
, char *filename
, void *ptr
)
342 snprintf(buf
, sizeof(buf
), "addr2line -e %s %p", filename
, ptr
);
343 output
= popen(buf
, "r");
346 while (i
< sizeof(buf
))
349 if (c
== '\n' || c
== EOF
)
358 println(file
, " -> %s%s%s", esc(file
, TTY_FG_GREEN
), buf
,
359 esc(file
, TTY_FG_DEF
));
363 #endif /* HAVE_BFD_H */
365 #else /* !HAVE_DLADDR */
367 void backtrace_init() {}
368 void backtrace_deinit() {}
370 #endif /* HAVE_DLADDR */
372 METHOD(backtrace_t
, log_
, void,
373 private_backtrace_t
*this, FILE *file
, bool detailed
)
375 #ifdef HAVE_BACKTRACE
379 strings
= backtrace_symbols(this->frames
, this->frame_count
);
381 println(file
, " dumping %d stack frame addresses:", this->frame_count
);
382 for (i
= 0; i
< this->frame_count
; i
++)
387 if (dladdr(this->frames
[i
], &info
))
389 void *ptr
= this->frames
[i
];
391 if (strstr(info
.dli_fname
, ".so"))
393 ptr
= (void*)(this->frames
[i
] - info
.dli_fbase
);
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
,
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
]);
410 if (detailed
&& info
.dli_fname
[0])
412 print_sourceline(file
, (char*)info
.dli_fname
, ptr
);
416 #endif /* HAVE_DLADDR */
418 println(file
, " %s", strings
[i
]);
422 #else /* !HAVE_BACKTRACE */
423 println(file
, "C library does not support backtrace().");
424 #endif /* HAVE_BACKTRACE */
427 METHOD(backtrace_t
, contains_function
, bool,
428 private_backtrace_t
*this, char *function
[], int count
)
433 for (i
= 0; i
< this->frame_count
; i
++)
437 if (dladdr(this->frames
[i
], &info
) && info
.dli_sname
)
439 for (j
= 0; j
< count
; j
++)
441 if (streq(info
.dli_sname
, function
[j
]))
448 #endif /* HAVE_DLADDR */
452 METHOD(backtrace_t
, equals
, bool,
453 private_backtrace_t
*this, backtrace_t
*other_public
)
455 private_backtrace_t
*other
= (private_backtrace_t
*)other_public
;
462 if (this->frame_count
!= other
->frame_count
)
466 for (i
= 0; i
< this->frame_count
; i
++)
468 if (this->frames
[i
] != other
->frames
[i
])
480 /** implements enumerator_t */
482 /** reference to backtrace */
483 private_backtrace_t
*bt
;
484 /** current position */
486 } frame_enumerator_t
;
488 METHOD(enumerator_t
, frame_enumerate
, bool,
489 frame_enumerator_t
*this, void **addr
)
491 if (this->i
< this->bt
->frame_count
)
493 *addr
= this->bt
->frames
[this->i
++];
499 METHOD(backtrace_t
, create_frame_enumerator
, enumerator_t
*,
500 private_backtrace_t
*this)
502 frame_enumerator_t
*enumerator
;
506 .enumerate
= (void*)_frame_enumerate
,
507 .destroy
= (void*)free
,
511 return &enumerator
->public;
514 METHOD(backtrace_t
, destroy
, void,
515 private_backtrace_t
*this)
523 backtrace_t
*backtrace_create(int skip
)
525 private_backtrace_t
*this;
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
;
537 this->public = (backtrace_t
) {
539 .contains_function
= _contains_function
,
541 .create_frame_enumerator
= _create_frame_enumerator
,
545 return &this->public;
551 void backtrace_dump(char *label
, FILE *file
, bool detailed
)
553 backtrace_t
*backtrace
;
555 backtrace
= backtrace_create(2);
559 println(file
, "Debug backtrace: %s", label
);
561 backtrace
->log(backtrace
, file
, detailed
);
562 backtrace
->destroy(backtrace
);