backtrace.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009, 2013-2015  Internet Systems Consortium, Inc. ("ISC")
00003  *
00004  * Permission to use, copy, modify, and/or distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00009  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00010  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00011  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00012  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00013  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00014  * PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 /* $Id: backtrace.c,v 1.3 2009/09/02 23:48:02 tbox Exp $ */
00018 
00019 /*! \file */
00020 
00021 #include "config.h"
00022 
00023 #include <string.h>
00024 #include <stdlib.h>
00025 #ifdef HAVE_LIBCTRACE
00026 #include <execinfo.h>
00027 #endif
00028 
00029 #include <isc/backtrace.h>
00030 #include <isc/result.h>
00031 #include <isc/util.h>
00032 
00033 #ifdef ISC_PLATFORM_USEBACKTRACE
00034 /*
00035  * Getting a back trace of a running process is tricky and highly platform
00036  * dependent.  Our current approach is as follows:
00037  * 1. If the system library supports the "backtrace()" function, use it.
00038  * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64,
00039  *    then use gcc's (hidden) Unwind_Backtrace() function.  Note that this
00040  *    function doesn't work for C programs on many other architectures.
00041  * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack
00042  *    frame following frame pointers.  This assumes the executable binary
00043  *    compiled with frame pointers; this is not always true for x86_64 (rather,
00044  *    compiler optimizations often disable frame pointers).  The validation
00045  *    checks in getnextframeptr() hopefully rejects bogus values stored in
00046  *    the RBP register in such a case.  If the backtrace function itself crashes
00047  *    due to this problem, the whole package should be rebuilt with
00048  *    --disable-backtrace.
00049  */
00050 #ifdef HAVE_LIBCTRACE
00051 #define BACKTRACE_LIBC
00052 #elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__))
00053 #define BACKTRACE_GCC
00054 #elif defined(WIN32)
00055 #define BACKTRACE_WIN32
00056 #elif defined(__x86_64__) || defined(__i386__)
00057 #define BACKTRACE_X86STACK
00058 #else
00059 #define BACKTRACE_DISABLED
00060 #endif  /* HAVE_LIBCTRACE */
00061 #else   /* !ISC_PLATFORM_USEBACKTRACE */
00062 #define BACKTRACE_DISABLED
00063 #endif  /* ISC_PLATFORM_USEBACKTRACE */
00064 
00065 #ifdef BACKTRACE_LIBC
00066 isc_result_t
00067 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
00068         int n;
00069 
00070         /*
00071          * Validate the arguments: intentionally avoid using REQUIRE().
00072          * See notes in backtrace.h.
00073          */
00074         if (addrs == NULL || nframes == NULL)
00075                 return (ISC_R_FAILURE);
00076 
00077         /*
00078          * backtrace(3) includes this function itself in the address array,
00079          * which should be eliminated from the returned sequence.
00080          */
00081         n = backtrace(addrs, maxaddrs);
00082         if (n < 2)
00083                 return (ISC_R_NOTFOUND);
00084         n--;
00085         memmove(addrs, &addrs[1], sizeof(void *) * n);
00086         *nframes = n;
00087         return (ISC_R_SUCCESS);
00088 }
00089 #elif defined(BACKTRACE_GCC)
00090 extern int _Unwind_Backtrace(void* fn, void* a);
00091 extern void* _Unwind_GetIP(void* ctx);
00092 
00093 typedef struct {
00094         void **result;
00095         int max_depth;
00096         int skip_count;
00097         int count;
00098 } trace_arg_t;
00099 
00100 static int
00101 btcallback(void *uc, void *opq) {
00102         trace_arg_t *arg = (trace_arg_t *)opq;
00103 
00104         if (arg->skip_count > 0)
00105                 arg->skip_count--;
00106         else
00107                 arg->result[arg->count++] = (void *)_Unwind_GetIP(uc);
00108         if (arg->count == arg->max_depth)
00109                 return (5); /* _URC_END_OF_STACK */
00110 
00111         return (0); /* _URC_NO_REASON */
00112 }
00113 
00114 isc_result_t
00115 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
00116         trace_arg_t arg;
00117 
00118         /* Argument validation: see above. */
00119         if (addrs == NULL || nframes == NULL)
00120                 return (ISC_R_FAILURE);
00121 
00122         arg.skip_count = 1;
00123         arg.result = addrs;
00124         arg.max_depth = maxaddrs;
00125         arg.count = 0;
00126         _Unwind_Backtrace(btcallback, &arg);
00127 
00128         *nframes = arg.count;
00129 
00130         return (ISC_R_SUCCESS);
00131 }
00132 #elif defined(BACKTRACE_WIN32)
00133 isc_result_t
00134 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
00135         unsigned long ftc = (unsigned long)maxaddrs;
00136 
00137         *nframes = (int)CaptureStackBackTrace(1, ftc, addrs, NULL);
00138         return ISC_R_SUCCESS;
00139 }
00140 #elif defined(BACKTRACE_X86STACK)
00141 #ifdef __x86_64__
00142 static unsigned long
00143 getrbp(void) {
00144         __asm("movq %rbp, %rax\n");
00145 }
00146 #endif
00147 
00148 static void **
00149 getnextframeptr(void **sp) {
00150         void **newsp = (void **)*sp;
00151 
00152         /*
00153          * Perform sanity check for the new frame pointer, derived from
00154          * google glog.  This can actually be bogus depending on compiler.
00155          */
00156 
00157         /* prohibit the stack frames from growing downwards */
00158         if (newsp <= sp)
00159                 return (NULL);
00160 
00161         /* A heuristics to reject "too large" frame: this actually happened. */
00162         if ((char *)newsp - (char *)sp > 100000)
00163                 return (NULL);
00164 
00165         /*
00166          * Not sure if other checks used in glog are needed at this moment.
00167          * For our purposes we don't have to consider non-contiguous frames,
00168          * for example.
00169          */
00170 
00171         return (newsp);
00172 }
00173 
00174 isc_result_t
00175 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
00176         int i = 0;
00177         void **sp;
00178 
00179         /* Argument validation: see above. */
00180         if (addrs == NULL || nframes == NULL)
00181                 return (ISC_R_FAILURE);
00182 
00183 #ifdef __x86_64__
00184         sp = (void **)getrbp();
00185         if (sp == NULL)
00186                 return (ISC_R_NOTFOUND);
00187         /*
00188          * sp is the frame ptr of this function itself due to the call to
00189          * getrbp(), so need to unwind one frame for consistency.
00190          */
00191         sp = getnextframeptr(sp);
00192 #else
00193         /*
00194          * i386: the frame pointer is stored 2 words below the address for the
00195          * first argument.  Note that the body of this function cannot be
00196          * inlined since it depends on the address of the function argument.
00197          */
00198         sp = (void **)&addrs - 2;
00199 #endif
00200 
00201         while (sp != NULL && i < maxaddrs) {
00202                 addrs[i++] = *(sp + 1);
00203                 sp = getnextframeptr(sp);
00204         }
00205 
00206         *nframes = i;
00207 
00208         return (ISC_R_SUCCESS);
00209 }
00210 #elif defined(BACKTRACE_DISABLED)
00211 isc_result_t
00212 isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
00213         /* Argument validation: see above. */
00214         if (addrs == NULL || nframes == NULL)
00215                 return (ISC_R_FAILURE);
00216 
00217         UNUSED(maxaddrs);
00218 
00219         return (ISC_R_NOTIMPLEMENTED);
00220 }
00221 #endif
00222 
00223 isc_result_t
00224 isc_backtrace_getsymbolfromindex(int idx, const void **addrp,
00225                                  const char **symbolp)
00226 {
00227         REQUIRE(addrp != NULL && *addrp == NULL);
00228         REQUIRE(symbolp != NULL && *symbolp == NULL);
00229 
00230         if (idx < 0 || idx >= isc__backtrace_nsymbols)
00231                 return (ISC_R_RANGE);
00232 
00233         *addrp = isc__backtrace_symtable[idx].addr;
00234         *symbolp = isc__backtrace_symtable[idx].symbol;
00235         return (ISC_R_SUCCESS);
00236 }
00237 
00238 static int
00239 symtbl_compare(const void *addr, const void *entryarg) {
00240         const isc_backtrace_symmap_t *entry = entryarg;
00241         const isc_backtrace_symmap_t *end =
00242                 &isc__backtrace_symtable[isc__backtrace_nsymbols - 1];
00243 
00244         if (isc__backtrace_nsymbols == 1 || entry == end) {
00245                 if (addr >= entry->addr) {
00246                         /*
00247                          * If addr is equal to or larger than that of the last
00248                          * entry of the table, we cannot be sure if this is
00249                          * within a valid range so we consider it valid.
00250                          */
00251                         return (0);
00252                 }
00253                 return (-1);
00254         }
00255 
00256         /* entry + 1 is a valid entry from now on. */
00257         if (addr < entry->addr)
00258                 return (-1);
00259         else if (addr >= (entry + 1)->addr)
00260                 return (1);
00261         return (0);
00262 }
00263 
00264 isc_result_t
00265 isc_backtrace_getsymbol(const void *addr, const char **symbolp,
00266                         unsigned long *offsetp)
00267 {
00268         isc_result_t result = ISC_R_SUCCESS;
00269         isc_backtrace_symmap_t *found;
00270 
00271         /*
00272          * Validate the arguments: intentionally avoid using REQUIRE().
00273          * See notes in backtrace.h.
00274          */
00275         if (symbolp == NULL || *symbolp != NULL || offsetp == NULL)
00276                 return (ISC_R_FAILURE);
00277 
00278         if (isc__backtrace_nsymbols < 1)
00279                 return (ISC_R_NOTFOUND);
00280 
00281         /*
00282          * Search the table for the entry that meets:
00283          * entry.addr <= addr < next_entry.addr.
00284          */
00285         found = bsearch(addr, isc__backtrace_symtable, isc__backtrace_nsymbols,
00286                         sizeof(isc__backtrace_symtable[0]), symtbl_compare);
00287         if (found == NULL)
00288                 result = ISC_R_NOTFOUND;
00289         else {
00290                 *symbolp = found->symbol;
00291                 *offsetp = (unsigned long) ((const char *)addr -
00292                                             (char *)found->addr);
00293         }
00294 
00295         return (result);
00296 }

Generated on Tue Apr 28 17:41:03 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha