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
43 void
xorg_backtrace(void)44 xorg_backtrace(void)
45 {
46 unw_cursor_t cursor;
47 unw_context_t context;
48 unw_word_t ip;
49 unw_word_t off;
50 unw_proc_info_t pip;
51 int ret, i = 0;
52 char procname[256];
53 const char *filename;
54 Dl_info dlinfo;
55
56 pip.unwind_info = NULL;
57 ret = unw_getcontext(&context);
58 if (ret) {
59 ErrorFSigSafe("unw_getcontext failed: %s [%d]\n", unw_strerror(ret),
60 ret);
61 return;
62 }
63
64 ret = unw_init_local(&cursor, &context);
65 if (ret) {
66 ErrorFSigSafe("unw_init_local failed: %s [%d]\n", unw_strerror(ret),
67 ret);
68 return;
69 }
70
71 ErrorFSigSafe("\n");
72 ErrorFSigSafe("Backtrace:\n");
73 ret = unw_step(&cursor);
74 while (ret > 0) {
75 ret = unw_get_proc_info(&cursor, &pip);
76 if (ret) {
77 ErrorFSigSafe("unw_get_proc_info failed: %s [%d]\n",
78 unw_strerror(ret), ret);
79 break;
80 }
81
82 off = 0;
83 ret = unw_get_proc_name(&cursor, procname, 256, &off);
84 if (ret && ret != -UNW_ENOMEM) {
85 if (ret != -UNW_EUNSPEC)
86 ErrorFSigSafe("unw_get_proc_name failed: %s [%d]\n",
87 unw_strerror(ret), ret);
88 procname[0] = '?';
89 procname[1] = 0;
90 }
91
92 if (unw_get_reg (&cursor, UNW_REG_IP, &ip) < 0)
93 ip = pip.start_ip + off;
94 if (dladdr((void *)(uintptr_t)(ip), &dlinfo) && dlinfo.dli_fname &&
95 *dlinfo.dli_fname)
96 filename = dlinfo.dli_fname;
97 else
98 filename = "?";
99
100 ErrorFSigSafe("%u: %s (%s%s+0x%x) [%p]\n", i++, filename, procname,
101 ret == -UNW_ENOMEM ? "..." : "", (int)off,
102 (void *)(uintptr_t)(ip));
103
104 ret = unw_step(&cursor);
105 if (ret < 0)
106 ErrorFSigSafe("unw_step failed: %s [%d]\n", unw_strerror(ret), ret);
107 }
108 ErrorFSigSafe("\n");
109 }
110 #else /* HAVE_LIBUNWIND */
111 #ifdef HAVE_BACKTRACE
112 #ifndef _GNU_SOURCE
113 #define _GNU_SOURCE
114 #endif
115 #include <dlfcn.h>
116 #include <execinfo.h>
117
118 void
xorg_backtrace(void)119 xorg_backtrace(void)
120 {
121 const int BT_SIZE = 64;
122 void *array[BT_SIZE];
123 const char *mod;
124 int size, i;
125 Dl_info info;
126
127 ErrorFSigSafe("\n");
128 ErrorFSigSafe("Backtrace:\n");
129 size = backtrace(array, BT_SIZE);
130 for (i = 0; i < size; i++) {
131 int rc = dladdr(array[i], &info);
132
133 if (rc == 0) {
134 ErrorFSigSafe("%u: ?? [%p]\n", i, array[i]);
135 continue;
136 }
137 mod = (info.dli_fname && *info.dli_fname) ? info.dli_fname : "(vdso)";
138 if (info.dli_saddr)
139 ErrorFSigSafe(
140 "%u: %s (%s+0x%x) [%p]\n",
141 i,
142 mod,
143 info.dli_sname,
144 (unsigned int)((char *) array[i] -
145 (char *) info.dli_saddr),
146 array[i]);
147 else
148 ErrorFSigSafe(
149 "%u: %s (%p+0x%x) [%p]\n",
150 i,
151 mod,
152 info.dli_fbase,
153 (unsigned int)((char *) array[i] -
154 (char *) info.dli_fbase),
155 array[i]);
156 }
157 ErrorFSigSafe("\n");
158 }
159
160 #else /* not glibc or glibc < 2.1 */
161
162 #if defined(__sun) && defined(__SVR4)
163 #define HAVE_PSTACK
164 #endif
165
166 #if defined(HAVE_WALKCONTEXT) /* Solaris 9 & later */
167
168 #include <ucontext.h>
169 #include <signal.h>
170 #include <dlfcn.h>
171 #include <sys/elf.h>
172
173 #ifdef _LP64
174 #define ElfSym Elf64_Sym
175 #else
176 #define ElfSym Elf32_Sym
177 #endif
178
179 /* Called for each frame on the stack to print its contents */
180 static int
xorg_backtrace_frame(uintptr_t pc,int signo,void * arg)181 xorg_backtrace_frame(uintptr_t pc, int signo, void *arg)
182 {
183 Dl_info dlinfo;
184 ElfSym *dlsym;
185 char header[32];
186 int depth = *((int *) arg);
187
188 if (signo) {
189 char signame[SIG2STR_MAX];
190
191 if (sig2str(signo, signame) != 0) {
192 strcpy(signame, "unknown");
193 }
194
195 ErrorFSigSafe("** Signal %u (%s)\n", signo, signame);
196 }
197
198 snprintf(header, sizeof(header), "%d: 0x%lx", depth, pc);
199 *((int *) arg) = depth + 1;
200
201 /* Ask system dynamic loader for info on the address */
202 if (dladdr1((void *) pc, &dlinfo, (void **) &dlsym, RTLD_DL_SYMENT)) {
203 unsigned long offset = pc - (uintptr_t) dlinfo.dli_saddr;
204 const char *symname;
205
206 if (offset < dlsym->st_size) { /* inside a function */
207 symname = dlinfo.dli_sname;
208 }
209 else { /* found which file it was in, but not which function */
210 symname = "<section start>";
211 offset = pc - (uintptr_t) dlinfo.dli_fbase;
212 }
213 ErrorFSigSafe("%s: %s:%s+0x%x\n", header, dlinfo.dli_fname, symname,
214 offset);
215
216 }
217 else {
218 /* Couldn't find symbol info from system dynamic loader, should
219 * probably poke elfloader here, but haven't written that code yet,
220 * so we just print the pc.
221 */
222 ErrorFSigSafe("%s\n", header);
223 }
224
225 return 0;
226 }
227 #endif /* HAVE_WALKCONTEXT */
228
229 #ifdef HAVE_PSTACK
230 #include <unistd.h>
231
232 static int
xorg_backtrace_pstack(void)233 xorg_backtrace_pstack(void)
234 {
235 pid_t kidpid;
236 int pipefd[2];
237
238 if (pipe(pipefd) != 0) {
239 return -1;
240 }
241
242 kidpid = fork1();
243
244 if (kidpid == -1) {
245 /* ERROR */
246 return -1;
247 }
248 else if (kidpid == 0) {
249 /* CHILD */
250 char parent[16];
251
252 seteuid(0);
253 close(STDIN_FILENO);
254 close(STDOUT_FILENO);
255 dup2(pipefd[1], STDOUT_FILENO);
256 closefrom(STDERR_FILENO);
257
258 snprintf(parent, sizeof(parent), "%d", getppid());
259 execle("/usr/bin/pstack", "pstack", parent, NULL);
260 exit(1);
261 }
262 else {
263 /* PARENT */
264 char btline[256];
265 int kidstat;
266 int bytesread;
267 int done = 0;
268
269 close(pipefd[1]);
270
271 while (!done) {
272 bytesread = read(pipefd[0], btline, sizeof(btline) - 1);
273
274 if (bytesread > 0) {
275 btline[bytesread] = 0;
276 ErrorFSigSafe("%s", btline);
277 }
278 else if ((bytesread < 0) || ((errno != EINTR) && (errno != EAGAIN)))
279 done = 1;
280 }
281 close(pipefd[0]);
282 waitpid(kidpid, &kidstat, 0);
283 if (kidstat != 0)
284 return -1;
285 }
286 return 0;
287 }
288 #endif /* HAVE_PSTACK */
289
290 #if defined(HAVE_PSTACK) || defined(HAVE_WALKCONTEXT)
291
292 void
xorg_backtrace(void)293 xorg_backtrace(void)
294 {
295
296 ErrorFSigSafe("\n");
297 ErrorFSigSafe("Backtrace:\n");
298
299 #ifdef HAVE_PSTACK
300 /* First try fork/exec of pstack - otherwise fall back to walkcontext
301 pstack is preferred since it can print names of non-exported functions */
302
303 if (xorg_backtrace_pstack() < 0)
304 #endif
305 {
306 #ifdef HAVE_WALKCONTEXT
307 ucontext_t u;
308 int depth = 1;
309
310 if (getcontext(&u) == 0)
311 walkcontext(&u, xorg_backtrace_frame, &depth);
312 else
313 #endif
314 ErrorFSigSafe("Failed to get backtrace info: %s\n", strerror(errno));
315 }
316 ErrorFSigSafe("\n");
317 }
318
319 #else
320
321 /* Default fallback if we can't find any way to get a backtrace */
322 void
xorg_backtrace(void)323 xorg_backtrace(void)
324 {
325 return;
326 }
327
328 #endif
329 #endif
330 #endif
331