backtrace.c revision 706f2543
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
31#ifdef HAVE_BACKTRACE
32#ifndef _GNU_SOURCE
33#define _GNU_SOURCE
34#endif
35#include <dlfcn.h>
36#include <execinfo.h>
37
38void xorg_backtrace(void)
39{
40    void *array[64];
41    const char *mod;
42    int size, i;
43    Dl_info info;
44    ErrorF("\nBacktrace:\n");
45    size = backtrace(array, 64);
46    for (i = 0; i < size; i++) {
47	int rc = dladdr(array[i], &info);
48	if (rc == 0) {
49	    ErrorF("%d: ?? [%p]\n", i, array[i]);
50	    continue;
51	}
52	mod = (info.dli_fname && *info.dli_fname) ? info.dli_fname : "(vdso)";
53	if (info.dli_saddr)
54	    ErrorF("%d: %s (%s+0x%lx) [%p]\n", i, mod,
55		   info.dli_sname, (long unsigned int)((char *) array[i] - (char *) info.dli_saddr), array[i]);
56	else
57	    ErrorF("%d: %s (%p+0x%lx) [%p]\n", i, mod,
58		   info.dli_fbase, (long unsigned int)((char *) array[i] - (char *) info.dli_fbase), array[i]);
59    }
60}
61
62#else /* not glibc or glibc < 2.1 */
63
64# if defined(sun) && defined(__SVR4)
65#  define HAVE_PSTACK
66# endif
67
68# if defined(HAVE_WALKCONTEXT) /* Solaris 9 & later */
69
70# include <ucontext.h>
71# include <signal.h>
72# include <dlfcn.h>
73# include <sys/elf.h>
74
75#ifdef _LP64
76# define ElfSym Elf64_Sym
77#else
78# define ElfSym Elf32_Sym
79#endif
80
81/* Called for each frame on the stack to print it's contents */
82static int xorg_backtrace_frame(uintptr_t pc, int signo, void *arg)
83{
84    Dl_info dlinfo;
85    ElfSym *dlsym;
86    char header[32];
87    int depth = *((int *) arg);
88
89    if (signo) {
90	char signame[SIG2STR_MAX];
91
92	if (sig2str(signo, signame) != 0) {
93	    strcpy(signame, "unknown");
94	}
95
96	ErrorF("** Signal %d (%s)\n", signo, signame);
97    }
98
99    snprintf(header, sizeof(header), "%d: 0x%lx", depth, pc);
100    *((int *) arg) = depth + 1;
101
102    /* Ask system dynamic loader for info on the address */
103    if (dladdr1((void *) pc, &dlinfo, (void **) &dlsym, RTLD_DL_SYMENT)) {
104	unsigned long offset = pc - (uintptr_t) dlinfo.dli_saddr;
105	const char *symname;
106
107	if (offset < dlsym->st_size) { /* inside a function */
108	    symname = dlinfo.dli_sname;
109	} else { /* found which file it was in, but not which function */
110	    symname = "<section start>";
111	    offset = pc - (uintptr_t)dlinfo.dli_fbase;
112	}
113	ErrorF("%s: %s:%s+0x%lx\n", header, dlinfo.dli_fname,
114	       symname, offset);
115
116    } else {
117	/* Couldn't find symbol info from system dynamic loader, should
118	 * probably poke elfloader here, but haven't written that code yet,
119	 * so we just print the pc.
120	 */
121	ErrorF("%s\n", header);
122    }
123
124    return 0;
125}
126# endif /* HAVE_WALKCONTEXT */
127
128# ifdef HAVE_PSTACK
129static int xorg_backtrace_pstack(void) {
130    pid_t kidpid;
131    int pipefd[2];
132
133    if (pipe(pipefd) != 0) {
134	return -1;
135    }
136
137    kidpid = fork1();
138
139    if (kidpid == -1) {
140	/* ERROR */
141	return -1;
142    } else if (kidpid == 0) {
143	/* CHILD */
144	char parent[16];
145
146	seteuid(0);
147	close(STDIN_FILENO);
148	close(STDOUT_FILENO);
149	dup2(pipefd[1],STDOUT_FILENO);
150	closefrom(STDERR_FILENO);
151
152	snprintf(parent, sizeof(parent), "%d", getppid());
153	execle("/usr/bin/pstack", "pstack", parent, NULL);
154	exit(1);
155    } else {
156	/* PARENT */
157	char btline[256];
158	int kidstat;
159	int bytesread;
160	int done = 0;
161
162	close(pipefd[1]);
163
164	while (!done) {
165	    bytesread = read(pipefd[0], btline, sizeof(btline) - 1);
166
167	    if (bytesread > 0) {
168		btline[bytesread] = 0;
169		ErrorF("%s", btline);
170	    }
171	    else if ((bytesread < 0) ||
172		     ((errno != EINTR) && (errno != EAGAIN)))
173		done = 1;
174	}
175	close(pipefd[0]);
176	waitpid(kidpid, &kidstat, 0);
177	if (kidstat != 0)
178	    return -1;
179    }
180    return 0;
181}
182# endif /* HAVE_PSTACK */
183
184
185# if defined(HAVE_PSTACK) || defined(HAVE_WALKCONTEXT)
186
187void xorg_backtrace(void) {
188
189    ErrorF("\nBacktrace:\n");
190
191#  ifdef HAVE_PSTACK
192/* First try fork/exec of pstack - otherwise fall back to walkcontext
193   pstack is preferred since it can print names of non-exported functions */
194
195    if (xorg_backtrace_pstack() < 0)
196#  endif
197    {
198#  ifdef HAVE_WALKCONTEXT
199	ucontext_t u;
200	int depth = 1;
201
202	if (getcontext(&u) == 0)
203	    walkcontext(&u, xorg_backtrace_frame, &depth);
204	else
205#  endif
206	    Error("Failed to get backtrace info");
207    }
208    ErrorF("\n");
209}
210
211# else
212
213/* Default fallback if we can't find any way to get a backtrace */
214void xorg_backtrace(void) { return; }
215
216# endif
217#endif
218