Commit e8475918 authored by Ondřej Surý's avatar Ondřej Surý

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.
parent ad5250ff
Pipeline #36329 failed with stages
in 18 minutes and 8 seconds
......@@ -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,
......
......@@ -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}
......
......@@ -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}
......
......@@ -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 <execinfo.h> 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 <krb5/krb5.h> 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
......
......@@ -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 <execinfo.h>
/* 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.
......
......@@ -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 <execinfo.h>]],
[[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
......
......@@ -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);
}
......@@ -13,51 +13,17 @@
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_LIBCTRACE
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#endif /* ifdef HAVE_LIBCTRACE */
#endif /* HAVE_BACKTRACE */
#include <isc/backtrace.h>
#include <isc/result.h>
#include <isc/util.h>
#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 */
......@@ -37,6 +37,10 @@
*** Imports
***/
#if HAVE_BACKTRACE_SYMBOLS
#include <execinfo.h>
#endif /* HAVE_BACKTRACE_SYMBOLS */
#include <isc/types.h>
/***
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment