xref: /OK3568_Linux_fs/external/xserver/os/backtrace.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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