From: Ondřej Surý Date: Wed, 11 Mar 2020 08:55:48 +0000 (+0100) Subject: Improve the backtrace to print symbols when backtrace_symbols() is available X-Git-Tag: v9.17.1~55^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e847591867cfe542c9e52d6176e0a2bb6e2ca875;p=thirdparty%2Fbind9.git Improve the backtrace to print symbols when backtrace_symbols() is available The previous commit removed the code related to the internal symbol table. On platforms where available, we can now use backtrace_symbols() to print more verbose symbols table to the output. As there's now general availability of backtrace() and backtrace_symbols() functions (see below), the commit also removes the usage of glibc internals and the custom stack tracing. * backtrace(), backtrace_symbols(), and backtrace_symbols_fd() are provided in glibc since version 2.1. * backtrace(), backtrace_symbols(), and backtrace_symbols_fd() first appeared in Mac OS X 10.5. * The backtrace() library of functions first appeared in NetBSD 7.0 and FreeBSD 10.0. --- diff --git a/bin/named/main.c b/bin/named/main.c index d7150c47bdf..19875e2ce88 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -197,7 +197,7 @@ static void assertion_failed(const char *file, int line, isc_assertiontype_t type, const char *cond) { void *tracebuf[BACKTRACE_MAXFRAME]; - int i, nframes; + int nframes; isc_result_t result; const char *logsuffix = ""; @@ -222,12 +222,22 @@ assertion_failed(const char *file, int line, isc_assertiontype_t type, "%s:%d: %s(%s) failed%s", file, line, isc_assertion_typetotext(type), cond, logsuffix); if (result == ISC_R_SUCCESS) { - for (i = 0; i < nframes; i++) { +#if HAVE_BACKTRACE_SYMBOLS + char **strs = backtrace_symbols(tracebuf, nframes); + for (int i = 0; i < nframes; i++) { + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, + ISC_LOG_CRITICAL, "%s", strs[i]); + } +#else /* HAVE_BACKTRACE_SYMBOLS */ + for (int i = 0; i < nframes; i++) { isc_log_write( named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, "#%d %p in ??", i, tracebuf[i]); } +#endif /* HAVE_BACKTRACE_SYMBOLS */ } isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, diff --git a/bin/tests/Makefile.in b/bin/tests/Makefile.in index 49a3d3aecb0..dd3dddd438c 100644 --- a/bin/tests/Makefile.in +++ b/bin/tests/Makefile.in @@ -18,7 +18,6 @@ CINCLUDES = ${DNS_INCLUDES} ${ISC_INCLUDES} ${ISCCFG_INCLUDES} \ CDEFINES = CWARNINGS = -BACKTRACECFLAGS = @BACKTRACECFLAGS@ DNSLIBS = ../../lib/dns/libdns.@A@ ${MAXMINDDB_LIBS} @DNS_CRYPTO_LIBS@ ISCLIBS = ../../lib/isc/libisc.@A@ ${OPENSSL_LIBS} ${JSON_C_LIBS} ${LIBXML2_LIBS} ${ZLIB_LIBS} diff --git a/bin/tests/optional/Makefile.in b/bin/tests/optional/Makefile.in index 593610633a1..b01bc5e04dd 100644 --- a/bin/tests/optional/Makefile.in +++ b/bin/tests/optional/Makefile.in @@ -19,7 +19,6 @@ CINCLUDES = ${DNS_INCLUDES} ${ISC_INCLUDES} ${ISCCFG_INCLUDES} \ CDEFINES = @USE_GSSAPI@ CWARNINGS = -BACKTRACECFLAGS = @BACKTRACECFLAGS@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ DNSLIBS = ../../../lib/dns/libdns.@A@ ${MAXMINDDB_LIBS} @DNS_CRYPTO_LIBS@ @@ -97,7 +96,7 @@ XSRCS = adb_test.c \ @BIND9_MAKE_RULES@ # disable optimization for backtrace test to get the expected result -BTTEST_CFLAGS = ${BACKTRACECFLAGS} ${EXT_CFLAGS} ${ALL_CPPFLAGS} -g \ +BTTEST_CFLAGS = ${EXT_CFLAGS} ${ALL_CPPFLAGS} -g \ ${ALWAYS_WARNINGS} ${STD_CWARNINGS} ${CWARNINGS} ${PTHREAD_CFLAGS} all_tests: ${XTARGETS} diff --git a/config.h.in b/config.h.in index 4d292f2950c..463841adbcb 100644 --- a/config.h.in +++ b/config.h.in @@ -45,6 +45,12 @@ /* define if the ARM yield instruction is available */ #undef HAVE_ARM_YIELD +/* Define to 1 if you have the `backtrace' function. */ +#undef HAVE_BACKTRACE + +/* Define to 1 if you have the `backtrace_symbols' function. */ +#undef HAVE_BACKTRACE_SYMBOLS + /* Define to 1 if the compiler supports __builtin_clz. */ #undef HAVE_BUILTIN_CLZ @@ -150,6 +156,9 @@ /* Define to 1 if you have the `EVP_sha512' function. */ #undef HAVE_EVP_SHA512 +/* Define to 1 if you have the header file. */ +#undef HAVE_EXECINFO_H + /* Define to 1 if you have the `explicit_bzero' function. */ #undef HAVE_EXPLICIT_BZERO @@ -231,9 +240,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_KRB5_KRB5_H -/* define if system have backtrace function */ -#undef HAVE_LIBCTRACE - /* Define if libidn2 was found */ #undef HAVE_LIBIDN2 @@ -549,9 +555,6 @@ /* Define to use default system tuning. */ #undef TUNE_LARGE -/* define if we can use backtrace */ -#undef USE_BACKTRACE - /* Enable DNS Response Policy Service API */ #undef USE_DNSRPS diff --git a/configure b/configure index 2f3a2ceb1bd..063b6606222 100755 --- a/configure +++ b/configure @@ -775,7 +775,6 @@ XTARGETS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG -BACKTRACECFLAGS CCNOOPT CCOPT STD_CWARNINGS @@ -920,7 +919,6 @@ with_json_c with_zlib with_purify with_gperftools_profiler -enable_backtrace enable_tcp_fastopen with_readline enable_isc_spnego @@ -1626,7 +1624,6 @@ Optional Features: pthread rwlock --enable-fips-mode enable FIPS mode in OpenSSL library [default=no] --enable-native-pkcs11 use native PKCS11 for public-key crypto [default=no] - --enable-backtrace log stack backtrace on abort [default=yes] --disable-tcp-fastopen disable TCP Fast Open support [default=yes] --disable-isc-spnego use SPNEGO from GSSAPI library --disable-chroot disable chroot @@ -12088,7 +12085,6 @@ fi - # # Use pkg-config # @@ -18681,43 +18677,89 @@ $as_echo "no" >&6; } esac # -# enable/disable dumping stack backtrace. Also check if the system supports -# glibc-compatible backtrace() function. +# Check if the system supports glibc-compatible backtrace() function. # -# Check whether --enable-backtrace was given. -if test "${enable_backtrace+set}" = set; then : - enableval=$enable_backtrace; +for ac_header in execinfo.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "execinfo.h" "ac_cv_header_execinfo_h" "$ac_includes_default" +if test "x$ac_cv_header_execinfo_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_EXECINFO_H 1 +_ACEOF + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing backtrace" >&5 +$as_echo_n "checking for library containing backtrace... " >&6; } +if ${ac_cv_search_backtrace+:} false; then : + $as_echo_n "(cached) " >&6 else - enable_backtrace="yes" -fi - - -if test "$enable_backtrace" = "yes"; then : - -$as_echo "#define USE_BACKTRACE 1" >>confdefs.h - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char backtrace (); int main () { -return (backtrace((void **)0, 0)); - +return backtrace (); ; return 0; } _ACEOF -if ac_fn_c_try_link "$LINENO"; then : +for ac_lib in '' execinfo; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_backtrace=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_backtrace+:} false; then : + break +fi +done +if ${ac_cv_search_backtrace+:} false; then : -$as_echo "#define HAVE_LIBCTRACE 1" >>confdefs.h +else + ac_cv_search_backtrace=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_backtrace" >&5 +$as_echo "$ac_cv_search_backtrace" >&6; } +ac_res=$ac_cv_search_backtrace +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + for ac_func in backtrace backtrace_symbols +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF +fi +done fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext + fi +done + + +# AM_CONDITIONAL([HAVE_BACKTRACE], [test "$ac_cv_func_backtrace" = "yes"]) + # # File name extension for static archive files, for those few places # where they are treated differently from dynamic ones. diff --git a/configure.ac b/configure.ac index 81bcc0ae51c..0f051587b44 100644 --- a/configure.ac +++ b/configure.ac @@ -56,7 +56,6 @@ AC_SUBST(STD_CDEFINES) AC_SUBST(STD_CWARNINGS) AC_SUBST(CCOPT) AC_SUBST(CCNOOPT) -AC_SUBST(BACKTRACECFLAGS) # # Use pkg-config @@ -1425,23 +1424,13 @@ case $use_profiler in esac # -# enable/disable dumping stack backtrace. Also check if the system supports -# glibc-compatible backtrace() function. +# Check if the system supports glibc-compatible backtrace() function. # -AC_ARG_ENABLE([backtrace], - [AS_HELP_STRING([--enable-backtrace], - [log stack backtrace on abort [default=yes]])], - [], [enable_backtrace="yes"]) +AC_CHECK_HEADERS([execinfo.h], + [AC_SEARCH_LIBS([backtrace], [execinfo], + [AC_CHECK_FUNCS([backtrace backtrace_symbols])])]) -AS_IF([test "$enable_backtrace" = "yes"], - [AC_DEFINE([USE_BACKTRACE], [1], [define if we can use backtrace]) - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include ]], - [[return (backtrace((void **)0, 0));]] - )], - [AC_DEFINE([HAVE_LIBCTRACE], [1], [define if system have backtrace function])] - )]) +# AM_CONDITIONAL([HAVE_BACKTRACE], [test "$ac_cv_func_backtrace" = "yes"]) # # File name extension for static archive files, for those few places diff --git a/lib/isc/assertions.c b/lib/isc/assertions.c index cd5cda3b5f2..c2ca5f46a13 100644 --- a/lib/isc/assertions.c +++ b/lib/isc/assertions.c @@ -95,7 +95,7 @@ static void default_callback(const char *file, int line, isc_assertiontype_t type, const char *cond) { void *tracebuf[BACKTRACE_MAXFRAME]; - int i, nframes; + int nframes; const char *logsuffix = "."; isc_result_t result; @@ -108,9 +108,16 @@ default_callback(const char *file, int line, isc_assertiontype_t type, isc_assertion_typetotext(type), cond, logsuffix); if (result == ISC_R_SUCCESS) { - for (i = 0; i < nframes; i++) { +#if HAVE_BACKTRACE_SYMBOLS + char **strs = backtrace_symbols(tracebuf, nframes); + for (int i = 0; i < nframes; i++) { + fprintf(stderr, "%s\n", strs[i]); + } +#else /* HAVE_BACKTRACE_SYMBOLS */ + for (int i = 0; i < nframes; i++) { fprintf(stderr, "#%d %p in ??\n", i, tracebuf[i]); } +#endif /* HAVE_BACKTRACE_SYMBOLS */ } fflush(stderr); } diff --git a/lib/isc/backtrace.c b/lib/isc/backtrace.c index aafff91b6a3..54699200b6b 100644 --- a/lib/isc/backtrace.c +++ b/lib/isc/backtrace.c @@ -13,51 +13,17 @@ #include #include -#ifdef HAVE_LIBCTRACE +#ifdef HAVE_BACKTRACE #include -#endif /* ifdef HAVE_LIBCTRACE */ +#endif /* HAVE_BACKTRACE */ #include #include #include -#ifdef USE_BACKTRACE -/* - * Getting a back trace of a running process is tricky and highly platform - * dependent. Our current approach is as follows: - * 1. If the system library supports the "backtrace()" function, use it. - * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64, - * then use gcc's (hidden) Unwind_Backtrace() function. Note that this - * function doesn't work for C programs on many other architectures. - * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack - * frame following frame pointers. This assumes the executable binary - * compiled with frame pointers; this is not always true for x86_64 (rather, - * compiler optimizations often disable frame pointers). The validation - * checks in getnextframeptr() hopefully rejects bogus values stored in - * the RBP register in such a case. If the backtrace function itself crashes - * due to this problem, the whole package should be rebuilt with - * --disable-backtrace. - */ -#ifdef HAVE_LIBCTRACE -#define BACKTRACE_LIBC -#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__)) -#define BACKTRACE_GCC -#elif defined(WIN32) -#define BACKTRACE_WIN32 -#elif defined(__x86_64__) || defined(__i386__) -#define BACKTRACE_X86STACK -#else /* ifdef HAVE_LIBCTRACE */ -#define BACKTRACE_DISABLED -#endif /* HAVE_LIBCTRACE */ -#else /* USE_BACKTRACE */ -#define BACKTRACE_DISABLED -#endif /* USE_BACKTRACE */ - -#ifdef BACKTRACE_LIBC +#ifdef HAVE_BACKTRACE isc_result_t isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { - int n; - /* * Validate the arguments: intentionally avoid using REQUIRE(). * See notes in backtrace.h. @@ -70,154 +36,23 @@ isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { * backtrace(3) includes this function itself in the address array, * which should be eliminated from the returned sequence. */ - n = backtrace(addrs, maxaddrs); + int n = backtrace(addrs, maxaddrs); if (n < 2) { return (ISC_R_NOTFOUND); } n--; - memmove(addrs, &addrs[1], sizeof(void *) * n); + memmove(addrs, &addrs[1], sizeof(addrs[0]) * n); *nframes = n; return (ISC_R_SUCCESS); } -#elif defined(BACKTRACE_GCC) -extern int -_Unwind_Backtrace(void *fn, void *a); -extern void * -_Unwind_GetIP(void *ctx); - -typedef struct { - void **result; - int max_depth; - int skip_count; - int count; -} trace_arg_t; - -static int -btcallback(void *uc, void *opq) { - trace_arg_t *arg = (trace_arg_t *)opq; - - if (arg->skip_count > 0) { - arg->skip_count--; - } else { - arg->result[arg->count++] = (void *)_Unwind_GetIP(uc); - } - if (arg->count == arg->max_depth) { - return (5); /* _URC_END_OF_STACK */ - } - return (0); /* _URC_NO_REASON */ -} - -isc_result_t -isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { - trace_arg_t arg; - - /* Argument validation: see above. */ - if (addrs == NULL || nframes == NULL) { - return (ISC_R_FAILURE); - } - - arg.skip_count = 1; - arg.result = addrs; - arg.max_depth = maxaddrs; - arg.count = 0; - _Unwind_Backtrace(btcallback, &arg); - - *nframes = arg.count; - - return (ISC_R_SUCCESS); -} -#elif defined(BACKTRACE_WIN32) -isc_result_t -isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { - unsigned long ftc = (unsigned long)maxaddrs; - - *nframes = (int)CaptureStackBackTrace(1, ftc, addrs, NULL); - return (ISC_R_SUCCESS); -} -#elif defined(BACKTRACE_X86STACK) -#ifdef __x86_64__ -static unsigned long -getrbp(void) { - __asm("movq %rbp, %rax\n"); -} -#endif /* ifdef __x86_64__ */ - -static void ** -getnextframeptr(void **sp) { - void **newsp = (void **)*sp; - - /* - * Perform sanity check for the new frame pointer, derived from - * google glog. This can actually be bogus depending on compiler. - */ - - /* prohibit the stack frames from growing downwards */ - if (newsp <= sp) { - return (NULL); - } - - /* A heuristics to reject "too large" frame: this actually happened. */ - if ((char *)newsp - (char *)sp > 100000) { - return (NULL); - } - - /* - * Not sure if other checks used in glog are needed at this moment. - * For our purposes we don't have to consider non-contiguous frames, - * for example. - */ - - return (newsp); -} - -isc_result_t -isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { - int i = 0; - void **sp; - - /* Argument validation: see above. */ - if (addrs == NULL || nframes == NULL) { - return (ISC_R_FAILURE); - } -#ifdef __x86_64__ - sp = (void **)getrbp(); - if (sp == NULL) { - return (ISC_R_NOTFOUND); - } - /* - * sp is the frame ptr of this function itself due to the call to - * getrbp(), so need to unwind one frame for consistency. - */ - sp = getnextframeptr(sp); -#else /* ifdef __x86_64__ */ - /* - * i386: the frame pointer is stored 2 words below the address for the - * first argument. Note that the body of this function cannot be - * inlined since it depends on the address of the function argument. - */ - sp = (void **)&addrs - 2; -#endif /* ifdef __x86_64__ */ - - while (sp != NULL && i < maxaddrs) { - addrs[i++] = *(sp + 1); - sp = getnextframeptr(sp); - } - - *nframes = i; - - return (ISC_R_SUCCESS); -} -#elif defined(BACKTRACE_DISABLED) +#else /* HAVE_BACKTRACE */ isc_result_t isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { - /* Argument validation: see above. */ - if (addrs == NULL || nframes == NULL) { - return (ISC_R_FAILURE); - } - + UNUSED(addrs); UNUSED(maxaddrs); + UNUSED(nframes); return (ISC_R_NOTIMPLEMENTED); } -#endif /* ifdef BACKTRACE_LIBC */ +#endif /* HAVE_BACKTRACE */ diff --git a/lib/isc/include/isc/backtrace.h b/lib/isc/include/isc/backtrace.h index a7a5ff0f7a3..c59dca87b74 100644 --- a/lib/isc/include/isc/backtrace.h +++ b/lib/isc/include/isc/backtrace.h @@ -37,6 +37,10 @@ *** Imports ***/ +#if HAVE_BACKTRACE_SYMBOLS +#include +#endif /* HAVE_BACKTRACE_SYMBOLS */ + #include /***