backtrace.c revision 254689
1254661Semaste/* $NetBSD: backtrace.c,v 1.2 2012/07/09 03:11:59 christos Exp $ */ 2254661Semaste 3254661Semaste/*- 4254661Semaste * Copyright (c) 2012 The NetBSD Foundation, Inc. 5254661Semaste * All rights reserved. 6254661Semaste * 7254661Semaste * This code is derived from software contributed to The NetBSD Foundation 8254661Semaste * by Christos Zoulas. 9254661Semaste * 10254661Semaste * Redistribution and use in source and binary forms, with or without 11254661Semaste * modification, are permitted provided that the following conditions 12254661Semaste * are met: 13254661Semaste * 1. Redistributions of source code must retain the above copyright 14254661Semaste * notice, this list of conditions and the following disclaimer. 15254661Semaste * 2. Redistributions in binary form must reproduce the above copyright 16254661Semaste * notice, this list of conditions and the following disclaimer in the 17254661Semaste * documentation and/or other materials provided with the distribution. 18254661Semaste * 19254661Semaste * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20254661Semaste * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21254661Semaste * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22254661Semaste * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23254661Semaste * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24254661Semaste * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25254661Semaste * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26254661Semaste * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27254661Semaste * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28254661Semaste * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29254661Semaste * POSSIBILITY OF SUCH DAMAGE. 30254661Semaste */ 31254661Semaste#include <sys/cdefs.h> 32254661Semaste__RCSID("$NetBSD: backtrace.c,v 1.2 2012/07/09 03:11:59 christos Exp $"); 33254661Semaste 34254661Semaste#include <sys/param.h> 35254661Semaste#include <assert.h> 36254689Semaste#define _WITH_DPRINTF 37254661Semaste#include <stdio.h> 38254661Semaste#include <string.h> 39254661Semaste#include <stdlib.h> 40254661Semaste#include <stdarg.h> 41254661Semaste#include <stdint.h> 42254661Semaste#include <stddef.h> 43254661Semaste#include <unistd.h> 44254661Semaste#include <fcntl.h> 45254661Semaste#include <dlfcn.h> 46254661Semaste#include <elf.h> 47254661Semaste 48254661Semaste#include "execinfo.h" 49254661Semaste#include "symtab.h" 50254661Semaste 51254661Semaste#ifdef __linux__ 52254661Semaste#define SELF "/proc/self/exe" 53254661Semaste#else 54254661Semaste#define SELF "/proc/curproc/file" 55254661Semaste#endif 56254661Semaste 57254661Semastestatic int __printflike(4, 5) 58254661Semasterasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...) 59254661Semaste{ 60254661Semaste for (;;) { 61254661Semaste size_t nbufsiz; 62254661Semaste char *nbuf; 63254661Semaste 64254661Semaste if (*buf && offs < *bufsiz) { 65254661Semaste va_list ap; 66254661Semaste int len; 67254661Semaste 68254661Semaste va_start(ap, fmt); 69254661Semaste len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap); 70254661Semaste va_end(ap); 71254661Semaste 72254661Semaste if (len < 0 || (size_t)len < *bufsiz - offs) 73254661Semaste return len; 74254661Semaste nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1); 75254661Semaste } else 76254661Semaste nbufsiz = MAX(offs, *bufsiz) + 512; 77254661Semaste 78254661Semaste nbuf = realloc(*buf, nbufsiz); 79254661Semaste if (nbuf == NULL) 80254661Semaste return -1; 81254661Semaste *buf = nbuf; 82254661Semaste *bufsiz = nbufsiz; 83254661Semaste } 84254661Semaste} 85254661Semaste 86254661Semaste/* 87254661Semaste * format specifiers: 88254661Semaste * %a = address 89254661Semaste * %n = symbol_name 90254661Semaste * %d = symbol_address - address 91254661Semaste * %D = if symbol_address == address "" else +%d 92254661Semaste * %f = filename 93254661Semaste */ 94254661Semastestatic ssize_t 95254661Semasteformat_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt, 96254661Semaste Dl_info *dli, const void *addr) 97254661Semaste{ 98254661Semaste ptrdiff_t diff = (const char *)addr - (const char *)dli->dli_saddr; 99254661Semaste size_t o = offs; 100254661Semaste int len; 101254661Semaste 102254661Semaste for (; *fmt; fmt++) { 103254661Semaste if (*fmt != '%') 104254661Semaste goto printone; 105254661Semaste switch (*++fmt) { 106254661Semaste case 'a': 107254661Semaste len = rasprintf(buf, bufsiz, o, "%p", addr); 108254661Semaste break; 109254661Semaste case 'n': 110254661Semaste len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname); 111254661Semaste break; 112254661Semaste case 'D': 113254661Semaste if (diff) 114254661Semaste len = rasprintf(buf, bufsiz, o, "+0x%tx", diff); 115254661Semaste else 116254661Semaste len = 0; 117254661Semaste break; 118254661Semaste case 'd': 119254661Semaste len = rasprintf(buf, bufsiz, o, "0x%tx", diff); 120254661Semaste break; 121254661Semaste case 'f': 122254661Semaste len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname); 123254661Semaste break; 124254661Semaste default: 125254661Semaste printone: 126254661Semaste len = rasprintf(buf, bufsiz, o, "%c", *fmt); 127254661Semaste break; 128254661Semaste } 129254661Semaste if (len == -1) 130254661Semaste return -1; 131254661Semaste o += len; 132254661Semaste } 133254661Semaste return o - offs; 134254661Semaste} 135254661Semaste 136254661Semastestatic ssize_t 137254661Semasteformat_address(symtab_t *st, char **buf, size_t *bufsiz, size_t offs, 138254661Semaste const char *fmt, const void *addr) 139254661Semaste{ 140254661Semaste Dl_info dli; 141254661Semaste 142254661Semaste memset(&dli, 0, sizeof(dli)); 143254661Semaste (void)dladdr(addr, &dli); 144254661Semaste if (st) 145254661Semaste symtab_find(st, addr, &dli); 146254661Semaste 147254661Semaste if (dli.dli_sname == NULL) 148254661Semaste dli.dli_sname = "???"; 149254661Semaste if (dli.dli_fname == NULL) 150254661Semaste dli.dli_fname = "???"; 151254661Semaste if (dli.dli_saddr == NULL) 152254661Semaste dli.dli_saddr = (void *)(intptr_t)addr; 153254661Semaste 154254661Semaste return format_string(buf, bufsiz, offs, fmt, &dli, addr); 155254661Semaste} 156254661Semaste 157254661Semastechar ** 158254661Semastebacktrace_symbols_fmt(void *const *trace, size_t len, const char *fmt) 159254661Semaste{ 160254661Semaste 161254661Semaste static const size_t slen = sizeof(char *) + 64; /* estimate */ 162254661Semaste char *ptr; 163254661Semaste symtab_t *st; 164254661Semaste int fd; 165254661Semaste 166254661Semaste if ((fd = open(SELF, O_RDONLY)) != -1) 167254661Semaste st = symtab_create(fd, -1, STT_FUNC); 168254661Semaste else 169254661Semaste st = NULL; 170254661Semaste 171254661Semaste if ((ptr = calloc(len, slen)) == NULL) 172254661Semaste goto out; 173254661Semaste 174254661Semaste size_t psize = len * slen; 175254661Semaste size_t offs = len * sizeof(char *); 176254661Semaste 177254661Semaste /* We store only offsets in the first pass because of realloc */ 178254661Semaste for (size_t i = 0; i < len; i++) { 179254661Semaste ssize_t x; 180254661Semaste ((char **)(void *)ptr)[i] = (void *)offs; 181254661Semaste x = format_address(st, &ptr, &psize, offs, fmt, trace[i]); 182254661Semaste if (x == -1) { 183254661Semaste free(ptr); 184254661Semaste ptr = NULL; 185254661Semaste goto out; 186254661Semaste } 187254661Semaste offs += x; 188254661Semaste ptr[offs++] = '\0'; 189254661Semaste assert(offs < psize); 190254661Semaste } 191254661Semaste 192254661Semaste /* Change offsets to pointers */ 193254661Semaste for (size_t j = 0; j < len; j++) 194254661Semaste ((char **)(void *)ptr)[j] += (intptr_t)ptr; 195254661Semaste 196254661Semasteout: 197254661Semaste symtab_destroy(st); 198254661Semaste if (fd != -1) 199254661Semaste (void)close(fd); 200254661Semaste 201254661Semaste return (void *)ptr; 202254661Semaste} 203254661Semaste 204254661Semasteint 205254661Semastebacktrace_symbols_fd_fmt(void *const *trace, size_t len, int fd, 206254661Semaste const char *fmt) 207254661Semaste{ 208254661Semaste char **s = backtrace_symbols_fmt(trace, len, fmt); 209254661Semaste if (s == NULL) 210254661Semaste return -1; 211254661Semaste for (size_t i = 0; i < len; i++) 212254661Semaste if (dprintf(fd, "%s\n", s[i]) < 0) 213254661Semaste break; 214254661Semaste free(s); 215254661Semaste return 0; 216254661Semaste} 217254661Semaste 218254661Semastestatic const char fmt[] = "%a <%n%D> at %f"; 219254661Semaste 220254661Semastechar ** 221254661Semastebacktrace_symbols(void *const *trace, size_t len) 222254661Semaste{ 223254661Semaste return backtrace_symbols_fmt(trace, len, fmt); 224254661Semaste} 225254661Semaste 226254661Semasteint 227254661Semastebacktrace_symbols_fd(void *const *trace, size_t len, int fd) 228254661Semaste{ 229254661Semaste return backtrace_symbols_fd_fmt(trace, len, fd, fmt); 230254661Semaste} 231