drm/i915: Fix compilation with clang
[dragonfly.git] / lib / libexecinfo / backtrace.c
1 /*      $NetBSD: backtrace.c,v 1.3 2013/08/29 14:58:56 christos Exp $   */
2
3 /*-
4  * Copyright (c) 2012 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include <sys/cdefs.h>
32 #include <sys/param.h>
33 #include <assert.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <stdint.h>
39 #include <stddef.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <dlfcn.h>
43 #include <elf.h>
44
45 #include "execinfo.h"
46 #include "symtab.h"
47
48 #ifdef __linux__
49 #define SELF    "/proc/self/exe"
50 #else
51 #include <sys/sysctl.h>
52 #define SELF    "/proc/curproc/file"
53 #endif
54
55 static int
56 open_self(int flags)
57 {
58         const char *pathname = SELF;
59 #ifdef KERN_PROC_PATHNAME
60         static const int name[] = {
61                 CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
62         };
63         char path[MAXPATHLEN];
64         size_t len;
65
66         len = sizeof(path);
67         if (sysctl(name, 4, path, &len, NULL, 0) != -1)
68                 pathname = path;
69 #endif
70         return open(pathname, flags);
71 }
72
73
74 static int __printflike(4, 5)
75 rasprintf(char **buf, size_t *bufsiz, size_t offs, const char *fmt, ...)
76 {
77         for (;;) {
78                 size_t nbufsiz;
79                 char *nbuf;
80
81                 if (*buf && offs < *bufsiz) {
82                         va_list ap;
83                         int len;
84
85                         va_start(ap, fmt);
86                         len = vsnprintf(*buf + offs, *bufsiz - offs, fmt, ap);
87                         va_end(ap);
88
89                         if (len < 0 || (size_t)len + 1 < *bufsiz - offs)
90                                 return len;
91                         nbufsiz = MAX(*bufsiz + 512, (size_t)len + 1);
92                 } else
93                         nbufsiz = MAX(offs, *bufsiz) + 512;
94
95                 nbuf = realloc(*buf, nbufsiz);
96                 if (nbuf == NULL)
97                         return -1;
98                 *buf = nbuf;
99                 *bufsiz = nbufsiz;
100         }
101 }
102
103 /*
104  * format specifiers:
105  *      %a      = address
106  *      %n      = symbol_name
107  *      %d      = symbol_address - address
108  *      %D      = if symbol_address == address "" else +%d
109  *      %f      = filename
110  */
111 static ssize_t
112 format_string(char **buf, size_t *bufsiz, size_t offs, const char *fmt,
113     Dl_info *dli, const void *addr)
114 {
115         ptrdiff_t diff = (const char *)addr - (const char *)dli->dli_saddr;
116         size_t o = offs;
117         int len;
118
119         for (; *fmt; fmt++) {
120                 if (*fmt != '%')
121                         goto printone;
122                 switch (*++fmt) {
123                 case 'a':
124                         len = rasprintf(buf, bufsiz, o, "%p", addr);
125                         break;
126                 case 'n':
127                         len = rasprintf(buf, bufsiz, o, "%s", dli->dli_sname);
128                         break;
129                 case 'D':
130                         if (diff)
131                                 len = rasprintf(buf, bufsiz, o, "+0x%tx", diff);
132                         else
133                                 len = 0;
134                         break;
135                 case 'd':
136                         len = rasprintf(buf, bufsiz, o, "0x%tx", diff);
137                         break;
138                 case 'f':
139                         len = rasprintf(buf, bufsiz, o, "%s", dli->dli_fname);
140                         break;
141                 default:
142                 printone:
143                         len = rasprintf(buf, bufsiz, o, "%c", *fmt);
144                         break;
145                 }
146                 if (len == -1)
147                         return -1;
148                 o += len;
149         }
150         return o - offs;
151 }
152
153 static ssize_t
154 format_address(symtab_t *st, char **buf, size_t *bufsiz, size_t offs,
155     const char *fmt, const void *addr)
156 {
157         Dl_info dli;
158
159         memset(&dli, 0, sizeof(dli));
160         (void)dladdr(addr, &dli);
161         if (st)
162                 symtab_find(st, addr, &dli);
163
164         if (dli.dli_sname == NULL)
165                 dli.dli_sname = "???";
166         if (dli.dli_fname == NULL)
167                 dli.dli_fname = "???";
168         if (dli.dli_saddr == NULL)
169                 dli.dli_saddr = (void *)(intptr_t)addr;
170
171         return format_string(buf, bufsiz, offs, fmt, &dli, addr);
172 }
173
174 char **
175 backtrace_symbols_fmt(void *const *trace, size_t len, const char *fmt)
176 {
177
178         static const size_t slen = sizeof(char *) + 64; /* estimate */
179         char *ptr;
180         symtab_t *st;
181         int fd;
182
183         if ((fd = open_self(O_RDONLY)) != -1)
184                 st = symtab_create(fd, -1, STT_FUNC);
185         else
186                 st = NULL;
187
188         if ((ptr = calloc(len, slen)) == NULL)
189                 goto out;
190
191         size_t psize = len * slen;
192         size_t offs = len * sizeof(char *);
193
194         /* We store only offsets in the first pass because of realloc */
195         for (size_t i = 0; i < len; i++) {
196                 ssize_t x;
197                 ((char **)(void *)ptr)[i] = (void *)offs;
198                 x = format_address(st, &ptr, &psize, offs, fmt, trace[i]);
199                 if (x == -1) {
200                         free(ptr);
201                         ptr = NULL;
202                         goto out;
203                 }
204                 offs += x;
205                 ptr[offs++] = '\0';
206                 assert(offs < psize);
207         }
208
209         /* Change offsets to pointers */
210         for (size_t j = 0; j < len; j++)
211                 ((char **)(void *)ptr)[j] += (intptr_t)ptr;
212
213 out:
214         symtab_destroy(st);
215         if (fd != -1)
216                 (void)close(fd);
217
218         return (void *)ptr;
219 }
220
221 int
222 backtrace_symbols_fd_fmt(void *const *trace, size_t len, int fd,
223     const char *fmt)
224 {
225         char **s = backtrace_symbols_fmt(trace, len, fmt);
226         if (s == NULL)
227                 return -1;
228         for (size_t i = 0; i < len; i++)
229                 if (dprintf(fd, "%s\n", s[i]) < 0)
230                         break;
231         free(s);
232         return 0;
233 }
234
235 static const char fmt[] = "%a <%n%D> at %f";
236
237 char **
238 backtrace_symbols(void *const *trace, size_t len)
239 {
240         return backtrace_symbols_fmt(trace, len, fmt);
241 }
242
243 int
244 backtrace_symbols_fd(void *const *trace, size_t len, int fd)
245 {
246         return backtrace_symbols_fd_fmt(trace, len, fd, fmt);
247 }