]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: add libunwind support for backtraces
authorDavid Lamparter <equinox@opensourcerouting.org>
Thu, 24 Aug 2017 14:09:48 +0000 (16:09 +0200)
committerDavid Lamparter <equinox@opensourcerouting.org>
Thu, 18 Oct 2018 00:51:51 +0000 (02:51 +0200)
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>
Makefile.am
configure.ac
lib/log.c
lib/subdir.am
tests/lib/test_segv.c

index 65aed791522222c1c711313af3b6337784cfa6d1..4cbf111b0422478cce0b2753f3dc638d4359b993 100644 (file)
@@ -4,6 +4,7 @@ AUTOMAKE_OPTIONS = subdir-objects 1.12
 ACLOCAL_AMFLAGS = -I m4
 
 AM_CFLAGS = \
+       $(UNWIND_CFLAGS) \
        $(SAN_FLAGS) \
        $(WERROR) \
        # end
index 12100121d6e48ec5ec83fc0bad4f881bb9131d59..60d3a876a8bbef66ed535f0f019c77fa94d07b0b 100755 (executable)
@@ -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
 
index 10cb2cd8a4d44c463a534978cf1eaf99800f6c60..5cc303daf07e20688dfaedc1db76567fb63b4fdb 100644 (file)
--- a/lib/log.c
+++ b/lib/log.c
 #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, ...)
index dd2731f74c88e8865ef5132ff888c197d0039412..eef00a90862851531937e46a63b49fcb5086f44c 100644 (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 \
index 3c9b57f049f4ac5d5a38d43be93ffc1882df36bd..43ca0837d5f38e0a89b456b27c2f73d5b371c43b 100644 (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;
 }