diff options
author | Jiri Olsa <Jiri Olsa jolsa@redhat.com> | 2011-10-18 09:10:05 +0200 |
---|---|---|
committer | Jiri Olsa <Jiri Olsa jolsa@redhat.com> | 2011-11-24 21:20:27 +0100 |
commit | fa668f3b96fd0363bae1adc640105b3914faa138 (patch) | |
tree | 25148826f7a96db52d9e806def1e1a409be0fddd | |
parent | c6bb0e49bed67c0cd53cd725f5efa09e6e94df56 (diff) | |
download | latrace-fa668f3b96fd0363bae1adc640105b3914faa138.tar.gz latrace-fa668f3b96fd0363bae1adc640105b3914faa138.tar.xz latrace-fa668f3b96fd0363bae1adc640105b3914faa138.zip |
error simulation: added support to display backtrace info
-rw-r--r-- | configure.ac | 29 | ||||
-rw-r--r-- | src/Makefile | 18 | ||||
-rw-r--r-- | src/audit-error.c | 51 | ||||
-rw-r--r-- | src/audit.c | 26 | ||||
-rw-r--r-- | src/autoconf.h.in | 6 | ||||
-rw-r--r-- | src/autoconf.make.in | 3 | ||||
-rw-r--r-- | src/backtrace-fp.c | 10 | ||||
-rw-r--r-- | src/backtrace-lunw.c | 76 | ||||
-rw-r--r-- | src/backtrace.c | 390 | ||||
-rw-r--r-- | src/backtrace.h | 46 | ||||
-rw-r--r-- | src/config.h | 4 | ||||
-rw-r--r-- | src/run.c | 4 | ||||
-rw-r--r-- | src/sysdeps/i686/error.c | 7 | ||||
-rw-r--r-- | src/sysdeps/x86_64/error.c | 7 |
14 files changed, 643 insertions, 34 deletions
diff --git a/configure.ac b/configure.ac index aa80b3a..7ac3f17 100644 --- a/configure.ac +++ b/configure.ac @@ -112,8 +112,35 @@ else AC_MSG_WARN([Error simulation support disabled]) fi +# check for libunwind, haven't found any generic function, +# so need to set one based on architecture +unwind_func="krava" +if test "$unamem" = "x86_64"; then + unwind_func="_Ux86_64_init_local" +fi +if test "$unamem" = "i686"; then + unwind_func="_Ux86_init_local" +fi + +AC_SEARCH_LIBS([$unwind_func], [unwind-generic], + [ + AC_DEFINE(CONFIG_LIBUNWIND, 1, "Unwind library found.") + AC_SUBST(CONFIG_LIBUNWIND, "y") + ], + [AC_MSG_WARN([libunwind not found, no backtrace support (install libunwind-dev)])]) + +AC_SEARCH_LIBS([elf_begin], [elf], + [ + AC_DEFINE(CONFIG_LIBELF, 1, "Elf library found.") + AC_SUBST(CONFIG_LIBELF, "y") + ], + [AC_MSG_WARN([libelf not found, no symbol support (install libelf-dev)])]) + AC_SEARCH_LIBS([cplus_demangle], [iberty_pic iberty], - [AC_DEFINE(CONFIG_LIBERTY, 1, "Liberty found.")], + [ + AC_DEFINE(CONFIG_LIBERTY, 1, "Liberty library found.") + AC_SUBST(CONFIG_LIBERTY, "y") + ], [AC_MSG_WARN([libiberty not found, no demangle support (install binutils-dev)])]) AC_CONFIG_HEADER([src/autoconf.h]) diff --git a/src/Makefile b/src/Makefile index e009358..181ab8e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -40,12 +40,18 @@ AUDIT_OBJS+=\ src/sysdeps/$(CONFIG_SYSDEP_DIR)/stack.o endif -# TODO make this for all error simulation objects ifeq ($(CONFIG_ARCH_HAVE_ERROR_SIM),y) AUDIT_OBJS+=\ src/sysdeps/$(CONFIG_SYSDEP_DIR)/error.o \ - src/audit-error.o -endif + src/audit-error.o \ + src/backtrace.o +ifeq ($(CONFIG_LIBUNWIND),y) +AUDIT_OBJS+=\ + src/backtrace-lunw.o +else + src/backtrace-fp.o +endif # CONFIG_LIBUNWIND +endif # CONFIG_ARCH_HAVE_ERROR_SIM OBJS+=$(AUDIT_OBJS) PROGRAMS+= $(AUDIT_BIN) @@ -63,7 +69,11 @@ install:: # latrace binary LATRACE_BIN=latrace LATRACE_CTL=latrace-ctl -LATRACE_LIB= $(LIBS) +LATRACE_LIB= + +ifeq ($(CONFIG_LIBERTY),y) +LATRACE_LIB+= -liberty +endif LATRACE_OBJS=\ src/latrace.o \ diff --git a/src/audit-error.c b/src/audit-error.c index 6a9bc50..428d41e 100644 --- a/src/audit-error.c +++ b/src/audit-error.c @@ -8,6 +8,7 @@ #include <string.h> #include "config.h" +#include "backtrace.h" int lt_audit_parse_init(struct lt_config_shared *cfg); int lt_audit_parse(); @@ -43,14 +44,43 @@ struct lt_error_sym* lt_error_sym_get(struct lt_config_shared *cfg, extern __thread int pipe_fd; +static void share_info(struct lt_config_audit *cfg, + struct lt_error_sym *esym, + struct timeval *tv, + struct link_map *lr, + const La_regs *inregs) +{ + void *ret_addr; +#define LT_MAXMSG 100 + char text[LT_MAXMSG]; + + lt_error_get_retaddr(cfg, &ret_addr, inregs); + ret_addr -= lr->l_addr; + + snprintf(text, LT_MAXMSG, "ERROR SIMULATION inserted %ld, ret %p", + esym->ret, ret_addr); + + if (lt_sh(cfg, pipe)) { + char buf[LT_FIFO_MSG_MAXLEN]; + int len; + + len = lt_fifo_mtext_get(cfg->sh, buf, tv, text); + /* TODO erro handling */ + lt_fifo_send(cfg->sh, pipe_fd, buf, len); + } else + lt_out_text(cfg->sh, tv, syscall(SYS_gettid), text); + + dump_backtrace(cfg->sh, 0); +} + int lt_error_sym_exit(struct lt_config_audit *cfg, struct lt_symbol *sym, struct timeval *tv, + struct link_map *lr, + const La_regs *inregs, La_retval *outregs) { struct lt_error_sym *esym; -#define LT_MAXMSG 100 - char text[LT_MAXMSG]; /* we're not interested in this symbol */ if (!sym || !sym->error) @@ -69,22 +99,11 @@ int lt_error_sym_exit(struct lt_config_audit *cfg, esym->ret); /* The fun begins right now.. let's change the return - * value for the symbol */ - + * value for the symbol ... */ lt_error_set_retval(cfg, esym->ret, outregs); - snprintf(text, LT_MAXMSG, "ERROR SIMULATION inserted %ld", - esym->ret); - - if (lt_sh(cfg, pipe)) { - char buf[LT_FIFO_MSG_MAXLEN]; - int len; - - len = lt_fifo_mtext_get(cfg, buf, tv, text); - return lt_fifo_send(cfg, pipe_fd, buf, len); - } - - lt_out_text(cfg->sh, tv, syscall(SYS_gettid), text); + /* and share some info about the sinner */ + share_info(cfg, esym, tv, lr, inregs); return 0; } diff --git a/src/audit.c b/src/audit.c index 3adfa3e..7a83b56 100644 --- a/src/audit.c +++ b/src/audit.c @@ -81,9 +81,11 @@ static void free_argbuf(int argret, char *argbuf, char *argdbuf) } static int sym_entry(const char *symname, void *ptr, - char *lib_from, char *lib_to, La_regs *regs) + struct link_map *lr, struct link_map *ld, + La_regs *regs) { - int argret = -1; + char *lib_to = ld ? ld->l_name : NULL; + int argret = -1; char *argbuf = "", *argdbuf = ""; struct timeval tv; struct lt_symbol *sym = NULL; @@ -130,10 +132,12 @@ static int sym_entry(const char *symname, void *ptr, } static int sym_exit(const char *symname, void *ptr, - char *lib_from, char *lib_to, - const La_regs *inregs, La_retval *outregs) + struct link_map *lr, struct link_map *ld, + const La_regs *inregs, La_retval *outregs) { - int argret = -1; + char *lib_from = lr ? lr->l_name : NULL; + char *lib_to = ld ? ld->l_name : NULL; + int argret = -1; char *argbuf = "", *argdbuf = ""; struct timeval tv; struct lt_symbol *sym = NULL; @@ -149,8 +153,10 @@ static int sym_exit(const char *symname, void *ptr, if (lt_sh(&cfg, global_symbols)) sym = lt_symbol_get(cfg.sh, ptr, symname); +#ifdef CONFIG_ARCH_HAVE_ERROR_SIM if (lt_sh(&cfg, error_sim)) - lt_error_sym_exit(&cfg, sym, &tv, outregs); + lt_error_sym_exit(&cfg, sym, &tv, lr, inregs, outregs); +#endif #ifdef CONFIG_ARCH_HAVE_ARGS argret = lt_sh(&cfg, args_enabled) ? @@ -329,9 +335,7 @@ pltenter(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook, CHECK_PID(sym->st_value); sym_entry(symname, (void*) sym->st_value, - lr ? lr->l_name : NULL, - ld ? ld->l_name : NULL, - regs); + lr, ld, regs); } while(0); @@ -351,9 +355,7 @@ unsigned int pltexit(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook, CHECK_PID(0); sym_exit(symname, (void*) sym->st_value, - lr ? lr->l_name : NULL, - ld ? ld->l_name : NULL, - inregs, outregs); + lr, ld, inregs, outregs); } while(0); diff --git a/src/autoconf.h.in b/src/autoconf.h.in index d2f53fb..4b77ab6 100644 --- a/src/autoconf.h.in +++ b/src/autoconf.h.in @@ -36,6 +36,12 @@ /* liberty */ #undef CONFIG_LIBERTY +/* libunwind */ +#undef CONFIG_LIBUNWIND + +/* libelf */ +#undef CONFIG_LIBELF + /* large file support */ #undef _LARGE_FILES #undef _FILE_OFFSET_BITS diff --git a/src/autoconf.make.in b/src/autoconf.make.in index 9655f4a..dac8a8b 100644 --- a/src/autoconf.make.in +++ b/src/autoconf.make.in @@ -47,5 +47,8 @@ CONFIG_SYSDEP_DIR = @CONFIG_SYSDEP_DIR@ CONFIG_VERSION = @CONFIG_VERSION@ CONFIG_ARCH_HAVE_ARGS = @CONFIG_ARCH_HAVE_ARGS@ CONFIG_ARCH_HAVE_TEST = @CONFIG_ARCH_HAVE_TEST@ +CONFIG_LIBUNWIND = @CONFIG_LIBUNWIND@ +CONFIG_LIBELF = @CONFIG_LIBELF@ +CONFIG_LIBERTY = @CONFIG_LIBERTY@ CONFIG_ARCH_HAVE_ERROR_SIM = @CONFIG_ARCH_HAVE_ERROR_SIM@ diff --git a/src/backtrace-fp.c b/src/backtrace-fp.c new file mode 100644 index 0000000..1c70dfc --- /dev/null +++ b/src/backtrace-fp.c @@ -0,0 +1,10 @@ + +#include "config.h" +#include "backtrace.h" + +int bt_dump(struct lt_config_shared *cfg, unsigned long flags) +{ + fprintf(stderr, "NOT IMPLEMENTED\n"); + fflush(NULL); + return -1; +} diff --git a/src/backtrace-lunw.c b/src/backtrace-lunw.c new file mode 100644 index 0000000..eb29467 --- /dev/null +++ b/src/backtrace-lunw.c @@ -0,0 +1,76 @@ +#include <libunwind.h> + +#include "config.h" +#include "backtrace.h" + +#define LT_MAXMSG 300 + +int bt_dump(struct lt_config_shared *cfg, unsigned long flags) +{ + unw_cursor_t cursor; + unw_context_t uc; + int ret = 0; + + unw_getcontext(&uc); + if (unw_init_local(&cursor, &uc) < 0) { + bt_display(cfg, "unw_init_local failed"); + return -1; + } + + bt_display(cfg, "backtrace:"); + + do { + char text[LT_MAXMSG]; + struct bt_map *map; + bt_word_t rel_ip = 0, ip = 0, sp; + bt_word_t sym_offset = 0; + bt_word_t sym_size= 0; + char *map_name = NULL; + char *sym_name = NULL; + struct bt_symbol *symbol = NULL; + int size = sizeof(bt_word_t) * 2; + int symbol_found = 0; + + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + + if (bt_find_map(cfg, &map, ip)) { + rel_ip = ip - map->base; + map_name = map->name; + + if (bt_find_symbol(cfg, &symbol, map, rel_ip)) { + sym_name = symbol->name; + sym_offset = rel_ip - symbol->start; + sym_size = symbol->end - symbol->start; + symbol_found = 1; + } + } + + if (symbol_found) + snprintf(text, LT_MAXMSG, + "\t<0x%0*zx> %s+0x%zx/0x%zx [%s]", + size, rel_ip, + sym_name, + sym_offset, + sym_size, + map_name); + else + snprintf(text, LT_MAXMSG, + "\t<0x%0*zx> [%s]", + size, rel_ip, + map_name); + + bt_display(cfg, text); + + ret = unw_step(&cursor); + if (ret < 0) { + unw_get_reg(&cursor, UNW_REG_IP, &ip); + snprintf(text, LT_MAXMSG, + "unw_step failed for ip=%zx\n", + ip); + bt_display(cfg, text); + } + } while (ret > 0); + + return ret; +} diff --git a/src/backtrace.c b/src/backtrace.c new file mode 100644 index 0000000..fb9c060 --- /dev/null +++ b/src/backtrace.c @@ -0,0 +1,390 @@ +#include <link.h> +#include <elf.h> +#include <gelf.h> +#include <libelf.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "config.h" +#include "backtrace.h" + +extern __thread int pipe_fd; + +static struct bt_map* maps; + +#ifdef CONFIG_LIBELF +static void symbols_destroy(struct bt_map *map) +{ + struct bt_symbol* next = map->symbols; + + do { + struct bt_symbol *symbol = next; + next = symbol->next; + free(symbol->name); + free(symbol); + } while(next); +} + +static Elf_Scn *get_section(Elf *elf, GElf_Ehdr *ep, + GElf_Shdr *shp, const char *name, + size_t *idx) +{ + Elf_Scn *sec = NULL; + size_t cnt = 1; + + while ((sec = elf_nextscn(elf, sec)) != NULL) { + char *str; + + gelf_getshdr(sec, shp); + str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); + if (!strcmp(name, str)) { + if (idx) + *idx = cnt; + break; + } + ++cnt; + } + + return sec; +} + +static int sym_type(GElf_Sym *sym) +{ + return GELF_ST_TYPE(sym->st_info); +} + +static int sym_is_function(GElf_Sym *sym) +{ + return sym_type(sym) == STT_FUNC && + sym->st_name != 0 && + sym->st_shndx != SHN_UNDEF; +} + +static int sym_is_label(GElf_Sym *sym) +{ + return sym_type(sym) == STT_NOTYPE && + sym->st_name != 0 && + sym->st_shndx != SHN_UNDEF && + sym->st_shndx != SHN_ABS; +} + +static char *sec_name(GElf_Shdr *shdr, Elf_Data *secstrs) +{ + return secstrs->d_buf + shdr->sh_name; +} + +static char *sym_name(GElf_Sym *sym, Elf_Data *symstrs) +{ + return symstrs->d_buf + sym->st_name; +} + +static struct bt_symbol* __get_symbols(struct lt_config_shared *cfg, + Elf *elf, GElf_Ehdr *ehdr, + Elf_Scn *sec, GElf_Shdr *shdr) +{ + Elf_Data *syms, *symstrs, *secstrs; + Elf_Scn *sec_str, *sec_strndx; + GElf_Sym sym; + struct bt_symbol *sym_last = NULL; + uint32_t idx, nr_syms; + + syms = elf_getdata(sec, NULL); + if (!syms) { + PRINT_VERBOSE(cfg, 1, "elf_getdata failed\n"); + return NULL; + } + + sec_str = elf_getscn(elf, shdr->sh_link); + if (!sec_str) { + PRINT_VERBOSE(cfg, 1, "no strtab\n"); + return NULL; + } + + symstrs = elf_getdata(sec_str, NULL); + if (!symstrs) { + PRINT_VERBOSE(cfg, 1, "no strtab\n"); + return NULL; + } + + sec_strndx = elf_getscn(elf, ehdr->e_shstrndx); + if (!sec_strndx) { + PRINT_VERBOSE(cfg, 1, "no strndx\n"); + return NULL; + } + + secstrs = elf_getdata(sec_strndx, NULL); + if (!secstrs) { + PRINT_VERBOSE(cfg, 1, "no strndx data\n"); + return NULL; + } + + nr_syms = shdr->sh_size / shdr->sh_entsize; + + for (idx = 0, gelf_getsym(syms, idx, &sym); + idx < nr_syms; + idx++, gelf_getsym(syms, idx, &sym)) { + Elf_Scn *sec_code; + GElf_Shdr shdr_code; + struct bt_symbol *symbol; + + if (!sym_is_label(&sym) && !sym_is_function(&sym)) + continue; + + sec_code = elf_getscn(elf, sym.st_shndx); + if (!sec_code) { + PRINT_VERBOSE(cfg, 1, "failed to get symbol section\n"); + return NULL; + } + + gelf_getshdr(sec_code, &shdr_code); + + if (!strstr(sec_name(&shdr_code, secstrs), "text")) { + PRINT_VERBOSE(cfg, 1, "text section expected, got '%s'\n", + sec_name(&shdr_code, secstrs)); + continue; + } + + symbol = malloc(sizeof(*symbol)); + if (!symbol) { + PRINT_VERBOSE(cfg, 1, "failed to allocate symbol\n"); + return NULL; + } + + symbol->start = sym.st_value; + symbol->end = symbol->start + sym.st_size; + symbol->name = strdup(sym_name(&sym, symstrs)); + symbol->next = sym_last; + + sym_last = symbol; + + PRINT_VERBOSE(cfg, 1, "adding symbol %s, %lx, %lx\n", + symbol->name, symbol->start, symbol->end); + } + + return sym_last; +} + +static struct bt_symbol* get_symbols(struct lt_config_shared *cfg, + int fd, struct bt_map *map) +{ + Elf *elf; + GElf_Ehdr ehdr; + GElf_Shdr shdr_sym, shdr_dyn; + Elf_Scn *sec_sym, *sec_dyn; + struct bt_symbol *s = NULL; + + PRINT_VERBOSE(cfg, 1, "map1 %s\n", map->name); + + elf_version(EV_CURRENT); + + elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); + if (!elf) { + PRINT_VERBOSE(cfg, 1, "elf_begin failed: %s\n", + elf_errmsg(elf_errno())); + return NULL; + } + + PRINT_VERBOSE(cfg, 1, "map2 %s\n", map->name); + + do { + if (gelf_getehdr(elf, &ehdr) == NULL) { + PRINT_VERBOSE(cfg, 1, "gelf_getehdr failed\n"); + break; + } + + sec_sym = get_section(elf, &ehdr, &shdr_sym, ".symtab", NULL); + sec_dyn = get_section(elf, &ehdr, &shdr_dyn, ".dynsym", NULL); + + if (!sec_sym && !sec_dyn) { + PRINT_VERBOSE(cfg, 1, "no symbols\n"); + break; + } + + PRINT_VERBOSE(cfg, 1, "map %s\n", map->name); + + if (sec_sym) + s = __get_symbols(cfg, elf, &ehdr, + sec_sym, &shdr_sym); + + map->symbols = s; + + if (sec_dyn) { + s = __get_symbols(cfg, elf, &ehdr, + sec_dyn, &shdr_dyn); + if (s) { + s->next = map->symbols; + map->symbols = s; + } + } + PRINT_VERBOSE(cfg, 1, "map %s, symbols %p\n", map->name, map->symbols); + } while(0); + + elf_end(elf); + return s; +} + +static int symbols_init(struct lt_config_shared *cfg, struct bt_map *map) +{ + int fd; + + fd = open(map->name, O_RDONLY); + if (fd < 0) + return -1; + + map->symbols = get_symbols(cfg, fd, map); + + close(fd); + + return map->symbols ? 0 : -1; +} + +int bt_find_symbol(struct lt_config_shared *cfg, struct bt_symbol **sym, + struct bt_map *map, bt_word_t ip) +{ + struct bt_symbol* symbol = map->symbols; + + PRINT_VERBOSE(cfg, 1, "looking for ip %lx, %p, %p\n", ip, map, symbol); + + while(symbol) { + PRINT_VERBOSE(cfg, 1, "looking for ip %lx, start %lx, size %lx, name %s\n", + ip, symbol->start, symbol->end, symbol->name); + + if (ip >= symbol->start && ip <= symbol->end) { + *sym = symbol; + return 1; + } + symbol = symbol->next; + } + + PRINT_VERBOSE(cfg, 1, "looking for ip %lx, %s\n", ip, map->name); + return 0; +} +#else +int bt_find_symbol(struct lt_config_shared *cfg, struct bt_symbol **sym, + struct bt_map *map, bt_word_t ip) +{ + return 0; +} + +static int symbols_init(struct lt_config_shared *cfg, struct bt_map *map) +{ + return 0; +} + +static void symbols_destroy(struct bt_map *map) { } +#endif /* CONFIG_LIBELF */ + +int bt_find_map(struct lt_config_shared *cfg, struct bt_map **map, + bt_word_t ip) +{ + struct bt_map* m = maps; + + PRINT_VERBOSE(cfg, 1, "ip %lx\n", ip); + + while(m) { + PRINT_VERBOSE(cfg, 1, "ip %lx, start %lx. end %lx\n", ip, m->start, m->end); + if (ip >= m->start && ip <= m->end) { + PRINT_VERBOSE(cfg, 1, "found map %p\n", m); + *map = m; + return 1; + } + m = m->next; + } + + PRINT_VERBOSE(cfg, 1, "not found\n"); + return 0; +} + +/* TODO erro handling */ +void bt_display(struct lt_config_shared *cfg, char *text) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + if (lt_sh(cfg, pipe)) { + char buf[LT_FIFO_MSG_MAXLEN]; + int len; + + len = lt_fifo_mtext_get(cfg, buf, &tv, text); + lt_fifo_send(cfg, pipe_fd, buf, len); + } else + lt_out_text(cfg, &tv, syscall(SYS_gettid), text); +} + +static int maps_callback(struct dl_phdr_info *info, + size_t size, void *data) +{ + struct lt_config_shared *cfg = data; + const ElfW(Phdr) *phdr = info->dlpi_phdr; + bt_word_t load_base = info->dlpi_addr; + long n; + + for (n = info->dlpi_phnum; --n >= 0; phdr++) { + struct bt_map *map; + + if ((phdr->p_type != PT_LOAD) || + (!(phdr->p_flags & PF_X))) + continue; + + map = malloc(sizeof(*map)); + if (!map) + return -1; + + map->base = load_base; + map->start = phdr->p_vaddr + load_base; + map->end = map->start + phdr->p_memsz; + map->name = (char*) info->dlpi_name; + map->symbols = NULL; + + map->next = maps; + maps = map; + + if (symbols_init(cfg, map)) + PRINT_VERBOSE(cfg, 1, + "failed to build symbols for %s\n", + map->name); + + PRINT_VERBOSE(cfg, 1, "adding map %p, %p - %p @ %s\n", + map, map->start, map->end, map->name); + } + + return 0; +} + +static int maps_init(struct lt_config_shared *cfg) +{ + return dl_iterate_phdr(maps_callback, cfg); +} + +static void maps_destroy(struct lt_config_shared *cfg) +{ + struct bt_map* next = maps; + + do { + struct bt_map *map = next; + next = map->next; + + symbols_destroy(map); + free(map); + } while(next); +} + +void dump_backtrace(struct lt_config_shared *cfg, unsigned long flags) +{ + /* moving on even if we fail here */ + if (maps_init(cfg)) + bt_display(cfg, "failed to build maps"); + + if (bt_dump(cfg, flags)) + bt_display(cfg, "dump failed"); + + if (BT_FLAG_DESTROY && flags) + maps_destroy(cfg); + + /* empty line after the backtrace */ + bt_display(cfg, ""); +} diff --git a/src/backtrace.h b/src/backtrace.h new file mode 100644 index 0000000..1ba10f0 --- /dev/null +++ b/src/backtrace.h @@ -0,0 +1,46 @@ +#ifndef BACKTRACE_H +#define BACKTRACE_H + +#define BIT(x) (1 << x) + +enum { + BT_FLAG_DESTROY = BIT(0), +}; + +#if defined(__x86_64) +typedef uint64_t bt_word_t; +#else +typedef uint32_t bt_word_t; +#endif + +struct bt_symbol; + +struct bt_map { + bt_word_t base; + bt_word_t start; + bt_word_t end; + char *name; + struct bt_symbol *symbols; + struct bt_map *next; +}; + +struct bt_symbol { + bt_word_t start; + bt_word_t end; + char *name; + struct bt_map *map; + struct bt_symbol *next; +}; + +/* backtrace user interface */ +void dump_backtrace(struct lt_config_shared *cfg, unsigned long flags); + +/* backtrace implementation helpers */ +int bt_dump(struct lt_config_shared *cfg, unsigned long flags); +int bt_find_map(struct lt_config_shared *cfg, struct bt_map **map, + bt_word_t ip); +int bt_find_symbol(struct lt_config_shared *cfg, struct bt_symbol **sym, + struct bt_map *map, bt_word_t ip); +void bt_display(struct lt_config_shared *cfg, char *text); + +#endif /* BACKTRACE_H*/ diff --git a/src/config.h b/src/config.h index b2dc73a..1ac519e 100644 --- a/src/config.h +++ b/src/config.h @@ -442,9 +442,13 @@ struct lt_error_sym* lt_error_sym_get(struct lt_config_shared *cfg, int lt_error_sym_exit(struct lt_config_audit *cfg, struct lt_symbol *sym, struct timeval *tv, + struct link_map *lr, + const La_regs *inregs, La_retval *outregs); int lt_error_set_retval(struct lt_config_audit *cfg, unsigned long ret, La_retval *outregs); +void lt_error_get_retaddr(struct lt_config_audit *cfg, + void **ret_addr, const La_regs *inregs); #define PRINT(fmt, args...) \ do { \ @@ -405,8 +405,10 @@ static void run_cleanup(struct lt_config_app *cfg, if (lt_sh(cfg, pipe)) lt_fifo_notify_cleanup(cfg); - if (cfg->output_tty) + if (cfg->output_tty) { tty_close(cfg); + close(pa->fd_tty_master); + } remove_dir(cfg, pa->dir); } diff --git a/src/sysdeps/i686/error.c b/src/sysdeps/i686/error.c index fd675a5..9aaa976 100644 --- a/src/sysdeps/i686/error.c +++ b/src/sysdeps/i686/error.c @@ -13,3 +13,10 @@ int lt_error_set_retval(struct lt_config_audit *cfg, return 0; } + +void lt_error_get_retaddr(struct lt_config_audit *cfg, + void **ret_addr, const La_regs *inregs) +{ + void **stack = (void**) inregs->lr_esp; + *ret_addr = *stack; +} diff --git a/src/sysdeps/x86_64/error.c b/src/sysdeps/x86_64/error.c index 36784cd..bdad198 100644 --- a/src/sysdeps/x86_64/error.c +++ b/src/sysdeps/x86_64/error.c @@ -13,3 +13,10 @@ int lt_error_set_retval(struct lt_config_audit *cfg, return 0; } + +void lt_error_get_retaddr(struct lt_config_audit *cfg, + void **ret_addr, const La_regs *inregs) +{ + void **stack = (void**) inregs->lr_rsp; + *ret_addr = *stack; +} |