dect
/
libdect
Archived
13
0
Fork 0

debug: add libdect-specific assert-macro with optional backtrace support

Make assertions more useful by printing a backtrace on failed assertions.
Requires libbfd.

Signed-off-by: Patrick McHardy <kaber@trash.net>
This commit is contained in:
Patrick McHardy 2010-09-14 20:07:11 +02:00
parent 073d8f6617
commit f8bf6942ab
15 changed files with 385 additions and 24 deletions

View File

@ -1,4 +1,6 @@
DEBUG = @CONFIG_DEBUG@
CONFIG_DEBUG = @CONFIG_DEBUG@
CONFIG_BACKTRACE= @CONFIG_BACKTRACE@
CC = @CC@
CPP = @CPP@
LEX = @LEX@
@ -38,7 +40,7 @@ CFLAGS += -Wformat-nonliteral -Wformat-security -Wmissing-format-attribute
CFLAGS += -Wcast-align -Wundef -Wbad-function-cast # -Wshadow
CFLAGS += -Waggregate-return -Wunused -Wwrite-strings
ifeq ($(DEBUG),y)
ifeq ($(CONFIG_DEBUG),y)
CFLAGS += -g -DDEBUG
endif

View File

@ -77,7 +77,7 @@ $(eval $(call generic_template,$(1)))
$(SUBDIR)lib$(1).so: $$($(1)-extra-targets) $$($(1)-obj)
@/bin/echo -e " LD\t\t$$@"
$$(CC) -shared -Wl,-soname,lib$(1).so.0 $$($(1)-obj) $$(LDFLAGS) -o $$@
$$(CC) -shared -Wl,-soname,lib$(1).so.0 $$($(1)-obj) $$($(1)-ldflags) $$(LDFLAGS) -o $$@
$(LN_S) -f lib$(1).so $$@.0
all_targets += $(SUBDIR)lib$(1).so

View File

