1255033Semaste/* $NetBSD: backtrace.c,v 1.3 2013/08/29 14:58:56 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> 32255033Semaste__RCSID("$NetBSD: backtrace.c,v 1.3 2013/08/29 14:58:56 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 54255033Semaste#include <sys/sysctl.h> 55254661Semaste#define SELF "/proc/curproc/file" 56254661Semaste#endif 57254661Semaste 58255033Semastestatic int 59255033Semasteopen_self(int flags) 60255033Semaste{ 61255033Semaste const char *pathname = SELF; 62255033Semaste#ifdef KERN_PROC_PATHNAME 63255033Semaste static const int name[] = { 64255033Semaste CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1, 65255033Semaste }; 66255033Semaste char path[MAXPATHLEN]; 67255033Semaste size_t len; 68255033Semaste 69255033Semaste len = sizeof(path); 70255033Semaste if (sysctl(name, 4, path, &len, NULL, 0) != -1) 71255033Semaste pathname = path; 72255033Semaste#endif 73255033Semaste return open(pathname, flags); 74255033Semaste} 75255033Semaste 76255033Semaste 77254661Semastestatic int __printflike(4, 5) 78254661Semasterasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...) 79254661Semaste{ 80254661Semaste for (;;) { 81254661Semaste size_t nbufsiz; 82254661Semaste char *nbuf; 83254661Semaste 84254661Semaste if (*buf && offs < *bufsiz) { 85254661Semaste va_list ap; 86254661Semaste int len; 87254661Semaste 88254661Semaste va_start(ap, fmt); 89254661Semaste len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap); 90254661Semaste va_end(ap); 91254661Semaste 92258558Semaste if (len < 0 || (size_t)len + 1 < *bufsiz - offs) 93254661Semaste return len; 94254661Semaste nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1); 95254661Semaste } else 96254661Semaste nbufsiz = MAX(offs, *bufsiz) + 512; 97254661Semaste 98254661Semaste nbuf = realloc(*buf, nbufsiz); 99254661Semaste if (nbuf == NULL) 100254661Semaste return -1; 101254661Semaste *buf = nbuf; 102254661Semaste *bufsiz = nbufsiz; 103254661Semaste } 104254661Semaste} 105254661Semaste 106254661Semaste/* 107254661Semaste * format specifiers: 108254661Semaste * %a = address 109254661Semaste * %n = symbol_name 110254661Semaste * %d = symbol_address - address 111254661Semaste * %D = if symbol_address == address "" else +%d 112254661Semaste * %f = filename 113254661Semaste */ 114254661Semastestatic ssize_t 115254661Semasteformat_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt, 116254661Semaste Dl_info *dli, const void *addr) 117254661Semaste{ 118254661Semaste ptrdiff_t diff = (const char *)addr - (const char *)dli->dli_saddr; 119254661Semaste size_t o = offs; 120254661Semaste int len; 121254661Semaste 122254661Semaste for (; *fmt; fmt++) { 123254661Semaste if (*fmt != '%') 124254661Semaste goto printone; 125254661Semaste switch (*++fmt) { 126254661Semaste case 'a': 127254661Semaste len = rasprintf(buf, bufsiz, o, "%p", addr); 128254661Semaste break; 129254661Semaste case 'n': 130254661Semaste len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname); 131254661Semaste break; 132254661Semaste case 'D': 133254661Semaste if (diff) 134254661Semaste len = rasprintf(buf, bufsiz, o, "+0x%tx", diff); 135254661Semaste else 136254661Semaste len = 0; 137254661Semaste break; 138254661Semaste case 'd': 139254661Semaste len = rasprintf(buf, bufsiz, o, "0x%tx", diff); 140254661Semaste break; 141254661Semaste case 'f': 142254661Semaste len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname); 143254661Semaste break; 144254661Semaste default: 145254661Semaste printone: 146254661Semaste len = rasprintf(buf, bufsiz, o, "%c", *fmt); 147254661Semaste break; 148254661Semaste } 149254661Semaste if (len == -1) 150254661Semaste return -1; 151254661Semaste o += len; 152254661Semaste } 153254661Semaste return o - offs; 154254661Semaste} 155254661Semaste 156254661Semastestatic ssize_t 157254661Semasteformat_address(symtab_t *st, char **buf, size_t *bufsiz, size_t offs, 158254661Semaste const char *fmt, const void *addr) 159254661Semaste{ 160254661Semaste Dl_info dli; 161254661Semaste 162254661Semaste memset(&dli, 0, sizeof(dli)); 163254661Semaste (void)dladdr(addr, &dli); 164254661Semaste if (st) 165254661Semaste symtab_find(st, addr, &dli); 166254661Semaste 167254661Semaste if (dli.dli_sname == NULL) 168254661Semaste dli.dli_sname = "???"; 169254661Semaste if (dli.dli_fname == NULL) 170254661Semaste dli.dli_fname = "???"; 171254661Semaste if (dli.dli_saddr == NULL) 172254661Semaste dli.dli_saddr = (void *)(intptr_t)addr; 173254661Semaste 174254661Semaste return format_string(buf, bufsiz, offs, fmt, &dli, addr); 175254661Semaste} 176254661Semaste 177254661Semastechar ** 178254661Semastebacktrace_symbols_fmt(void *const *trace, size_t len, const char *fmt) 179254661Semaste{ 180254661Semaste 181254661Semaste static const size_t slen = sizeof(char *) + 64; /* estimate */ 182254661Semaste char *ptr; 183254661Semaste symtab_t *st; 184254661Semaste int fd; 185254661Semaste 186255033Semaste if ((fd = open_self(O_RDONLY)) != -1) 187254661Semaste st = symtab_create(fd, -1, STT_FUNC); 188254661Semaste else 189254661Semaste st = NULL; 190254661Semaste 191254661Semaste if ((ptr = calloc(len, slen)) == NULL) 192254661Semaste goto out; 193254661Semaste 194254661Semaste size_t psize = len * slen; 195254661Semaste size_t offs = len * sizeof(char *); 196254661Semaste 197254661Semaste /* We store only offsets in the first pass because of realloc */ 198254661Semaste for (size_t i = 0; i < len; i++) { 199254661Semaste ssize_t x; 200254661Semaste ((char **)(void *)ptr)[i] = (void *)offs; 201254661Semaste x = format_address(st, &ptr, &psize, offs, fmt, trace[i]); 202254661Semaste if (x == -1) { 203254661Semaste free(ptr); 204254661Semaste ptr = NULL; 205254661Semaste goto out; 206254661Semaste } 207254661Semaste offs += x; 208254661Semaste ptr[offs++] = '\0'; 209254661Semaste assert(offs < psize); 210254661Semaste } 211254661Semaste 212254661Semaste /* Change offsets to pointers */ 213254661Semaste for (size_t j = 0; j < len; j++) 214254661Semaste ((char **)(void *)ptr)[j] += (intptr_t)ptr; 215254661Semaste 216254661Semasteout: 217254661Semaste symtab_destroy(st); 218254661Semaste if (fd != -1) 219254661Semaste (void)close(fd); 220254661Semaste 221254661Semaste return (void *)ptr; 222254661Semaste} 223254661Semaste 224254661Semasteint 225254661Semastebacktrace_symbols_fd_fmt(void *const *trace, size_t len, int fd, 226254661Semaste const char *fmt) 227254661Semaste{ 228254661Semaste char **s = backtrace_symbols_fmt(trace, len, fmt); 229254661Semaste if (s == NULL) 230254661Semaste return -1; 231254661Semaste for (size_t i = 0; i < len; i++) 232254661Semaste if (dprintf(fd, "%s\n", s[i]) < 0) 233254661Semaste break; 234254661Semaste free(s); 235254661Semaste return 0; 236254661Semaste} 237254661Semaste 238254661Semastestatic const char fmt[] = "%a <%n%D> at %f"; 239254661Semaste 240254661Semastechar ** 241254661Semastebacktrace_symbols(void *const *trace, size_t len) 242254661Semaste{ 243254661Semaste return backtrace_symbols_fmt(trace, len, fmt); 244254661Semaste} 245254661Semaste 246254661Semasteint 247254661Semastebacktrace_symbols_fd(void *const *trace, size_t len, int fd) 248254661Semaste{ 249254661Semaste return backtrace_symbols_fd_fmt(trace, len, fd, fmt); 250254661Semaste} 251