backtrace.c revision 706f2543
1706f2543Smrg/*
2706f2543Smrg * Copyright 2008 Red Hat, Inc.
3706f2543Smrg *
4706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5706f2543Smrg * copy of this software and associated documentation files (the "Software"),
6706f2543Smrg * to deal in the Software without restriction, including without limitation
7706f2543Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8706f2543Smrg * and/or sell copies of the Software, and to permit persons to whom the
9706f2543Smrg * Software is furnished to do so, subject to the following conditions:
10706f2543Smrg *
11706f2543Smrg * The above copyright notice and this permission notice (including the next
12706f2543Smrg * paragraph) shall be included in all copies or substantial portions of the
13706f2543Smrg * Software.
14706f2543Smrg *
15706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16706f2543Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17706f2543Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18706f2543Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19706f2543Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20706f2543Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21706f2543Smrg * DEALINGS IN THE SOFTWARE.
22706f2543Smrg */
23706f2543Smrg
24706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
25706f2543Smrg#include <dix-config.h>
26706f2543Smrg#endif
27706f2543Smrg
28706f2543Smrg#include "os.h"
29706f2543Smrg#include "misc.h"
30706f2543Smrg
31706f2543Smrg#ifdef HAVE_BACKTRACE
32706f2543Smrg#ifndef _GNU_SOURCE
33706f2543Smrg#define _GNU_SOURCE
34706f2543Smrg#endif
35706f2543Smrg#include <dlfcn.h>
36706f2543Smrg#include <execinfo.h>
37706f2543Smrg
38706f2543Smrgvoid xorg_backtrace(void)
39706f2543Smrg{
40706f2543Smrg    void *array[64];
41706f2543Smrg    const char *mod;
42706f2543Smrg    int size, i;
43706f2543Smrg    Dl_info info;
44706f2543Smrg    ErrorF("\nBacktrace:\n");
45706f2543Smrg    size = backtrace(array, 64);
46706f2543Smrg    for (i = 0; i < size; i++) {
47706f2543Smrg	int rc = dladdr(array[i], &info);
48706f2543Smrg	if (rc == 0) {
49706f2543Smrg	    ErrorF("%d: ?? [%p]\n", i, array[i]);
50706f2543Smrg	    continue;
51706f2543Smrg	}
52706f2543Smrg	mod = (info.dli_fname && *info.dli_fname) ? info.dli_fname : "(vdso)";
53706f2543Smrg	if (info.dli_saddr)
54706f2543Smrg	    ErrorF("%d: %s (%s+0x%lx) [%p]\n", i, mod,
55706f2543Smrg		   info.dli_sname, (long unsigned int)((char *) array[i] - (char *) info.dli_saddr), array[i]);
56706f2543Smrg	else
57706f2543Smrg	    ErrorF("%d: %s (%p+0x%lx) [%p]\n", i, mod,
58706f2543Smrg		   info.dli_fbase, (long unsigned int)((char *) array[i] - (char *) info.dli_fbase), array[i]);
59706f2543Smrg    }
60706f2543Smrg}
61706f2543Smrg
62706f2543Smrg#else /* not glibc or glibc < 2.1 */
63706f2543Smrg
64706f2543Smrg# if defined(sun) && defined(__SVR4)
65706f2543Smrg#  define HAVE_PSTACK
66706f2543Smrg# endif
67706f2543Smrg
68706f2543Smrg# if defined(HAVE_WALKCONTEXT) /* Solaris 9 & later */
69706f2543Smrg
70706f2543Smrg# include <ucontext.h>
71706f2543Smrg# include <signal.h>
72706f2543Smrg# include <dlfcn.h>
73706f2543Smrg# include <sys/elf.h>
74706f2543Smrg
75706f2543Smrg#ifdef _LP64
76706f2543Smrg# define ElfSym Elf64_Sym
77706f2543Smrg#else
78706f2543Smrg# define ElfSym Elf32_Sym
79706f2543Smrg#endif
80706f2543Smrg
81706f2543Smrg/* Called for each frame on the stack to print it's contents */
82706f2543Smrgstatic int xorg_backtrace_frame(uintptr_t pc, int signo, void *arg)
83706f2543Smrg{
84706f2543Smrg    Dl_info dlinfo;
85706f2543Smrg    ElfSym *dlsym;
86706f2543Smrg    char header[32];
87706f2543Smrg    int depth = *((int *) arg);
88706f2543Smrg
89706f2543Smrg    if (signo) {
90706f2543Smrg	char signame[SIG2STR_MAX];
91706f2543Smrg
92706f2543Smrg	if (sig2str(signo, signame) != 0) {
93706f2543Smrg	    strcpy(signame, "unknown");
94706f2543Smrg	}
95706f2543Smrg
96706f2543Smrg	ErrorF("** Signal %d (%s)\n", signo, signame);
97706f2543Smrg    }
98706f2543Smrg
99706f2543Smrg    snprintf(header, sizeof(header), "%d: 0x%lx", depth, pc);
100706f2543Smrg    *((int *) arg) = depth + 1;
101706f2543Smrg
102706f2543Smrg    /* Ask system dynamic loader for info on the address */
103706f2543Smrg    if (dladdr1((void *) pc, &dlinfo, (void **) &dlsym, RTLD_DL_SYMENT)) {
104706f2543Smrg	unsigned long offset = pc - (uintptr_t) dlinfo.dli_saddr;
105706f2543Smrg	const char *symname;
106706f2543Smrg
107706f2543Smrg	if (offset < dlsym->st_size) { /* inside a function */
108706f2543Smrg	    symname = dlinfo.dli_sname;
109706f2543Smrg	} else { /* found which file it was in, but not which function */
110706f2543Smrg	    symname = "<section start>";
111706f2543Smrg	    offset = pc - (uintptr_t)dlinfo.dli_fbase;
112706f2543Smrg	}
113706f2543Smrg	ErrorF("%s: %s:%s+0x%lx\n", header, dlinfo.dli_fname,
114706f2543Smrg	       symname, offset);
115706f2543Smrg
116706f2543Smrg    } else {
117706f2543Smrg	/* Couldn't find symbol info from system dynamic loader, should
118706f2543Smrg	 * probably poke elfloader here, but haven't written that code yet,
119706f2543Smrg	 * so we just print the pc.
120706f2543Smrg	 */
121706f2543Smrg	ErrorF("%s\n", header);
122706f2543Smrg    }
123706f2543Smrg
124706f2543Smrg    return 0;
125706f2543Smrg}
126706f2543Smrg# endif /* HAVE_WALKCONTEXT */
127706f2543Smrg
128706f2543Smrg# ifdef HAVE_PSTACK
129706f2543Smrgstatic int xorg_backtrace_pstack(void) {
130706f2543Smrg    pid_t kidpid;
131706f2543Smrg    int pipefd[2];
132706f2543Smrg
133706f2543Smrg    if (pipe(pipefd) != 0) {
134706f2543Smrg	return -1;
135706f2543Smrg    }
136706f2543Smrg
137706f2543Smrg    kidpid = fork1();
138706f2543Smrg
139706f2543Smrg    if (kidpid == -1) {
140706f2543Smrg	/* ERROR */
141706f2543Smrg	return -1;
142706f2543Smrg    } else if (kidpid == 0) {
143706f2543Smrg	/* CHILD */
144706f2543Smrg	char parent[16];
145706f2543Smrg
146706f2543Smrg	seteuid(0);
147706f2543Smrg	close(STDIN_FILENO);
148706f2543Smrg	close(STDOUT_FILENO);
149706f2543Smrg	dup2(pipefd[1],STDOUT_FILENO);
150706f2543Smrg	closefrom(STDERR_FILENO);
151706f2543Smrg
152706f2543Smrg	snprintf(parent, sizeof(parent), "%d", getppid());
153706f2543Smrg	execle("/usr/bin/pstack", "pstack", parent, NULL);
154706f2543Smrg	exit(1);
155706f2543Smrg    } else {
156706f2543Smrg	/* PARENT */
157706f2543Smrg	char btline[256];
158706f2543Smrg	int kidstat;
159706f2543Smrg	int bytesread;
160706f2543Smrg	int done = 0;
161706f2543Smrg
162706f2543Smrg	close(pipefd[1]);
163706f2543Smrg
164706f2543Smrg	while (!done) {
165706f2543Smrg	    bytesread = read(pipefd[0], btline, sizeof(btline) - 1);
166706f2543Smrg
167706f2543Smrg	    if (bytesread > 0) {
168706f2543Smrg		btline[bytesread] = 0;
169706f2543Smrg		ErrorF("%s", btline);
170706f2543Smrg	    }
171706f2543Smrg	    else if ((bytesread < 0) ||
172706f2543Smrg		     ((errno != EINTR) && (errno != EAGAIN)))
173706f2543Smrg		done = 1;
174706f2543Smrg	}
175706f2543Smrg	close(pipefd[0]);
176706f2543Smrg	waitpid(kidpid, &kidstat, 0);
177706f2543Smrg	if (kidstat != 0)
178706f2543Smrg	    return -1;
179706f2543Smrg    }
180706f2543Smrg    return 0;
181706f2543Smrg}
182706f2543Smrg# endif /* HAVE_PSTACK */
183706f2543Smrg
184706f2543Smrg
185706f2543Smrg# if defined(HAVE_PSTACK) || defined(HAVE_WALKCONTEXT)
186706f2543Smrg
187706f2543Smrgvoid xorg_backtrace(void) {
188706f2543Smrg
189706f2543Smrg    ErrorF("\nBacktrace:\n");
190706f2543Smrg
191706f2543Smrg#  ifdef HAVE_PSTACK
192706f2543Smrg/* First try fork/exec of pstack - otherwise fall back to walkcontext
193706f2543Smrg   pstack is preferred since it can print names of non-exported functions */
194706f2543Smrg
195706f2543Smrg    if (xorg_backtrace_pstack() < 0)
196706f2543Smrg#  endif
197706f2543Smrg    {
198706f2543Smrg#  ifdef HAVE_WALKCONTEXT
199706f2543Smrg	ucontext_t u;
200706f2543Smrg	int depth = 1;
201706f2543Smrg
202706f2543Smrg	if (getcontext(&u) == 0)
203706f2543Smrg	    walkcontext(&u, xorg_backtrace_frame, &depth);
204706f2543Smrg	else
205706f2543Smrg#  endif
206706f2543Smrg	    Error("Failed to get backtrace info");
207706f2543Smrg    }
208706f2543Smrg    ErrorF("\n");
209706f2543Smrg}
210706f2543Smrg
211706f2543Smrg# else
212706f2543Smrg
213706f2543Smrg/* Default fallback if we can't find any way to get a backtrace */
214706f2543Smrgvoid xorg_backtrace(void) { return; }
215706f2543Smrg
216706f2543Smrg# endif
217706f2543Smrg#endif
218