The stack traces that I get on the CI machines are not always useful, in particular because no line number information is included.
With this patch, the abort-debug module uses the libbacktrace instead of the execinfo facility. The output (on a Debian-based system) before: ../../gltests/test-floor1.c:36: assertion 'floor (0.0) == 3.0' failed Stack trace: ./test-floor1(rpl_abort+0x3d) [0x559ead41c3ad] ./test-floor1(+0x1189) [0x559ead41c189] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f2378198d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f2378198e40] ./test-floor1(_start+0x25) [0x559ead41c1b5] Aborted (core dumped) and after the patch: ../../gltests/test-floor1.c:36: assertion 'floor (0.0) == 3.0' failed Stack trace: 0x557a64e9c5f0 print_stack_trace ../../gllib/abort-debug.c:40 0x557a64e9c5f0 rpl_abort ../../gllib/abort-debug.c:94 0x557a64e9c458 main ../../gltests/test-floor1.c:36 0x7f496331bd8f __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 0x7f496331be3f __libc_start_main_impl ../csu/libc-start.c:392 0x557a64e9c484 ??? ???:0 0xffffffffffffffff ??? ???:0 Aborted (core dumped) The lines 0x557a64e9c458 main ../../gltests/test-floor1.c:36 are definitely more informative than ./test-floor1(+0x1189) [0x559ead41c189] 2024-05-18 Bruno Haible <br...@clisp.org> abort-debug: Prefer libbacktrace to execinfo. * lib/abort-debug.c: Include <backtrace.h>. (state): New variable. (print_stack_trace): Add another implementation. (_gl_pre_abort, rpl_abort): Also test HAVE_LIBBACKTRACE. * m4/abort-debug.m4 (gl_ABORT_DEBUG_EARLY): Check for libbacktrace. Set LIBS, not LDFLAGS. diff --git a/lib/abort-debug.c b/lib/abort-debug.c index ca4dbc7291..8252b66def 100644 --- a/lib/abort-debug.c +++ b/lib/abort-debug.c @@ -21,7 +21,26 @@ #include <signal.h> -#if HAVE_EXECINFO_H +#if HAVE_LIBBACKTRACE + +# include <backtrace.h> + +static struct backtrace_state *state /* = NULL */; + +static inline void +# if (__GNUC__ >= 3) || (__clang_major__ >= 4) +__attribute__ ((always_inline)) +# endif +print_stack_trace (FILE *stream) +{ + if (state == NULL) + state = backtrace_create_state (NULL, 0, NULL, NULL); + /* Pass skip=0, to work around <https://github.com/ianlancetaylor/libbacktrace/issues/60>. */ + fprintf (stream, "Stack trace:\n"); + backtrace_print (state, 0, stream); +} + +#elif HAVE_EXECINFO_H # include <stdio.h> @@ -58,7 +77,7 @@ print_stack_trace (FILE *stream) void _gl_pre_abort (void) { -#if HAVE_EXECINFO_H +#if HAVE_LIBBACKTRACE || HAVE_EXECINFO_H print_stack_trace (stderr); #endif } @@ -71,7 +90,7 @@ _gl_pre_abort (void) void rpl_abort (void) { -#if HAVE_EXECINFO_H +#if HAVE_LIBBACKTRACE || HAVE_EXECINFO_H print_stack_trace (stderr); #endif raise (SIGABRT); diff --git a/m4/abort-debug.m4 b/m4/abort-debug.m4 index e819d58dcb..61716e5a06 100644 --- a/m4/abort-debug.m4 +++ b/m4/abort-debug.m4 @@ -1,5 +1,5 @@ # abort-debug.m4 -# serial 1 +# serial 2 dnl Copyright (C) 2024 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -23,26 +23,55 @@ AC_DEFUN([gl_ABORT_DEBUG_EARLY] AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) if test $enable_debug_abort = yes; then - AC_REQUIRE([AC_CANONICAL_HOST]) - case "$host_os" in - *-gnu* | gnu* | darwin* | freebsd* | dragonfly* | netbsd* | openbsd* | solaris*) - dnl execinfo might be implemented on this platform. - REPLACE_ABORT=1 - dnl On *BSD system, link all programs with -lexecinfo. Cf. m4/execinfo.m4. - case "$host_os" in - freebsd* | dragonfly* | netbsd* | openbsd*) - LDFLAGS="$LDFLAGS -lexecinfo" - ;; - esac - dnl Link all programs in such a way that the stack trace includes the - dnl function names. '-rdynamic' is equivalent to '-Wl,-export-dynamic'. - case "$host_os" in - *-gnu* | gnu* | openbsd*) - LDFLAGS="$LDFLAGS -rdynamic" - ;; - esac - ;; - esac + dnl The first choice is libbacktrace by Ian Lance Taylor. + dnl Maintained at https://github.com/ianlancetaylor/libbacktrace, + dnl mirrored into GCC, installed as part of GCC by a few distros. + dnl It produces source file names and line numbers, if the binary + dnl is compiled with debug information. + AC_CACHE_CHECK([for libbacktrace], [gl_cv_lib_backtrace], [ + gl_saved_LIBS="$LIBS" + LIBS="$gl_saved_LIBS -lbacktrace" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include <backtrace.h> + ]], + [[struct backtrace_state *state = + backtrace_create_state (NULL, 0, NULL, NULL); + ]])], + [gl_cv_lib_backtrace=yes], + [gl_cv_lib_backtrace=no]) + LIBS="$gl_saved_LIBS" + ]) + if test $gl_cv_lib_backtrace = yes; then + AC_DEFINE([HAVE_LIBBACKTRACE], [1], + [Define if you have the libbacktrace library.]) + REPLACE_ABORT=1 + LIBS="$LIBS -lbacktrace" + else + dnl The second choice is libexecinfo. + dnl It does not produce source file names and line numbers, only addresses + dnl (which are mostly useless due to ASLR) and _sometimes_ function names. + AC_REQUIRE([AC_CANONICAL_HOST]) + case "$host_os" in + *-gnu* | gnu* | darwin* | freebsd* | dragonfly* | netbsd* | openbsd* | solaris*) + dnl execinfo might be implemented on this platform. + REPLACE_ABORT=1 + dnl On *BSD system, link all programs with -lexecinfo. Cf. m4/execinfo.m4. + case "$host_os" in + freebsd* | dragonfly* | netbsd* | openbsd*) + LIBS="$LIBS -lexecinfo" + ;; + esac + dnl Link all programs in such a way that the stack trace includes the + dnl function names. '-rdynamic' is equivalent to '-Wl,-export-dynamic'. + case "$host_os" in + *-gnu* | gnu* | openbsd*) + LDFLAGS="$LDFLAGS -rdynamic" + ;; + esac + ;; + esac + fi fi ])