1275970Scy/* 2275970Scy * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") 3275970Scy * 4275970Scy * Permission to use, copy, modify, and/or distribute this software for any 5275970Scy * purpose with or without fee is hereby granted, provided that the above 6275970Scy * copyright notice and this permission notice appear in all copies. 7275970Scy * 8275970Scy * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9275970Scy * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10275970Scy * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11275970Scy * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12275970Scy * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13275970Scy * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14275970Scy * PERFORMANCE OF THIS SOFTWARE. 15275970Scy */ 16275970Scy 17275970Scy/* $Id: backtrace.c,v 1.3 2009/09/02 23:48:02 tbox Exp $ */ 18275970Scy 19275970Scy/*! \file */ 20275970Scy 21275970Scy#include "config.h" 22275970Scy 23275970Scy#include <string.h> 24275970Scy#include <stdlib.h> 25275970Scy#ifdef HAVE_LIBCTRACE 26275970Scy#include <execinfo.h> 27275970Scy#endif 28275970Scy 29275970Scy#include <isc/backtrace.h> 30275970Scy#include <isc/result.h> 31275970Scy#include <isc/util.h> 32275970Scy 33275970Scy#ifdef ISC_PLATFORM_USEBACKTRACE 34275970Scy/* 35275970Scy * Getting a back trace of a running process is tricky and highly platform 36275970Scy * dependent. Our current approach is as follows: 37275970Scy * 1. If the system library supports the "backtrace()" function, use it. 38275970Scy * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64, 39275970Scy * then use gcc's (hidden) Unwind_Backtrace() function. Note that this 40275970Scy * function doesn't work for C programs on many other architectures. 41275970Scy * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack 42275970Scy * frame following frame pointers. This assumes the executable binary 43275970Scy * compiled with frame pointers; this is not always true for x86_64 (rather, 44275970Scy * compiler optimizations often disable frame pointers). The validation 45275970Scy * checks in getnextframeptr() hopefully rejects bogus values stored in 46275970Scy * the RBP register in such a case. If the backtrace function itself crashes 47275970Scy * due to this problem, the whole package should be rebuilt with 48275970Scy * --disable-backtrace. 49275970Scy */ 50275970Scy#ifdef HAVE_LIBCTRACE 51275970Scy#define BACKTRACE_LIBC 52275970Scy#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__)) 53275970Scy#define BACKTRACE_GCC 54275970Scy#elif defined(__x86_64__) || defined(__i386__) 55275970Scy#define BACKTRACE_X86STACK 56275970Scy#else 57275970Scy#define BACKTRACE_DISABLED 58275970Scy#endif /* HAVE_LIBCTRACE */ 59275970Scy#else /* !ISC_PLATFORM_USEBACKTRACE */ 60275970Scy#define BACKTRACE_DISABLED 61275970Scy#endif /* ISC_PLATFORM_USEBACKTRACE */ 62275970Scy 63275970Scy#ifdef BACKTRACE_LIBC 64275970Scyisc_result_t 65275970Scyisc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 66275970Scy int n; 67275970Scy 68275970Scy /* 69275970Scy * Validate the arguments: intentionally avoid using REQUIRE(). 70275970Scy * See notes in backtrace.h. 71275970Scy */ 72275970Scy if (addrs == NULL || nframes == NULL) 73275970Scy return (ISC_R_FAILURE); 74275970Scy 75275970Scy /* 76275970Scy * backtrace(3) includes this function itself in the address array, 77275970Scy * which should be eliminated from the returned sequence. 78275970Scy */ 79275970Scy n = backtrace(addrs, maxaddrs); 80275970Scy if (n < 2) 81275970Scy return (ISC_R_NOTFOUND); 82275970Scy n--; 83275970Scy memmove(addrs, &addrs[1], sizeof(void *) * n); 84275970Scy *nframes = n; 85275970Scy return (ISC_R_SUCCESS); 86275970Scy} 87275970Scy#elif defined(BACKTRACE_GCC) 88275970Scyextern int _Unwind_Backtrace(void* fn, void* a); 89275970Scyextern void* _Unwind_GetIP(void* ctx); 90275970Scy 91275970Scytypedef struct { 92275970Scy void **result; 93275970Scy int max_depth; 94275970Scy int skip_count; 95275970Scy int count; 96275970Scy} trace_arg_t; 97275970Scy 98275970Scystatic int 99275970Scybtcallback(void *uc, void *opq) { 100275970Scy trace_arg_t *arg = (trace_arg_t *)opq; 101275970Scy 102275970Scy if (arg->skip_count > 0) 103275970Scy arg->skip_count--; 104275970Scy else 105275970Scy arg->result[arg->count++] = (void *)_Unwind_GetIP(uc); 106275970Scy if (arg->count == arg->max_depth) 107275970Scy return (5); /* _URC_END_OF_STACK */ 108275970Scy 109275970Scy return (0); /* _URC_NO_REASON */ 110275970Scy} 111275970Scy 112275970Scyisc_result_t 113275970Scyisc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 114275970Scy trace_arg_t arg; 115275970Scy 116275970Scy /* Argument validation: see above. */ 117275970Scy if (addrs == NULL || nframes == NULL) 118275970Scy return (ISC_R_FAILURE); 119275970Scy 120275970Scy arg.skip_count = 1; 121275970Scy arg.result = addrs; 122275970Scy arg.max_depth = maxaddrs; 123275970Scy arg.count = 0; 124275970Scy _Unwind_Backtrace(btcallback, &arg); 125275970Scy 126275970Scy *nframes = arg.count; 127275970Scy 128275970Scy return (ISC_R_SUCCESS); 129275970Scy} 130275970Scy#elif defined(BACKTRACE_X86STACK) 131275970Scy#ifdef __x86_64__ 132275970Scystatic unsigned long 133275970Scygetrbp() { 134275970Scy __asm("movq %rbp, %rax\n"); 135275970Scy} 136275970Scy#endif 137275970Scy 138275970Scystatic void ** 139275970Scygetnextframeptr(void **sp) { 140275970Scy void **newsp = (void **)*sp; 141275970Scy 142275970Scy /* 143275970Scy * Perform sanity check for the new frame pointer, derived from 144275970Scy * google glog. This can actually be bogus depending on compiler. 145275970Scy */ 146275970Scy 147275970Scy /* prohibit the stack frames from growing downwards */ 148275970Scy if (newsp <= sp) 149275970Scy return (NULL); 150275970Scy 151275970Scy /* A heuristics to reject "too large" frame: this actually happened. */ 152275970Scy if ((char *)newsp - (char *)sp > 100000) 153275970Scy return (NULL); 154275970Scy 155275970Scy /* 156275970Scy * Not sure if other checks used in glog are needed at this moment. 157275970Scy * For our purposes we don't have to consider non-contiguous frames, 158275970Scy * for example. 159275970Scy */ 160275970Scy 161275970Scy return (newsp); 162275970Scy} 163275970Scy 164275970Scyisc_result_t 165275970Scyisc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 166275970Scy int i = 0; 167275970Scy void **sp; 168275970Scy 169275970Scy /* Argument validation: see above. */ 170275970Scy if (addrs == NULL || nframes == NULL) 171275970Scy return (ISC_R_FAILURE); 172275970Scy 173275970Scy#ifdef __x86_64__ 174275970Scy sp = (void **)getrbp(); 175275970Scy if (sp == NULL) 176275970Scy return (ISC_R_NOTFOUND); 177275970Scy /* 178275970Scy * sp is the frame ptr of this function itself due to the call to 179275970Scy * getrbp(), so need to unwind one frame for consistency. 180275970Scy */ 181275970Scy sp = getnextframeptr(sp); 182275970Scy#else 183275970Scy /* 184275970Scy * i386: the frame pointer is stored 2 words below the address for the 185275970Scy * first argument. Note that the body of this function cannot be 186275970Scy * inlined since it depends on the address of the function argument. 187275970Scy */ 188275970Scy sp = (void **)&addrs - 2; 189275970Scy#endif 190275970Scy 191275970Scy while (sp != NULL && i < maxaddrs) { 192275970Scy addrs[i++] = *(sp + 1); 193275970Scy sp = getnextframeptr(sp); 194275970Scy } 195275970Scy 196275970Scy *nframes = i; 197275970Scy 198275970Scy return (ISC_R_SUCCESS); 199275970Scy} 200275970Scy#elif defined(BACKTRACE_DISABLED) 201275970Scyisc_result_t 202275970Scyisc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { 203275970Scy /* Argument validation: see above. */ 204275970Scy if (addrs == NULL || nframes == NULL) 205275970Scy return (ISC_R_FAILURE); 206275970Scy 207275970Scy UNUSED(maxaddrs); 208275970Scy 209275970Scy return (ISC_R_NOTIMPLEMENTED); 210275970Scy} 211275970Scy#endif 212275970Scy 213275970Scyisc_result_t 214280849Scyisc_backtrace_getsymbolfromindex(int idx, const void **addrp, 215275970Scy const char **symbolp) 216275970Scy{ 217275970Scy REQUIRE(addrp != NULL && *addrp == NULL); 218275970Scy REQUIRE(symbolp != NULL && *symbolp == NULL); 219275970Scy 220280849Scy if (idx < 0 || idx >= isc__backtrace_nsymbols) 221275970Scy return (ISC_R_RANGE); 222275970Scy 223280849Scy *addrp = isc__backtrace_symtable[idx].addr; 224280849Scy *symbolp = isc__backtrace_symtable[idx].symbol; 225275970Scy return (ISC_R_SUCCESS); 226275970Scy} 227275970Scy 228275970Scystatic int 229275970Scysymtbl_compare(const void *addr, const void *entryarg) { 230275970Scy const isc_backtrace_symmap_t *entry = entryarg; 231275970Scy const isc_backtrace_symmap_t *end = 232275970Scy &isc__backtrace_symtable[isc__backtrace_nsymbols - 1]; 233275970Scy 234275970Scy if (isc__backtrace_nsymbols == 1 || entry == end) { 235275970Scy if (addr >= entry->addr) { 236275970Scy /* 237275970Scy * If addr is equal to or larger than that of the last 238275970Scy * entry of the table, we cannot be sure if this is 239275970Scy * within a valid range so we consider it valid. 240275970Scy */ 241275970Scy return (0); 242275970Scy } 243275970Scy return (-1); 244275970Scy } 245275970Scy 246275970Scy /* entry + 1 is a valid entry from now on. */ 247275970Scy if (addr < entry->addr) 248275970Scy return (-1); 249275970Scy else if (addr >= (entry + 1)->addr) 250275970Scy return (1); 251275970Scy return (0); 252275970Scy} 253275970Scy 254275970Scyisc_result_t 255275970Scyisc_backtrace_getsymbol(const void *addr, const char **symbolp, 256275970Scy unsigned long *offsetp) 257275970Scy{ 258275970Scy isc_result_t result = ISC_R_SUCCESS; 259275970Scy isc_backtrace_symmap_t *found; 260275970Scy 261275970Scy /* 262275970Scy * Validate the arguments: intentionally avoid using REQUIRE(). 263275970Scy * See notes in backtrace.h. 264275970Scy */ 265275970Scy if (symbolp == NULL || *symbolp != NULL || offsetp == NULL) 266275970Scy return (ISC_R_FAILURE); 267275970Scy 268275970Scy if (isc__backtrace_nsymbols < 1) 269275970Scy return (ISC_R_NOTFOUND); 270275970Scy 271275970Scy /* 272275970Scy * Search the table for the entry that meets: 273275970Scy * entry.addr <= addr < next_entry.addr. 274275970Scy */ 275275970Scy found = bsearch(addr, isc__backtrace_symtable, isc__backtrace_nsymbols, 276275970Scy sizeof(isc__backtrace_symtable[0]), symtbl_compare); 277275970Scy if (found == NULL) 278275970Scy result = ISC_R_NOTFOUND; 279275970Scy else { 280275970Scy *symbolp = found->symbol; 281293650Sglebius *offsetp = (u_long)((const char *)addr - (char *)found->addr); 282275970Scy } 283275970Scy 284275970Scy return (result); 285275970Scy} 286