lib: add libunwind support for backtraces

libunwind provides an alternate to backtrace() for printing out the call
stack of a particular location.  It doesn't use the frame pointer, it
goes by the DWARF debug info.  In most cases the traces have exactly the
same information, but there are some situations where libunwind traces
are better.

(On some platforms, the libc backtrace() also uses the DWARF debug info
[e.g.: ARM backtraces are impossible without it] but this is not the
case everywhere, especially not on BSD libexecinfo.)

Signed-off-by: David Lamparter <equinox@diac24.net>
This commit is contained in:
David Lamparter 2017-08-24 16:09:48 +02:00
parent b9ea408385
commit 68b8a15f87
5 changed files with 128 additions and 19 deletions

View file

@ -4,6 +4,7 @@ AUTOMAKE_OPTIONS = subdir-objects 1.12
ACLOCAL_AMFLAGS = -I m4
AM_CFLAGS = \
$(UNWIND_CFLAGS) \
$(SAN_FLAGS) \
$(WERROR) \
# end

View file

@ -958,10 +958,6 @@ case "$host_os" in
AC_CHECK_LIB(socket, main)
AC_CHECK_LIB(nsl, main)
AC_CHECK_LIB(umem, main)
AC_CHECK_FUNCS([printstack], [
AC_DEFINE([HAVE_PRINTSTACK],1,[Solaris printstack])
AC_DEFINE([HAVE_STACK_TRACE],1,[Stack symbols decode functionality])
])
CURSES=-lcurses
SOLARIS="solaris"
;;
@ -1811,17 +1807,31 @@ dnl check for glibc 'backtrace'
dnl ---------------------------
if test x"${enable_backtrace}" != x"no" ; then
backtrace_ok=no
PKG_CHECK_MODULES([UNWIND], [libunwind], [
AC_DEFINE(HAVE_LIBUNWIND, 1, [libunwind])
backtrace_ok=yes
], [
case "$host_os" in
sunos* | solaris2*)
AC_CHECK_FUNCS([printstack], [
AC_DEFINE([HAVE_PRINTSTACK], 1, [Solaris printstack])
backtrace_ok=yes
])
;;
esac
if test "$backtrace_ok" = no; then
AC_CHECK_HEADER([execinfo.h], [
AC_SEARCH_LIBS([backtrace], [execinfo], [
AC_DEFINE(HAVE_GLIBC_BACKTRACE,,[Glibc backtrace])
AC_DEFINE(HAVE_STACK_TRACE,,[Stack symbol decoding])
AC_DEFINE(HAVE_GLIBC_BACKTRACE, 1, [Glibc backtrace])
backtrace_ok=yes
],, [-lm])
])
fi
])
if test x"${enable_backtrace}" = x"yes" -a x"${backtrace_ok}" = x"no"; then
dnl user explicitly requested backtrace but we failed to find support
AC_MSG_FAILURE([failed to find backtrace support])
AC_MSG_FAILURE([failed to find backtrace or libunwind support])
fi
fi

View file

