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