/contrib/bind9/lib/isc/backtrace.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 285 lines · 164 code · 43 blank · 78 comment · 56 complexity · 3df113da5abd7f40e442f386f9a9a0c9 MD5 · raw file

  1. /*
  2. * Copyright (C) 2009 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. /* $Id: backtrace.c,v 1.3 2009/09/02 23:48:02 tbox Exp $ */
  17. /*! \file */
  18. #include "config.h"
  19. #include <string.h>
  20. #include <stdlib.h>
  21. #ifdef HAVE_LIBCTRACE
  22. #include <execinfo.h>
  23. #endif
  24. #include <isc/backtrace.h>
  25. #include <isc/result.h>
  26. #include <isc/util.h>
  27. #ifdef ISC_PLATFORM_USEBACKTRACE
  28. /*
  29. * Getting a back trace of a running process is tricky and highly platform
  30. * dependent. Our current approach is as follows:
  31. * 1. If the system library supports the "backtrace()" function, use it.
  32. * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64,
  33. * then use gcc's (hidden) Unwind_Backtrace() function. Note that this
  34. * function doesn't work for C programs on many other architectures.
  35. * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack
  36. * frame following frame pointers. This assumes the executable binary
  37. * compiled with frame pointers; this is not always true for x86_64 (rather,
  38. * compiler optimizations often disable frame pointers). The validation
  39. * checks in getnextframeptr() hopefully rejects bogus values stored in
  40. * the RBP register in such a case. If the backtrace function itself crashes
  41. * due to this problem, the whole package should be rebuilt with
  42. * --disable-backtrace.
  43. */
  44. #ifdef HAVE_LIBCTRACE
  45. #define BACKTRACE_LIBC
  46. #elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__))
  47. #define BACKTRACE_GCC
  48. #elif defined(__x86_64__) || defined(__i386__)
  49. #define BACKTRACE_X86STACK
  50. #else
  51. #define BACKTRACE_DISABLED
  52. #endif /* HAVE_LIBCTRACE */
  53. #else /* !ISC_PLATFORM_USEBACKTRACE */
  54. #define BACKTRACE_DISABLED
  55. #endif /* ISC_PLATFORM_USEBACKTRACE */
  56. #ifdef BACKTRACE_LIBC
  57. isc_result_t
  58. isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
  59. int n;
  60. /*
  61. * Validate the arguments: intentionally avoid using REQUIRE().
  62. * See notes in backtrace.h.
  63. */
  64. if (addrs == NULL || nframes == NULL)
  65. return (ISC_R_FAILURE);
  66. /*
  67. * backtrace(3) includes this function itself in the address array,
  68. * which should be eliminated from the returned sequence.
  69. */
  70. n = backtrace(addrs, maxaddrs);
  71. if (n < 2)
  72. return (ISC_R_NOTFOUND);
  73. n--;
  74. memmove(addrs, &addrs[1], sizeof(void *) * n);
  75. *nframes = n;
  76. return (ISC_R_SUCCESS);
  77. }
  78. #elif defined(BACKTRACE_GCC)
  79. extern int _Unwind_Backtrace(void* fn, void* a);
  80. extern void* _Unwind_GetIP(void* ctx);
  81. typedef struct {
  82. void **result;
  83. int max_depth;
  84. int skip_count;
  85. int count;
  86. } trace_arg_t;
  87. static int
  88. btcallback(void *uc, void *opq) {
  89. trace_arg_t *arg = (trace_arg_t *)opq;
  90. if (arg->skip_count > 0)
  91. arg->skip_count--;
  92. else
  93. arg->result[arg->count++] = (void *)_Unwind_GetIP(uc);
  94. if (arg->count == arg->max_depth)
  95. return (5); /* _URC_END_OF_STACK */
  96. return (0); /* _URC_NO_REASON */
  97. }
  98. isc_result_t
  99. isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
  100. trace_arg_t arg;
  101. /* Argument validation: see above. */
  102. if (addrs == NULL || nframes == NULL)
  103. return (ISC_R_FAILURE);
  104. arg.skip_count = 1;
  105. arg.result = addrs;
  106. arg.max_depth = maxaddrs;
  107. arg.count = 0;
  108. _Unwind_Backtrace(btcallback, &arg);
  109. *nframes = arg.count;
  110. return (ISC_R_SUCCESS);
  111. }
  112. #elif defined(BACKTRACE_X86STACK)
  113. #ifdef __x86_64__
  114. static unsigned long
  115. getrbp() {
  116. __asm("movq %rbp, %rax\n");
  117. }
  118. #endif
  119. static void **
  120. getnextframeptr(void **sp) {
  121. void **newsp = (void **)*sp;
  122. /*
  123. * Perform sanity check for the new frame pointer, derived from
  124. * google glog. This can actually be bogus depending on compiler.
  125. */
  126. /* prohibit the stack frames from growing downwards */
  127. if (newsp <= sp)
  128. return (NULL);
  129. /* A heuristics to reject "too large" frame: this actually happened. */
  130. if ((char *)newsp - (char *)sp > 100000)
  131. return (NULL);
  132. /*
  133. * Not sure if other checks used in glog are needed at this moment.
  134. * For our purposes we don't have to consider non-contiguous frames,
  135. * for example.
  136. */
  137. return (newsp);
  138. }
  139. isc_result_t
  140. isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
  141. int i = 0;
  142. void **sp;
  143. /* Argument validation: see above. */
  144. if (addrs == NULL || nframes == NULL)
  145. return (ISC_R_FAILURE);
  146. #ifdef __x86_64__
  147. sp = (void **)getrbp();
  148. if (sp == NULL)
  149. return (ISC_R_NOTFOUND);
  150. /*
  151. * sp is the frame ptr of this function itself due to the call to
  152. * getrbp(), so need to unwind one frame for consistency.
  153. */
  154. sp = getnextframeptr(sp);
  155. #else
  156. /*
  157. * i386: the frame pointer is stored 2 words below the address for the
  158. * first argument. Note that the body of this function cannot be
  159. * inlined since it depends on the address of the function argument.
  160. */
  161. sp = (void **)&addrs - 2;
  162. #endif
  163. while (sp != NULL && i < maxaddrs) {
  164. addrs[i++] = *(sp + 1);
  165. sp = getnextframeptr(sp);
  166. }
  167. *nframes = i;
  168. return (ISC_R_SUCCESS);
  169. }
  170. #elif defined(BACKTRACE_DISABLED)
  171. isc_result_t
  172. isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
  173. /* Argument validation: see above. */
  174. if (addrs == NULL || nframes == NULL)
  175. return (ISC_R_FAILURE);
  176. UNUSED(maxaddrs);
  177. return (ISC_R_NOTIMPLEMENTED);
  178. }
  179. #endif
  180. isc_result_t
  181. isc_backtrace_getsymbolfromindex(int index, const void **addrp,
  182. const char **symbolp)
  183. {
  184. REQUIRE(addrp != NULL && *addrp == NULL);
  185. REQUIRE(symbolp != NULL && *symbolp == NULL);
  186. if (index < 0 || index >= isc__backtrace_nsymbols)
  187. return (ISC_R_RANGE);
  188. *addrp = isc__backtrace_symtable[index].addr;
  189. *symbolp = isc__backtrace_symtable[index].symbol;
  190. return (ISC_R_SUCCESS);
  191. }
  192. static int
  193. symtbl_compare(const void *addr, const void *entryarg) {
  194. const isc_backtrace_symmap_t *entry = entryarg;
  195. const isc_backtrace_symmap_t *end =
  196. &isc__backtrace_symtable[isc__backtrace_nsymbols - 1];
  197. if (isc__backtrace_nsymbols == 1 || entry == end) {
  198. if (addr >= entry->addr) {
  199. /*
  200. * If addr is equal to or larger than that of the last
  201. * entry of the table, we cannot be sure if this is
  202. * within a valid range so we consider it valid.
  203. */
  204. return (0);
  205. }
  206. return (-1);
  207. }
  208. /* entry + 1 is a valid entry from now on. */
  209. if (addr < entry->addr)
  210. return (-1);
  211. else if (addr >= (entry + 1)->addr)
  212. return (1);
  213. return (0);
  214. }
  215. isc_result_t
  216. isc_backtrace_getsymbol(const void *addr, const char **symbolp,
  217. unsigned long *offsetp)
  218. {
  219. isc_result_t result = ISC_R_SUCCESS;
  220. isc_backtrace_symmap_t *found;
  221. /*
  222. * Validate the arguments: intentionally avoid using REQUIRE().
  223. * See notes in backtrace.h.
  224. */
  225. if (symbolp == NULL || *symbolp != NULL || offsetp == NULL)
  226. return (ISC_R_FAILURE);
  227. if (isc__backtrace_nsymbols < 1)
  228. return (ISC_R_NOTFOUND);
  229. /*
  230. * Search the table for the entry that meets:
  231. * entry.addr <= addr < next_entry.addr.
  232. */
  233. found = bsearch(addr, isc__backtrace_symtable, isc__backtrace_nsymbols,
  234. sizeof(isc__backtrace_symtable[0]), symtbl_compare);
  235. if (found == NULL)
  236. result = ISC_R_NOTFOUND;
  237. else {
  238. *symbolp = found->symbol;
  239. *offsetp = (const char *)addr - (char *)found->addr;
  240. }
  241. return (result);
  242. }