summaryrefslogtreecommitdiffstats
path: root/contrib/libexecinfo/backtrace.c
diff options
context:
space:
mode:
authoremaste <emaste@FreeBSD.org>2013-08-23 12:38:51 +0000
committeremaste <emaste@FreeBSD.org>2013-08-23 12:38:51 +0000
commitc269490987041ebfff61039a2653346eae305f0d (patch)
tree6e2bf6a450c6107d397e9924334fca0513806db6 /contrib/libexecinfo/backtrace.c
parent69d69e8c31e4194232fe07a10788647223ea898d (diff)
parent6fea629cde7e83b0d12d6ad858a2f7ce6c318fc5 (diff)
downloadFreeBSD-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.c229
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);
+}
OpenPOWER on IntegriCloud