libspl/assert: dump backtrace in assert

Adds a check for the backtrace() function. If available, uses it to show
a stack backtrace in the assertion output.

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-27 21:35:05 +10:00 committed by Tony Hutter
parent 3ca305f873
commit 21f66db674
4 changed files with 37 additions and 0 deletions

14
config/user-backtrace.m4 Normal file
View File

@ -0,0 +1,14 @@
dnl
dnl backtrace(), for userspace assertions. glibc has this directly in libc.
dnl FreeBSD and (sometimes) musl have it in a separate -lexecinfo. It's assumed
dnl that this will also get the companion function backtrace_symbols().
dnl
AC_DEFUN([ZFS_AC_CONFIG_USER_BACKTRACE], [
AX_SAVE_FLAGS
LIBS=""
AC_SEARCH_LIBS([backtrace], [execinfo], [
AC_DEFINE(HAVE_BACKTRACE, 1, [backtrace() is available])
AC_SUBST([BACKTRACE_LIBS], ["$LIBS"])
])
AX_RESTORE_FLAGS
])

View File

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

View File

@ -43,3 +43,5 @@ libspl_la_LIBADD = \
libspl_assert.la libspl_assert.la
libspl_la_LIBADD += $(LIBATOMIC_LIBS) $(LIBCLOCK_GETTIME) libspl_la_LIBADD += $(LIBATOMIC_LIBS) $(LIBCLOCK_GETTIME)
libspl_assert_la_LIBADD = $(BACKTRACE_LIBS)

View File

@ -49,6 +49,24 @@
pthread_getname_np(pthread_self(), buf, len); pthread_getname_np(pthread_self(), buf, len);
#endif #endif
#if defined(HAVE_BACKTRACE)
#include <execinfo.h>
static inline void
libspl_dump_backtrace(void)
{
void *btptrs[100];
size_t nptrs = backtrace(btptrs, 100);
char **bt = backtrace_symbols(btptrs, nptrs);
fprintf(stderr, "Call trace:\n");
for (size_t i = 0; i < nptrs; i++)
fprintf(stderr, " %s\n", bt[i]);
free(bt);
}
#else
#define libspl_dump_backtrace()
#endif
static boolean_t libspl_assert_ok = B_FALSE; static boolean_t libspl_assert_ok = B_FALSE;
void void
@ -83,6 +101,8 @@ libspl_assertf(const char *file, const char *func, int line,
getpid(), libspl_getprogname(), getpid(), libspl_getprogname(),
libspl_gettid(), tname); libspl_gettid(), tname);
libspl_dump_backtrace();
#if !__has_feature(attribute_analyzer_noreturn) && !defined(__COVERITY__) #if !__has_feature(attribute_analyzer_noreturn) && !defined(__COVERITY__)
if (libspl_assert_ok) { if (libspl_assert_ok) {
pthread_mutex_unlock(&assert_lock); pthread_mutex_unlock(&assert_lock);