diff options
author | emaste <emaste@FreeBSD.org> | 2013-08-23 12:38:51 +0000 |
---|---|---|
committer | emaste <emaste@FreeBSD.org> | 2013-08-23 12:38:51 +0000 |
commit | c269490987041ebfff61039a2653346eae305f0d (patch) | |
tree | 6e2bf6a450c6107d397e9924334fca0513806db6 /contrib/libexecinfo/backtrace.c | |
parent | 69d69e8c31e4194232fe07a10788647223ea898d (diff) | |
parent | 6fea629cde7e83b0d12d6ad858a2f7ce6c318fc5 (diff) | |
download | FreeBSD-src-c269490987041ebfff61039a2653346eae305f0d.zip FreeBSD-src-c269490987041ebfff61039a2653346eae305f0d.tar.gz |
Import NetBSD libexecinfo 20130822 to contrib
Diffstat (limited to 'contrib/libexecinfo/backtrace.c')
-rw-r--r-- | contrib/libexecinfo/backtrace.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/contrib/libexecinfo/backtrace.c b/contrib/libexecinfo/backtrace.c new file mode 100644 index 0000000..02ad148 --- /dev/null +++ b/contrib/libexecinfo/backtrace.c @@ -0,0 +1,229 @@ +/* $NetBSD: backtrace.c,v 1.2 2012/07/09 03:11:59 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/cdefs.h> +__RCSID("$NetBSD: backtrace.c,v 1.2 2012/07/09 03:11:59 christos Exp $"); + +#include <sys/param.h> +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdint.h> +#include <stddef.h> +#include <unistd.h> +#include <fcntl.h> +#include <dlfcn.h> +#include <elf.h> + +#include "execinfo.h" +#include "symtab.h" + +#ifdef __linux__ +#define SELF "/proc/self/exe" +#else +#define SELF "/proc/curproc/file" +#endif + +static int __printflike(4, 5) +rasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...) +{ + for (;;) { + size_t nbufsiz; + char *nbuf; + + if (*buf && offs < *bufsiz) { + va_list ap; + int len; + + va_start(ap, fmt); + len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap); + va_end(ap); + + if (len < 0 || (size_t)len < *bufsiz - offs) + return len; + nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1); + } else + nbufsiz = MAX(offs, *bufsiz) + 512; + + nbuf = realloc(*buf, nbufsiz); + if (nbuf == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + } +} + +/* + * format specifiers: + * %a = address + * %n = symbol_name + * %d = symbol_address - address + * %D = if symbol_address == address "" else +%d + * %f = filename + */ +static ssize_t +format_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt, + Dl_info *dli, const void *addr) +{ + ptrdiff_t diff = (const char *)addr - (const char *)dli->dli_saddr; + size_t o = offs; + int len; + + for (; *fmt; fmt++) { + if (*fmt != '%') + goto printone; + switch (*++fmt) { + case 'a': + len = rasprintf(buf, bufsiz, o, "%p", addr); + break; + case 'n': + len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname); + break; + case 'D': + if (diff) + len = rasprintf(buf, bufsiz, o, "+0x%tx", diff); + else + len = 0; + break; + case 'd': + len = rasprintf(buf, bufsiz, o, "0x%tx", diff); + break; + case 'f': + len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname); + break; + default: + printone: + len = rasprintf(buf, bufsiz, o, "%c", *fmt); + break; + } + if (len == -1) + return -1; + o += len; + } + return o - offs; +} + +static ssize_t +format_address(symtab_t *st, char **buf, size_t *bufsiz, size_t offs, + const char *fmt, const void *addr) +{ + Dl_info dli; + + memset(&dli, 0, sizeof(dli)); + (void)dladdr(addr, &dli); + if (st) + symtab_find(st, addr, &dli); + + if (dli.dli_sname == NULL) + dli.dli_sname = "???"; + if (dli.dli_fname == NULL) + dli.dli_fname = "???"; + if (dli.dli_saddr == NULL) + dli.dli_saddr = (void *)(intptr_t)addr; + + return format_string(buf, bufsiz, offs, fmt, &dli, addr); +} + +char ** +backtrace_symbols_fmt(void *const *trace, size_t len, const char *fmt) +{ + + static const size_t slen = sizeof(char *) + 64; /* estimate */ + char *ptr; + symtab_t *st; + int fd; + + if ((fd = open(SELF, O_RDONLY)) != -1) + st = symtab_create(fd, -1, STT_FUNC); + else + st = NULL; + + if ((ptr = calloc(len, slen)) == NULL) + goto out; + + size_t psize = len * slen; + size_t offs = len * sizeof(char *); + + /* We store only offsets in the first pass because of realloc */ + for (size_t i = 0; i < len; i++) { + ssize_t x; + ((char **)(void *)ptr)[i] = (void *)offs; + x = format_address(st, &ptr, &psize, offs, fmt, trace[i]); + if (x == -1) { + free(ptr); + ptr = NULL; + goto out; + } + offs += x; + ptr[offs++] = '\0'; + assert(offs < psize); + } + + /* Change offsets to pointers */ + for (size_t j = 0; j < len; j++) + ((char **)(void *)ptr)[j] += (intptr_t)ptr; + +out: + symtab_destroy(st); + if (fd != -1) + (void)close(fd); + + return (void *)ptr; +} + +int +backtrace_symbols_fd_fmt(void *const *trace, size_t len, int fd, + const char *fmt) +{ + char **s = backtrace_symbols_fmt(trace, len, fmt); + if (s == NULL) + return -1; + for (size_t i = 0; i < len; i++) + if (dprintf(fd, "%s\n", s[i]) < 0) + break; + free(s); + return 0; +} + +static const char fmt[] = "%a <%n%D> at %f"; + +char ** +backtrace_symbols(void *const *trace, size_t len) +{ + return backtrace_symbols_fmt(trace, len, fmt); +} + +int +backtrace_symbols_fd(void *const *trace, size_t len, int fd) +{ + return backtrace_symbols_fd_fmt(trace, len, fd, fmt); +} |