libspl/assert: use libunwind for backtrace when available

libunwind seems to do a better job of resolving a symbols than
backtrace(), and is also useful on platforms that don't have backtrace()
(eg musl). If it's available, use it.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Rob Norris <robn@despairlabs.com>
Sponsored-by: https://despairlabs.com/sponsor/
Closes #16140
This commit is contained in:
Rob Norris 2024-04-30 10:37:29 +10:00 committed by Brian Behlendorf
parent 2152c405ba
commit 051460b8b2
4 changed files with 79 additions and 3 deletions

44
config/user-libunwind.m4 Normal file
View File

@ -0,0 +1,44 @@
dnl
dnl Checks for libunwind, which usually does a better job than backtrace() when
dnl resolving symbols in the stack backtrace. Newer versions have support for
dnl getting info about the object file the function came from, so we look for
dnl that too and use it if found.
dnl
AC_DEFUN([ZFS_AC_CONFIG_USER_LIBUNWIND], [
AC_ARG_WITH([libunwind],
AS_HELP_STRING([--with-libunwind],
[use libunwind for backtraces in userspace assertions]),
[],
[with_libunwind=auto])
AS_IF([test "x$with_libunwind" != "xno"], [
ZFS_AC_FIND_SYSTEM_LIBRARY(LIBUNWIND, [libunwind], [libunwind.h], [], [unwind], [], [
dnl unw_get_elf_filename() is sometimes a macro, other
dnl times a proper symbol, so we can't just do a link
dnl check; we need to include the header properly.
AX_SAVE_FLAGS
CFLAGS="$CFLAGS $LIBUNWIND_CFLAGS"
LIBS="$LIBS $LIBUNWIND_LIBS"
AC_MSG_CHECKING([for unw_get_elf_filename in libunwind])
AC_LINK_IFELSE([
AC_LANG_PROGRAM([
#define UNW_LOCAL_ONLY
#include <libunwind.h>
], [
unw_get_elf_filename(0, 0, 0, 0);
])
], [
AC_MSG_RESULT([yes])
AC_DEFINE(HAVE_LIBUNWIND_ELF, 1,
[libunwind has unw_get_elf_filename])
], [
AC_MSG_RESULT([no])
])
AX_RESTORE_FLAGS
], [
AS_IF([test "x$with_libunwind" = "xyes"], [
AC_MSG_FAILURE([--with-libunwind was given, but libunwind is not available, try installing libunwind-devel])
])
])
])
])

View File

@ -27,6 +27,7 @@ AC_DEFUN([ZFS_AC_CONFIG_USER], [
ZFS_AC_CONFIG_USER_CLOCK_GETTIME
ZFS_AC_CONFIG_USER_PAM
ZFS_AC_CONFIG_USER_BACKTRACE
ZFS_AC_CONFIG_USER_LIBUNWIND
ZFS_AC_CONFIG_USER_RUNSTATEDIR
ZFS_AC_CONFIG_USER_MAKEDEV_IN_SYSMACROS
ZFS_AC_CONFIG_USER_MAKEDEV_IN_MKDEV

View File

@ -1,6 +1,6 @@
include $(srcdir)/%D%/include/Makefile.am
libspl_assert_la_CFLAGS = $(AM_CFLAGS) $(LIBRARY_CFLAGS)
libspl_assert_la_CFLAGS = $(AM_CFLAGS) $(LIBRARY_CFLAGS) $(LIBUNWIND_CFLAGS)
libspl_la_CFLAGS = $(libspl_assert_la_CFLAGS)
noinst_LTLIBRARIES += libspl_assert.la libspl.la
@ -44,4 +44,4 @@ libspl_la_LIBADD = \
libspl_la_LIBADD += $(LIBATOMIC_LIBS) $(LIBCLOCK_GETTIME)
libspl_assert_la_LIBADD = $(BACKTRACE_LIBS)
libspl_assert_la_LIBADD = $(BACKTRACE_LIBS) $(LIBUNWIND_LIBS)

View File

@ -49,7 +49,38 @@
pthread_getname_np(pthread_self(), buf, len);
#endif
#if defined(HAVE_BACKTRACE)
#if defined(HAVE_LIBUNWIND)
#define UNW_LOCAL_ONLY
#include <libunwind.h>
static inline void
libspl_dump_backtrace(void)
{
unw_context_t uc;
unw_cursor_t cp;
unw_word_t ip, off;
char funcname[128];
#ifdef HAVE_LIBUNWIND_ELF
char objname[128];
unw_word_t objoff;
#endif
fprintf(stderr, "Call trace:\n");
unw_getcontext(&uc);
unw_init_local(&cp, &uc);
while (unw_step(&cp) > 0) {
unw_get_reg(&cp, UNW_REG_IP, &ip);
unw_get_proc_name(&cp, funcname, sizeof (funcname), &off);
#ifdef HAVE_LIBUNWIND_ELF
unw_get_elf_filename(&cp, objname, sizeof (objname), &objoff);
fprintf(stderr, " [0x%08lx] %s+0x%2lx (in %s +0x%2lx)\n",
ip, funcname, off, objname, objoff);
#else
fprintf(stderr, " [0x%08lx] %s+0x%2lx\n", ip, funcname, off);
#endif
}
}
#elif defined(HAVE_BACKTRACE)
#include <execinfo.h>
static inline void