@ -38,6 +38,12 @@
#include <ucontext.h>
#endif
#ifdef HAVE_LIBUNWIND
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <dlfcn.h>
#endif
DEFINE_MTYPE_STATIC(LIB, ZLOG, "Logging")
static int logfile_fd = -1; /* Used in signal handler. */
@ -313,7 +319,9 @@ static char *num_append(char *s, int len, unsigned long x)
return str_append(s, len, t);
}
#if defined(SA_SIGINFO) || defined(HAVE_STACK_TRACE)
#if defined(SA_SIGINFO) \
|| defined(HAVE_PRINTSTACK) \
|| defined(HAVE_GLIBC_BACKTRACE)
static char *hex_append(char *s, int len, unsigned long x)
{
char buf[30];
@ -533,7 +541,37 @@ void zlog_signal(int signo, const char *action
Needs to be enhanced to support syslog logging. */
void zlog_backtrace_sigsafe(int priority, void *program_counter)
{
#ifdef HAVE_STACK_TRACE
#ifdef HAVE_LIBUNWIND
char buf[100];
unw_cursor_t cursor;
unw_context_t uc;
unw_word_t ip, off, sp;
Dl_info dlinfo;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
while (unw_step(&cursor) > 0) {
char name[128] = "?";
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
if (unw_is_signal_frame(&cursor))
dprintf(2, " ---- signal ----\n");
if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off)) {
snprintf(name, sizeof(name), "%s+%#lx",
buf, (long)off);
}
dprintf(2, "%-30s %16lx %16lx", name, (long)ip, (long)sp);
if (dladdr((void *)ip, &dlinfo)) {
dprintf(2, " %s (mapped at %p)",
dlinfo.dli_fname, dlinfo.dli_fbase);
}
dprintf(2, "\n");
}
#elif defined(HAVE_GLIBC_BACKTRACE) || defined(HAVE_PRINTSTACK)
static const char pclabel[] = "Program counter: ";
void *array[64];
int size;
@ -624,9 +662,38 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter)
void zlog_backtrace(int priority)
{
#ifndef HAVE_GLIBC_BACKTRACE
zlog(priority, "No backtrace available on this platform.");
#else
#ifdef HAVE_LIBUNWIND
char buf[100];
unw_cursor_t cursor;
unw_context_t uc;
unw_word_t ip, off, sp;
Dl_info dlinfo;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
zlog(priority, "Backtrace:");
while (unw_step(&cursor) > 0) {
char name[128] = "?";
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
if (unw_is_signal_frame(&cursor))
zlog(priority, " ---- signal ----");
if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off))
snprintf(name, sizeof(name), "%s+%#lx",
buf, (long)off);
if (dladdr((void *)ip, &dlinfo))
zlog(priority, "%-30s %16lx %16lx %s (mapped at %p)",
name, (long)ip, (long)sp,
dlinfo.dli_fname, dlinfo.dli_fbase);
else
zlog(priority, "%-30s %16lx %16lx",
name, (long)ip, (long)sp);
}
#elif defined(HAVE_GLIBC_BACKTRACE)
void *array[20];
int size, i;
char **strings;
@ -651,7 +718,9 @@ void zlog_backtrace(int priority)
zlog(priority, "[bt %d] %s", i, strings[i]);
free(strings);
}
#endif /* HAVE_GLIBC_BACKTRACE */
#else /* !HAVE_GLIBC_BACKTRACE && !HAVE_LIBUNWIND */
zlog(priority, "No backtrace available on this platform.");
#endif
}
void zlog(int priority, const char *format, ...)

View file

@ -3,7 +3,7 @@
#
lib_LTLIBRARIES += lib/libfrr.la
lib_libfrr_la_LDFLAGS = -version-info 0:0:0 -Xlinker -e_libfrr_version
lib_libfrr_la_LIBADD = @LIBCAP@
lib_libfrr_la_LIBADD = @LIBCAP@ $(UNWIND_LIBS)
lib_libfrr_la_SOURCES = \
lib/agg_table.c \

View file

@ -32,10 +32,39 @@ struct quagga_signal_t sigs[] = {};
struct thread_master *master;
static int threadfunc(struct thread *thread)
void func1(int *arg);
void func3(void);
void func1(int *arg)
{
int *null = NULL;
*null += 1;
*arg = 1;
}
static void func2(size_t depth, int *arg)
{
/* variable stack frame size */
int buf[depth];
for (size_t i = 0; i < depth; i++)
buf[i] = arg[i] + 1;
if (depth > 0)
func2(depth - 1, buf);
else
func1(&buf[0]);
for (size_t i = 0; i < depth; i++)
buf[i] = arg[i] + 2;
}
void func3(void)
{
int buf[6];
func2(6, buf);
}
static int threadfunc(struct thread *thread)
{
func3();
return 0;
}