1/*
|
2 * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
|
2 * Copyright (C) 2009, 2013 Internet Systems Consortium, Inc. ("ISC") |
3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17/* $Id: backtrace.c,v 1.3 2009/09/02 23:48:02 tbox Exp $ */ 18 19/*! \file */ 20 21#include "config.h" 22 23#include <string.h> 24#include <stdlib.h> 25#ifdef HAVE_LIBCTRACE 26#include <execinfo.h> 27#endif 28 29#include <isc/backtrace.h> 30#include <isc/result.h> 31#include <isc/util.h> 32 33#ifdef ISC_PLATFORM_USEBACKTRACE 34/* 35 * Getting a back trace of a running process is tricky and highly platform 36 * dependent. Our current approach is as follows: 37 * 1. If the system library supports the "backtrace()" function, use it. 38 * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64, 39 * then use gcc's (hidden) Unwind_Backtrace() function. Note that this 40 * function doesn't work for C programs on many other architectures. 41 * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack 42 * frame following frame pointers. This assumes the executable binary 43 * compiled with frame pointers; this is not always true for x86_64 (rather, 44 * compiler optimizations often disable frame pointers). The validation 45 * checks in getnextframeptr() hopefully rejects bogus values stored in 46 * the RBP register in such a case. If the backtrace function itself crashes 47 * due to this problem, the whole package should be rebuilt with 48 * --disable-backtrace. 49 */ 50#ifdef HAVE_LIBCTRACE 51#define BACKTRACE_LIBC 52#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__)) 53#define BACKTRACE_GCC
|
54#elif defined(WIN32) 55#define BACKTRACE_WIN32 |
56#elif defined(__x86_64__) || defined(__i386__) 57#define BACKTRACE_X86STACK 58#else 59#define BACKTRACE_DISABLED 60#endif /* HAVE_LIBCTRACE */ 61#else /* !ISC_PLATFORM_USEBACKTRACE */ 62#define BACKTRACE_DISABLED 63#endif /* ISC_PLATFORM_USEBACKTRACE */ 64 65#ifdef BACKTRACE_LIBC 66isc_result_t 67isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 68 int n; 69 70 /* 71 * Validate the arguments: intentionally avoid using REQUIRE(). 72 * See notes in backtrace.h. 73 */ 74 if (addrs == NULL || nframes == NULL) 75 return (ISC_R_FAILURE); 76 77 /* 78 * backtrace(3) includes this function itself in the address array, 79 * which should be eliminated from the returned sequence. 80 */ 81 n = backtrace(addrs, maxaddrs); 82 if (n < 2) 83 return (ISC_R_NOTFOUND); 84 n--; 85 memmove(addrs, &addrs[1], sizeof(void *) * n); 86 *nframes = n; 87 return (ISC_R_SUCCESS); 88} 89#elif defined(BACKTRACE_GCC) 90extern int _Unwind_Backtrace(void* fn, void* a); 91extern void* _Unwind_GetIP(void* ctx); 92 93typedef struct { 94 void **result; 95 int max_depth; 96 int skip_count; 97 int count; 98} trace_arg_t; 99 100static int 101btcallback(void *uc, void *opq) { 102 trace_arg_t *arg = (trace_arg_t *)opq; 103 104 if (arg->skip_count > 0) 105 arg->skip_count--; 106 else 107 arg->result[arg->count++] = (void *)_Unwind_GetIP(uc); 108 if (arg->count == arg->max_depth) 109 return (5); /* _URC_END_OF_STACK */ 110 111 return (0); /* _URC_NO_REASON */ 112} 113 114isc_result_t 115isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 116 trace_arg_t arg; 117 118 /* Argument validation: see above. */ 119 if (addrs == NULL || nframes == NULL) 120 return (ISC_R_FAILURE); 121 122 arg.skip_count = 1; 123 arg.result = addrs; 124 arg.max_depth = maxaddrs; 125 arg.count = 0; 126 _Unwind_Backtrace(btcallback, &arg); 127 128 *nframes = arg.count; 129 130 return (ISC_R_SUCCESS); 131}
|
132#elif defined(BACKTRACE_WIN32) 133isc_result_t 134isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 135 unsigned long ftc = (unsigned long)maxaddrs; 136 137 *nframes = (int)CaptureStackBackTrace(1, ftc, addrs, NULL); 138 return ISC_R_SUCCESS; 139} |
140#elif defined(BACKTRACE_X86STACK) 141#ifdef __x86_64__ 142static unsigned long 143getrbp() { 144 __asm("movq %rbp, %rax\n"); 145} 146#endif 147 148static void ** 149getnextframeptr(void **sp) { 150 void **newsp = (void **)*sp; 151 152 /* 153 * Perform sanity check for the new frame pointer, derived from 154 * google glog. This can actually be bogus depending on compiler. 155 */ 156 157 /* prohibit the stack frames from growing downwards */ 158 if (newsp <= sp) 159 return (NULL); 160 161 /* A heuristics to reject "too large" frame: this actually happened. */ 162 if ((char *)newsp - (char *)sp > 100000) 163 return (NULL); 164 165 /* 166 * Not sure if other checks used in glog are needed at this moment. 167 * For our purposes we don't have to consider non-contiguous frames, 168 * for example. 169 */ 170 171 return (newsp); 172} 173 174isc_result_t 175isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 176 int i = 0; 177 void **sp; 178 179 /* Argument validation: see above. */ 180 if (addrs == NULL || nframes == NULL) 181 return (ISC_R_FAILURE); 182 183#ifdef __x86_64__ 184 sp = (void **)getrbp(); 185 if (sp == NULL) 186 return (ISC_R_NOTFOUND); 187 /* 188 * sp is the frame ptr of this function itself due to the call to 189 * getrbp(), so need to unwind one frame for consistency. 190 */ 191 sp = getnextframeptr(sp); 192#else 193 /* 194 * i386: the frame pointer is stored 2 words below the address for the 195 * first argument. Note that the body of this function cannot be 196 * inlined since it depends on the address of the function argument. 197 */ 198 sp = (void **)&addrs - 2; 199#endif 200 201 while (sp != NULL && i < maxaddrs) { 202 addrs[i++] = *(sp + 1); 203 sp = getnextframeptr(sp); 204 } 205 206 *nframes = i; 207 208 return (ISC_R_SUCCESS); 209} 210#elif defined(BACKTRACE_DISABLED) 211isc_result_t 212isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 213 /* Argument validation: see above. */ 214 if (addrs == NULL || nframes == NULL) 215 return (ISC_R_FAILURE); 216 217 UNUSED(maxaddrs); 218 219 return (ISC_R_NOTIMPLEMENTED); 220} 221#endif 222 223isc_result_t 224isc_backtrace_getsymbolfromindex(int index, const void **addrp, 225 const char **symbolp) 226{ 227 REQUIRE(addrp != NULL && *addrp == NULL); 228 REQUIRE(symbolp != NULL && *symbolp == NULL); 229 230 if (index < 0 || index >= isc__backtrace_nsymbols) 231 return (ISC_R_RANGE); 232 233 *addrp = isc__backtrace_symtable[index].addr; 234 *symbolp = isc__backtrace_symtable[index].symbol; 235 return (ISC_R_SUCCESS); 236} 237 238static int 239symtbl_compare(const void *addr, const void *entryarg) { 240 const isc_backtrace_symmap_t *entry = entryarg; 241 const isc_backtrace_symmap_t *end = 242 &isc__backtrace_symtable[isc__backtrace_nsymbols - 1]; 243 244 if (isc__backtrace_nsymbols == 1 || entry == end) { 245 if (addr >= entry->addr) { 246 /* 247 * If addr is equal to or larger than that of the last 248 * entry of the table, we cannot be sure if this is 249 * within a valid range so we consider it valid. 250 */ 251 return (0); 252 } 253 return (-1); 254 } 255 256 /* entry + 1 is a valid entry from now on. */ 257 if (addr < entry->addr) 258 return (-1); 259 else if (addr >= (entry + 1)->addr) 260 return (1); 261 return (0); 262} 263 264isc_result_t 265isc_backtrace_getsymbol(const void *addr, const char **symbolp, 266 unsigned long *offsetp) 267{ 268 isc_result_t result = ISC_R_SUCCESS; 269 isc_backtrace_symmap_t *found; 270 271 /* 272 * Validate the arguments: intentionally avoid using REQUIRE(). 273 * See notes in backtrace.h. 274 */ 275 if (symbolp == NULL || *symbolp != NULL || offsetp == NULL) 276 return (ISC_R_FAILURE); 277 278 if (isc__backtrace_nsymbols < 1) 279 return (ISC_R_NOTFOUND); 280 281 /* 282 * Search the table for the entry that meets: 283 * entry.addr <= addr < next_entry.addr. 284 */ 285 found = bsearch(addr, isc__backtrace_symtable, isc__backtrace_nsymbols, 286 sizeof(isc__backtrace_symtable[0]), symtbl_compare); 287 if (found == NULL) 288 result = ISC_R_NOTFOUND; 289 else { 290 *symbolp = found->symbol;
|
281 *offsetp = (const char *)addr - (char *)found->addr;
|
291 *offsetp = (unsigned long) ((const char *)addr - 292 (char *)found->addr); |
293 } 294 295 return (result); 296}
|