backtrace.c (8573B)
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 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 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 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 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 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 323 xorg_backtrace(void) 324 { 325 return; 326 } 327 328 #endif 329 #endif 330 #endif