Imported Upstream version 1.15.1
[deb_xorg-server.git] / os / backtrace.c
CommitLineData
a09e091a
JB
1/*
2 * Copyright 2008 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#ifdef HAVE_DIX_CONFIG_H
25#include <dix-config.h>
26#endif
27
28#include "os.h"
29#include "misc.h"
30#include <errno.h>
31#include <string.h>
32
33#ifdef HAVE_LIBUNWIND
34
35#define UNW_LOCAL_ONLY
36#include <libunwind.h>
37
38#ifndef _GNU_SOURCE
39#define _GNU_SOURCE
40#endif
41#include <dlfcn.h>
42
43void
44xorg_backtrace(void)
45{
46 unw_cursor_t cursor;
47 unw_context_t context;
48 unw_word_t off;
49 unw_proc_info_t pip;
50 int ret, i = 0;
51 char procname[256];
52 const char *filename;
53 Dl_info dlinfo;
54
55 pip.unwind_info = NULL;
56 ret = unw_getcontext(&context);
57 if (ret) {
58 ErrorFSigSafe("unw_getcontext failed: %s [%d]\n", unw_strerror(ret),
59 ret);
60 return;
61 }
62
63 ret = unw_init_local(&cursor, &context);
64 if (ret) {
65 ErrorFSigSafe("unw_init_local failed: %s [%d]\n", unw_strerror(ret),
66 ret);
67 return;
68 }
69
70 ErrorFSigSafe("\n");
71 ErrorFSigSafe("Backtrace:\n");
72 ret = unw_step(&cursor);
73 while (ret > 0) {
74 ret = unw_get_proc_info(&cursor, &pip);
75 if (ret) {
76 ErrorFSigSafe("unw_get_proc_info failed: %s [%d]\n",
77 unw_strerror(ret), ret);
78 break;
79 }
80
81 ret = unw_get_proc_name(&cursor, procname, 256, &off);
82 if (ret && ret != -UNW_ENOMEM) {
83 if (ret != -UNW_EUNSPEC)
84 ErrorFSigSafe("unw_get_proc_name failed: %s [%d]\n",
85 unw_strerror(ret), ret);
86 procname[0] = '?';
87 procname[1] = 0;
88 }
89
90 if (dladdr((void *)(pip.start_ip + off), &dlinfo) && dlinfo.dli_fname &&
91 *dlinfo.dli_fname)
92 filename = dlinfo.dli_fname;
93 else
94 filename = "?";
95
96 ErrorFSigSafe("%u: %s (%s%s+0x%x) [%p]\n", i++, filename, procname,
97 ret == -UNW_ENOMEM ? "..." : "", (int)off,
98 (void *)(pip.start_ip + off));
99
100 ret = unw_step(&cursor);
101 if (ret < 0)
102 ErrorFSigSafe("unw_step failed: %s [%d]\n", unw_strerror(ret), ret);
103 }
104 ErrorFSigSafe("\n");
105}
106#else /* HAVE_LIBUNWIND */
107#ifdef HAVE_BACKTRACE
108#ifndef _GNU_SOURCE
109#define _GNU_SOURCE
110#endif
111#include <dlfcn.h>
112#include <execinfo.h>
113
114void
115xorg_backtrace(void)
116{
117 const int BT_SIZE = 64;
118 void *array[BT_SIZE];
119 const char *mod;
120 int size, i;
121 Dl_info info;
122
123 ErrorFSigSafe("\n");
124 ErrorFSigSafe("Backtrace:\n");
125 size = backtrace(array, BT_SIZE);
126 for (i = 0; i < size; i++) {
127 int rc = dladdr(array[i], &info);
128
129 if (rc == 0) {
130 ErrorFSigSafe("%u: ?? [%p]\n", i, array[i]);
131 continue;
132 }
133 mod = (info.dli_fname && *info.dli_fname) ? info.dli_fname : "(vdso)";
134 if (info.dli_saddr)
135 ErrorFSigSafe(
136 "%u: %s (%s+0x%x) [%p]\n",
137 i,
138 mod,
139 info.dli_sname,
140 (unsigned int)((char *) array[i] -
141 (char *) info.dli_saddr),
142 array[i]);
143 else
144 ErrorFSigSafe(
145 "%u: %s (%p+0x%x) [%p]\n",
146 i,
147 mod,
148 info.dli_fbase,
149 (unsigned int)((char *) array[i] -
150 (char *) info.dli_fbase),
151 array[i]);
152 }
153 ErrorFSigSafe("\n");
154}
155
156#else /* not glibc or glibc < 2.1 */
157
158#if defined(sun) && defined(__SVR4)
159#define HAVE_PSTACK
160#endif
161
162#if defined(HAVE_WALKCONTEXT) /* Solaris 9 & later */
163
164#include <ucontext.h>
165#include <signal.h>
166#include <dlfcn.h>
167#include <sys/elf.h>
168
169#ifdef _LP64
170#define ElfSym Elf64_Sym
171#else
172#define ElfSym Elf32_Sym
173#endif
174
175/* Called for each frame on the stack to print it's contents */
176static int
177xorg_backtrace_frame(uintptr_t pc, int signo, void *arg)
178{
179 Dl_info dlinfo;
180 ElfSym *dlsym;
181 char header[32];
182 int depth = *((int *) arg);
183
184 if (signo) {
185 char signame[SIG2STR_MAX];
186
187 if (sig2str(signo, signame) != 0) {
188 strcpy(signame, "unknown");
189 }
190
191 ErrorFSigSafe("** Signal %u (%s)\n", signo, signame);
192 }
193
194 snprintf(header, sizeof(header), "%d: 0x%lx", depth, pc);
195 *((int *) arg) = depth + 1;
196
197 /* Ask system dynamic loader for info on the address */
198 if (dladdr1((void *) pc, &dlinfo, (void **) &dlsym, RTLD_DL_SYMENT)) {
199 unsigned long offset = pc - (uintptr_t) dlinfo.dli_saddr;
200 const char *symname;
201
202 if (offset < dlsym->st_size) { /* inside a function */
203 symname = dlinfo.dli_sname;
204 }
205 else { /* found which file it was in, but not which function */
206 symname = "<section start>";
207 offset = pc - (uintptr_t) dlinfo.dli_fbase;
208 }
209 ErrorFSigSafe("%s: %s:%s+0x%x\n", header, dlinfo.dli_fname, symname,
210 offset);
211
212 }
213 else {
214 /* Couldn't find symbol info from system dynamic loader, should
215 * probably poke elfloader here, but haven't written that code yet,
216 * so we just print the pc.
217 */
218 ErrorFSigSafe("%s\n", header);
219 }
220
221 return 0;
222}
223#endif /* HAVE_WALKCONTEXT */
224
225#ifdef HAVE_PSTACK
226static int
227xorg_backtrace_pstack(void)
228{
229 pid_t kidpid;
230 int pipefd[2];
231
232 if (pipe(pipefd) != 0) {
233 return -1;
234 }
235
236 kidpid = fork1();
237
238 if (kidpid == -1) {
239 /* ERROR */
240 return -1;
241 }
242 else if (kidpid == 0) {
243 /* CHILD */
244 char parent[16];
245
246 seteuid(0);
247 close(STDIN_FILENO);
248 close(STDOUT_FILENO);
249 dup2(pipefd[1], STDOUT_FILENO);
250 closefrom(STDERR_FILENO);
251
252 snprintf(parent, sizeof(parent), "%d", getppid());
253 execle("/usr/bin/pstack", "pstack", parent, NULL);
254 exit(1);
255 }
256 else {
257 /* PARENT */
258 char btline[256];
259 int kidstat;
260 int bytesread;
261 int done = 0;
262
263 close(pipefd[1]);
264
265 while (!done) {
266 bytesread = read(pipefd[0], btline, sizeof(btline) - 1);
267
268 if (bytesread > 0) {
269 btline[bytesread] = 0;
270 ErrorFSigSafe("%s", btline);
271 }
272 else if ((bytesread < 0) || ((errno != EINTR) && (errno != EAGAIN)))
273 done = 1;
274 }
275 close(pipefd[0]);
276 waitpid(kidpid, &kidstat, 0);
277 if (kidstat != 0)
278 return -1;
279 }
280 return 0;
281}
282#endif /* HAVE_PSTACK */
283
284#if defined(HAVE_PSTACK) || defined(HAVE_WALKCONTEXT)
285
286void
287xorg_backtrace(void)
288{
289
290 ErrorFSigSafe("\n");
291 ErrorFSigSafe("Backtrace:\n");
292
293#ifdef HAVE_PSTACK
294/* First try fork/exec of pstack - otherwise fall back to walkcontext
295 pstack is preferred since it can print names of non-exported functions */
296
297 if (xorg_backtrace_pstack() < 0)
298#endif
299 {
300#ifdef HAVE_WALKCONTEXT
301 ucontext_t u;
302 int depth = 1;
303
304 if (getcontext(&u) == 0)
305 walkcontext(&u, xorg_backtrace_frame, &depth);
306 else
307#endif
308 ErrorFSigSafe("Failed to get backtrace info: %s\n", strerror(errno));
309 }
310 ErrorFSigSafe("\n");
311}
312
313#else
314
315/* Default fallback if we can't find any way to get a backtrace */
316void
317xorg_backtrace(void)
318{
319 return;
320}
321
322#endif
323#endif
324#endif