diff --git a/Makefile.am b/Makefile.am index 65aed79152..4cbf111b04 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,6 +4,7 @@ AUTOMAKE_OPTIONS = subdir-objects 1.12 ACLOCAL_AMFLAGS = -I m4 AM_CFLAGS = \ + $(UNWIND_CFLAGS) \ $(SAN_FLAGS) \ $(WERROR) \ # end diff --git a/configure.ac b/configure.ac index 12100121d6..60d3a876a8 100755 --- a/configure.ac +++ b/configure.ac @@ -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 - 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]) - backtrace_ok=yes - ],, [-lm]) + 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, 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 diff --git a/lib/log.c b/lib/log.c index 10cb2cd8a4..5cc303daf0 100644 --- a/lib/log.c +++ b/lib/log.c @@ -38,6 +38,12 @@ #include #endif +#ifdef HAVE_LIBUNWIND +#define UNW_LOCAL_ONLY +#include +#include +#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, ...) diff --git a/lib/subdir.am b/lib/subdir.am index dd2731f74c..eef00a9086 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -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 \ diff --git a/tests/lib/test_segv.c b/tests/lib/test_segv.c index 3c9b57f049..43ca0837d5 100644 --- a/tests/lib/test_segv.c +++ b/tests/lib/test_segv.c @@ -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; }