@ -47,6 +47,16 @@ AC_CHECK_LIB([ev], [event_init],
EVENT_LDFLAGS=$EVENTLIB
AC_SUBST(EVENT_LDFLAGS)
if test "$CONFIG_DEBUG" = "y";
then
AC_CHECK_LIB([bfd], [bfd_init],
[CONFIG_BACKTRACE="y"],
[CONFIG_BACKTRACE="n";
AC_MSG_NOTICE([libbfd not found, backtrace support disabled])])
else
CONFIG_BACKTRACE="n"
fi
# Checks for header files.
AC_HEADER_STDC
AC_HEADER_ASSERT
@ -66,6 +76,14 @@ AC_CHECK_HEADER([event.h],
EVENT_CFLAGS=$EVENTINC
AC_SUBST(EVENT_CFLAGS)
if test "$CONFIG_BACKTRACE" = "y";
then
AC_CHECK_HEADER([bfd.h], ,
[CONFIG_BACKTRACE="";
AC_MSG_NOTICE([bfd.h not found, backtrace support disabled])])
fi
AC_SUBST(CONFIG_BACKTRACE)
# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
AC_C_CONST

View File

@ -20,6 +20,17 @@ extern void __dect_hexdump(enum dect_debug_subsys subsys, const char *prefix,
({ if (0) __dect_hexdump(subsys, pfx, buf, size); })
#endif
extern void __dect_assert_fail(const char *assertion,
const char *file,
unsigned int line,
const char *function) __noreturn;
#define dect_assert(expr) \
((expr) ? \
(void)0 : \
__dect_assert_fail(__STRING(expr), __FILE__, __LINE__, \
__ASSERT_FUNCTION))
struct dect_trans_tbl {
uint64_t val;
const char *str;

View File

@ -21,6 +21,10 @@ dect-obj += timer.o
dect-obj += utils.o
dect-obj += raw.o
dect-obj += debug.o
ifeq ($(CONFIG_BACKTRACE),y)
dect-obj += backtrace.o
dect-ldflags += -lbfd
endif
dect-obj += ccitt-adpcm/g711.o
dect-obj += ccitt-adpcm/g72x.o

301
src/backtrace.c Normal file
View File

@ -0,0 +1,301 @@
/*
A hacky replacement for backtrace_symbols in glibc
backtrace_symbols in glibc looks up symbols using dladdr which is limited in
the symbols that it sees. libbacktracesymbols opens the executable and shared
libraries using libbfd and will look up backtrace information using the symbol
table and the dwarf line information.
It may make more sense for this program to use libelf instead of libbfd.
However, I have not investigated that yet.
Derived from addr2line.c from GNU Binutils by Jeff Muizelaar
Copyright 2007 Jeff Muizelaar
*/
/* addr2line.c -- convert addresses to line number and function name
Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
This file was part of GNU Binutils.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#define fatal(a, b) exit(1)
#define bfd_fatal(a) exit(1)
#define bfd_nonfatal(a) exit(1)
#define list_matching_formats(a) exit(1)
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#include <bfd.h>
#include <libiberty.h>
#include <dlfcn.h>
#include <link.h>
static asymbol **syms; /* Symbol table. */
/* Read in the symbol table. */
static void slurp_symtab(bfd * abfd)
{
long symcount;
unsigned int size;
if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
return;
symcount = bfd_read_minisymbols(abfd, false, (PTR) & syms, &size);
if (symcount == 0)
symcount = bfd_read_minisymbols(abfd, true /* dynamic */ ,
(PTR) &syms, &size);
if (symcount < 0)
bfd_fatal(bfd_get_filename(abfd));
}
/* These global variables are used to pass information between
translate_addresses and find_address_in_section. */
static bfd_vma pc;
static const char *filename;
static const char *functionname;
static unsigned int line;
static int found;
/* Look for an address in a section. This is called via bfd_map_over_sections. */
static void find_address_in_section(bfd *abfd, asection *section, void *data)
{
bfd_vma vma;
bfd_size_type size;
if (found)
return;
if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)
return;
vma = bfd_get_section_vma(abfd, section);
if (pc < vma)
return;
size = bfd_section_size(abfd, section);
if (pc >= vma + size)
return;
found = bfd_find_nearest_line(abfd, section, syms, pc - vma,
&filename, &functionname, &line);
}
static char **translate_addresses_buf(const char *obj_file, const void *obj_base,
bfd *abfd, bfd_vma *addr, int naddr)
{
int naddr_orig = naddr;
char b;
int total = 0;
enum { Count, Print } state;
char *buf = &b;
int len = 0;
char **ret_buf = NULL;
/* iterate over the formating twice.
* the first time we count how much space we need
* the second time we do the actual printing */
for (state = Count; state <= Print; state++) {
if (state == Print) {
ret_buf = malloc(total + sizeof(char*)*naddr);
buf = (char*)(ret_buf + naddr);
len = total;
}
while (naddr) {
if (state == Print)
ret_buf[naddr - 1] = buf;
pc = addr[naddr - 1];
found = false;
bfd_map_over_sections(abfd, find_address_in_section,
(PTR)NULL);
if (!found) {
total += snprintf(buf, len, "0x%016llx in \?\?() from %s at \?\?:0",
(long long unsigned int)obj_base + addr[naddr - 1],
obj_file) + 1;
} else {
const char *name;
name = functionname;
if (name == NULL || *name == '\0')
name = "??";
if (filename != NULL) {
char *h;
h = strrchr(filename, '/');
if (h != NULL)
filename = h + 1;
}
total += snprintf(buf, len, "0x%016llx in %s() from %s at %s:%u",
(long long unsigned int)obj_base + addr[naddr - 1],
name, obj_file,
filename ? filename : "??",
line) + 1;
}
if (state == Print) {
/* set buf just past the end of string */
buf = buf + total + 1;
}
naddr--;
}
naddr = naddr_orig;
}
return ret_buf;
}
/* Process a file. */
static char **process_file(const char *file_name, const void *base,
bfd_vma *addr, int naddr)
{
bfd *abfd;
char **matching;
char **ret_buf;
abfd = bfd_openr(file_name, NULL);
if (abfd == NULL)
bfd_fatal(file_name);
if (bfd_check_format(abfd, bfd_archive))
fatal("%s: can not get addresses from archive", file_name);
if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
bfd_nonfatal(bfd_get_filename(abfd));
if (bfd_get_error() == bfd_error_file_ambiguously_recognized) {
list_matching_formats(matching);
free(matching);
}
xexit(1);
}
slurp_symtab(abfd);
ret_buf = translate_addresses_buf(file_name, base, abfd, addr, naddr);
if (syms != NULL) {
free(syms);
syms = NULL;
}
bfd_close(abfd);
return ret_buf;
}
#define MAX_DEPTH 16
struct file_match {
const char *file;
void *address;
void *base;
void *hdr;
};
static int find_matching_file(struct dl_phdr_info *info,
size_t size, void *data)
{
struct file_match *match = data;
const ElfW(Phdr) *phdr;
long n;
ElfW(Addr) load_base = info->dlpi_addr;
phdr = info->dlpi_phdr;
for (n = info->dlpi_phnum; --n >= 0; phdr++) {
if (phdr->p_type == PT_LOAD) {
ElfW(Addr) vaddr = phdr->p_vaddr + load_base;
if (match->address >= (void *)vaddr &&
match->address < (void *)vaddr + phdr->p_memsz) {
/* we found a match */
match->file = info->dlpi_name;
match->base = (void *)info->dlpi_addr;
}
}
}
return 0;
}
char **backtrace_symbols(void *const *buffer, int size)
{
int stack_depth = size - 1;
int x,y;
/* discard calling function */
int total = 0;
char ***locations;
char **final;
char *f_strings;
locations = malloc(sizeof(char **) * (stack_depth + 1));
bfd_init();
for (x = stack_depth, y = 0; x >= 0; x--, y++){
struct file_match match = { .address = buffer[x] };
char **ret_buf;
bfd_vma addr;
dl_iterate_phdr(find_matching_file, &match);
addr = buffer[x] - match.base;
if (match.file && strlen(match.file))
ret_buf = process_file(match.file, match.base, &addr, 1);
else
ret_buf = process_file("/proc/self/exe", match.base, &addr, 1);
locations[x] = ret_buf;
total += strlen(ret_buf[0]) + 1;
}
/* allocate the array of char * we are going to return and extra space
* for all of the strings */
final = malloc(total + (stack_depth + 1) * sizeof(char *));
/* get a pointer to the extra space */
f_strings = (char *)(final + stack_depth + 1);
/* fill in all of strings and pointers */
for (x = stack_depth; x >= 0; x--){
strcpy(f_strings, locations[x][0]);
free(locations[x]);
final[x] = f_strings;
f_strings += strlen(f_strings) + 1;
}
free(locations);
return final;
}
void backtrace_symbols_fd(void * const *buffer, int size, int fd)
{
int j;
char **strings;
strings = backtrace_symbols(buffer, size);
if (strings == NULL) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
for (j = 0; j < size; j++)
printf("%s\n", strings[j]);
free(strings);
}

View File

@ -58,7 +58,7 @@ void dect_clms_rcv_fixed(struct dect_handle *dh, struct dect_msg_buf *mb)
unsigned int n, len, section, sections;
clms_debug("parse {CLMS-FIXED} message");
assert(mb->len % 5 == 0);
dect_assert(mb->len % 5 == 0);
as = (void *)mb->data;
if ((as->hdr & DECT_CLMS_SECTION_TYPE_MASK) != DECT_CLMS_SECTION_ADDR)

View File

@ -14,6 +14,8 @@
#include <inttypes.h>
#include <unistd.h>
#include <ctype.h>
#include <execinfo.h>
#include <bfd.h>
#include <libdect.h>
#include <utils.h>
@ -121,6 +123,29 @@ const char *__dect_val2str(const struct dect_trans_tbl *tbl, unsigned int nelem,
snprintf(buf, size, "unknown (%" PRIx64 ")", val);
return buf;
}
static void dect_backtrace(void)
{
void *buf[100];
char **strings;
unsigned int size, i;
size = backtrace(buf, array_size(buf));
strings = backtrace_symbols(buf, size);
for (i = 0; i < size; i++)
dect_debug(0, "#%-2u %s\n", i, strings[i]);
free(strings);
}
void __dect_assert_fail(const char *assertion, const char *file,
unsigned int line, const char *function)
{
dect_debug(0, "%s:%d: %s: assertion `%s' failed.\n",
file, line, function, assertion);
dect_backtrace();
abort();
}
#endif
/** @} */

View File

@ -69,7 +69,7 @@ struct dect_ie_common *__dect_ie_hold(struct dect_ie_common *ie)
if (ie == NULL)
return NULL;
refcnt_debug("IE %p: hold refcnt=%u\n", ie, ie->refcnt);
assert(ie->refcnt != 0);
dect_assert(ie->refcnt != 0);
ie->refcnt++;
return ie;
}
@ -80,7 +80,7 @@ void __dect_ie_put(const struct dect_handle *dh, struct dect_ie_common *ie)
if (ie == NULL)
return;
refcnt_debug("IE %p: release refcnt=%u\n", ie, ie->refcnt);
assert(ie->refcnt != 0);
dect_assert(ie->refcnt != 0);
if (--ie->refcnt == 0)
dect_ie_destroy(dh, ie);
}
@ -180,7 +180,7 @@ EXPORT_SYMBOL(dect_ie_collection_free);
void __dect_ie_collection_put(const struct dect_handle *dh, struct dect_ie_collection *iec)
{
assert(iec->refcnt != 0);
dect_assert(iec->refcnt != 0);
if (--iec->refcnt > 0)
return;
dect_ie_collection_free(dh, iec);
@ -189,7 +189,7 @@ EXPORT_SYMBOL(__dect_ie_collection_put);
struct dect_ie_collection *__dect_ie_collection_hold(struct dect_ie_collection *iec)
{
assert(iec->refcnt != 0);
dect_assert(iec->refcnt != 0);
iec->refcnt++;
return iec;
}

View File

@ -99,7 +99,7 @@ int dect_fd_register(const struct dect_handle *dh, struct dect_fd *dfd,
{
int err;
assert(dfd->state == DECT_FD_UNREGISTERED);
dect_assert(dfd->state == DECT_FD_UNREGISTERED);
err = dh->ops->event_ops->register_fd(dh, dfd, events);
if (err == 0)
dfd->state = DECT_FD_REGISTERED;
@ -109,7 +109,7 @@ EXPORT_SYMBOL(dect_fd_register);
void dect_fd_unregister(const struct dect_handle *dh, struct dect_fd *dfd)
{
assert(dfd->state == DECT_FD_REGISTERED);
dect_assert(dfd->state == DECT_FD_REGISTERED);
dh->ops->event_ops->unregister_fd(dh, dfd);
dfd->state = DECT_FD_UNREGISTERED;
}
@ -127,14 +127,14 @@ EXPORT_SYMBOL(dect_fd_unregister);
*/
void dect_fd_process(struct dect_handle *dh, struct dect_fd *dfd, uint32_t events)
{
assert(dfd->state == DECT_FD_REGISTERED);
dect_assert(dfd->state == DECT_FD_REGISTERED);
dfd->callback(dh, dfd, events);
}
EXPORT_SYMBOL(dect_fd_process);
void dect_close(const struct dect_handle *dh, struct dect_fd *dfd)
{
assert(dfd->state == DECT_FD_UNREGISTERED);
dect_assert(dfd->state == DECT_FD_UNREGISTERED);
if (dfd->fd >= 0)
close(dfd->fd);
dect_free(dh, dfd);

View File

@ -260,7 +260,7 @@ static void dect_ddl_destroy(struct dect_handle *dh, struct dect_data_link *ddl)
struct dect_msg_buf *mb, *next;
ddl_debug(ddl, "destroy");
assert(list_empty(&ddl->transactions));
dect_assert(list_empty(&ddl->transactions));
list_del(&ddl->list);
list_for_each_entry_safe(mb, next, &ddl->msg_queue, list)
@ -835,7 +835,7 @@ ssize_t dect_lce_broadcast(const struct dect_handle *dh,
dect_mbuf_dump(DECT_DEBUG_LCE, mb, "LCE: BCAST TX");
size = dect_mbuf_send(dh, dh->b_sap, &msg, mb);
assert(size == (ssize_t)mb->len);
dect_assert(size == (ssize_t)mb->len);
return 0;
}
@ -1402,7 +1402,7 @@ void dect_transaction_get_ulei(struct sockaddr_dect_lu *addr,
static void dect_pp_set_default_pmid(struct dect_handle *dh)
{
assert(!(dh->flags & DECT_PP_TPUI));
dect_assert(!(dh->flags & DECT_PP_TPUI));
dh->pmid = DECT_PMID_DEFAULT_ID +
(rand() & DECT_PMID_DEFAULT_NUM_MASK);
lce_debug("set default pmid %05x\n", dh->pmid);
@ -1419,7 +1419,7 @@ static void dect_pp_set_assigned_pmid(struct dect_handle *dh)
{
struct dect_pmid pmid;
assert(dh->flags & DECT_PP_TPUI &&
dect_assert(dh->flags & DECT_PP_TPUI &&
dh->tpui.type == DECT_TPUI_INDIVIDUAL_ASSIGNED);
dh->pmid = dect_build_pmid(dect_tpui_to_pmid(&pmid, &dh->tpui));
lce_debug("set assigned pmid %05x\n", dh->pmid);

View File

@ -2505,7 +2505,7 @@ int dect_mm_identity_assign_res(struct dect_handle *dh,
if (accept) {
struct dect_mm_locate_param *lp = (struct dect_mm_locate_param *)mp->iec;
assert(lp->portable_identity->type == DECT_PORTABLE_ID_TYPE_TPUI);
dect_assert(lp->portable_identity->type == DECT_PORTABLE_ID_TYPE_TPUI);
dect_pp_set_tpui(dh, &lp->portable_identity->tpui);
err = dect_mm_send_temporary_identity_assign_ack(dh, mme, param);
} else

View File

@ -98,7 +98,7 @@ static void dect_raw_event(struct dect_handle *dh, struct dect_fd *dfd,
struct iovec iov;
ssize_t len;
assert(!(events & ~DECT_FD_READ));
dect_assert(!(events & ~DECT_FD_READ));
msg.msg_name = NULL;
msg.msg_namelen = 0;

View File

@ -2464,7 +2464,7 @@ static int dect_build_sfmt_ie_header(struct dect_sfmt_ie *dst, uint8_t id)
if (dst->len == 2)
dst->len = 0;
else {
assert(dst->len > 2);
dect_assert(dst->len > 2);
dst->data[1] = dst->len - 2;
dst->data[0] = id;
}
@ -2693,7 +2693,7 @@ enum dect_sfmt_error dect_build_sfmt_msg(const struct dect_handle *dh,
}
desc++;
assert(desc->flags & DECT_SFMT_IE_REPEAT);
dect_assert(desc->flags & DECT_SFMT_IE_REPEAT);
dect_foreach_ie(rsrc, iel) {
err = __dect_build_sfmt_ie(dh, desc, mb, rsrc);
if (err != DECT_SFMT_OK)

View File

@ -52,7 +52,7 @@ EXPORT_SYMBOL(dect_timer_alloc);
void dect_timer_free(const struct dect_handle *dh, struct dect_timer *timer)
{
if (timer != NULL)
assert(timer->state == DECT_TIMER_STOPPED);
dect_assert(timer->state == DECT_TIMER_STOPPED);
dect_free(dh, timer);
}
EXPORT_SYMBOL(dect_timer_free);
@ -72,7 +72,7 @@ void dect_timer_setup(struct dect_timer *timer,
void (*cb)(struct dect_handle *, struct dect_timer *),
void *data)
{
assert(timer->state == DECT_TIMER_STOPPED);
dect_assert(timer->state == DECT_TIMER_STOPPED);
timer->callback = cb;
timer->data = data;
}
@ -96,7 +96,7 @@ EXPORT_SYMBOL(dect_timer_start);
void dect_timer_stop(const struct dect_handle *dh, struct dect_timer *timer)
{
assert(timer->state == DECT_TIMER_RUNNING);
dect_assert(timer->state == DECT_TIMER_RUNNING);
dh->ops->event_ops->stop_timer(dh, timer);
timer->state = DECT_TIMER_STOPPED;
}
@ -116,7 +116,7 @@ EXPORT_SYMBOL(dect_timer_running);
*/
void dect_timer_run(struct dect_handle *dh, struct dect_timer *timer)
{
assert(timer->state == DECT_TIMER_RUNNING);
dect_assert(timer->state == DECT_TIMER_RUNNING);
timer->state = DECT_TIMER_STOPPED;
timer->callback(dh, timer);
}