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