diff --git a/Makefile.defs.in b/Makefile.defs.in index af20d76..f04ceea 100644 --- a/Makefile.defs.in +++ b/Makefile.defs.in @@ -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 diff --git a/Makefile.rules.in b/Makefile.rules.in index a2dc5ec..411f47b 100644 --- a/Makefile.rules.in +++ b/Makefile.rules.in @@ -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 diff --git a/configure.ac b/configure.ac index 8a7d814..d17dda0 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/include/debug.h b/include/debug.h index 3a9d86e..6cfb43e 100644 --- a/include/debug.h +++ b/include/debug.h @@ -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; diff --git a/src/Makefile.in b/src/Makefile.in index efbc8c4..07cad33 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -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 diff --git a/src/backtrace.c b/src/backtrace.c new file mode 100644 index 0000000..19ce3da --- /dev/null +++ b/src/backtrace.c @@ -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 + + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/src/clms.c b/src/clms.c index 9621121..1c716cc 100644 --- a/src/clms.c +++ b/src/clms.c @@ -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) diff --git a/src/debug.c b/src/debug.c index 5615dba..96c8b42 100644 --- a/src/debug.c +++ b/src/debug.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -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 /** @} */ diff --git a/src/ie.c b/src/ie.c index 0580050..359114a 100644 --- a/src/ie.c +++ b/src/ie.c @@ -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; } diff --git a/src/io.c b/src/io.c index 37fb452..15f60e4 100644 --- a/src/io.c +++ b/src/io.c @@ -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); diff --git a/src/lce.c b/src/lce.c index 2221aa4..f937222 100644 --- a/src/lce.c +++ b/src/lce.c @@ -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); diff --git a/src/mm.c b/src/mm.c index 97696e1..f358fa8 100644 --- a/src/mm.c +++ b/src/mm.c @@ -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 diff --git a/src/raw.c b/src/raw.c index 84c6266..76ef2a0 100644 --- a/src/raw.c +++ b/src/raw.c @@ -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; diff --git a/src/s_msg.c b/src/s_msg.c index fb5e905..8c41cbe 100644 --- a/src/s_msg.c +++ b/src/s_msg.c @@ -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) diff --git a/src/timer.c b/src/timer.c index a42e8fe..b1ad429 100644 --- a/src/timer.c +++ b/src/timer.c @@ -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); }