endif
LIBFREERADIUS_SERVER += libfreeradius-server.a libfreeradius-unlang.a
+WITH_BACKTRACE = @WITH_BACKTRACE@
+
ifneq ($(WITH_OPENSSL_MD5),)
WITH_OPENSSL_DIGEST = 1
CFLAGS += -DWITH_OPENSSL_MD5
CPP
LIBREADLINE_PREFIX
LIBREADLINE
+WITH_BACKTRACE
KQUEUE_LDFLAGS
KQUEUE_LIBS
TALLOC_LDFLAGS
with_talloc_lib_dir
with_talloc_include_dir
with_regex
+with_libbacktrace
with_libcap
enable_year2038
'
available(default=yes)
--with-pcap use pcap library for the RADIUS sniffer. (default=yes)
--with-collectdclient use collectd client. (default=yes)
+ --with-libbacktrace use libbacktrace to print backtraces with line numbers. (default=yes)
--with-libcap use libcap for debugger checks. (default=yes)
Some influential environment variables:
allow_core_dumps="no"
fi
+
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build commit" >&5
printf %s "checking build commit... " >&6; }
RADIUSD_VERSION_COMMIT=`./version.sh commit`
*)
fuzzer=yes
CFLAGS="$CFLAGS -g3"
+ ;;
esac
fi
LIBS="${old_LIBS}"
fi
+
+# Check whether --with-libbacktrace was given.
+if test ${with_libbacktrace+y}
+then :
+ withval=$with_libbacktrace; case "$withval" in
+ no)
+ WITH_BACKTRACE=no
+ ;;
+ yes)
+ if ! test -f src/lib/backtrace/configure || git submodule update --init src/lib/backtrace > /dev/null 2>&1; then
+ as_fn_error $? "--with-libbacktrace=yes requires the backtrace submodule to be checked out. Please run 'git submodule update --init src/lib/backtrace'." "$LINENO" 5
+ fi
+ WITH_BACKTRACE=yes
+ ;;
+ *)
+ if test -f src/lib/backtrace/configure; then
+ WITH_BACKTRACE=yes
+ else
+ WITH_BACKTRACE=no
+ fi
+ esac
+
+fi
+
+
+if test "x$WITH_BACKTRACE" = xyes; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: building with libbacktrace, this will be statically linked" >&5
+printf "%s\n" "$as_me: building with libbacktrace, this will be statically linked" >&6;}
+
+printf "%s\n" "#define HAVE_BACKTRACE 1" >>confdefs.h
+
+ WITH_BACKTRACE=$WITH_BACKTRACE
+
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: building without libbacktrace" >&5
+printf "%s\n" "$as_me: building without libbacktrace" >&6;}
+fi
+
WITH_LIBCAP=yes
# Check whether --with-libcap was given.
LIBS="${old_LIBS}"
fi
+dnl #
+dnl # extra argument: --with-libbacktrace
+dnl #
+AC_ARG_WITH(libbacktrace,
+[ --with-libbacktrace use libbacktrace to print backtraces with line numbers. (default=yes)],
+[ case "$withval" in
+ no)
+ WITH_BACKTRACE=no
+ ;;
+ yes)
+ if ! test -f src/lib/backtrace/configure || git submodule update --init src/lib/backtrace > /dev/null 2>&1; then
+ AC_MSG_ERROR([--with-libbacktrace=yes requires the backtrace submodule to be checked out. Please run 'git submodule update --init src/lib/backtrace'.])
+ fi
+ WITH_BACKTRACE=yes
+ ;;
+ dnl # Build based on whether the submodule was checked out
+ *)
+ if test -f src/lib/backtrace/configure; then
+ WITH_BACKTRACE=yes
+ else
+ WITH_BACKTRACE=no
+ fi
+ esac ]
+)
+
+dnl #
+dnl # Check for libbacktrace
+dnl #
+dnl # This is a weird one because it's intended to be bundled with the code that
+dnl # uses it and statically linked. There's no guarantee the user checked out
+dnl # or was able to check out the submodule though, so we still need to fail
+dnl # gracefully.
+dnl #
+if test "x$WITH_BACKTRACE" = xyes; then
+ AC_MSG_NOTICE([building with libbacktrace, this will be statically linked])
+ AC_DEFINE(HAVE_BACKTRACE, 1,
+ [Define to 1 if you have the `backtrace' library (-lbacktrace).]
+ )
+ AC_SUBST([WITH_BACKTRACE], $WITH_BACKTRACE)
+else
+ AC_MSG_NOTICE([building without libbacktrace])
+fi
+
dnl #
dnl # extra argument: --with-libcap
dnl #
/* Define to 1 if you have the <arpa/inet.h> header file. */
#undef HAVE_ARPA_INET_H
+/* Define to 1 if you have the `backtrace' library (-lbacktrace). */
+#undef HAVE_BACKTRACE
+
/* Define to 1 if you have the 'bindat' function. */
#undef HAVE_BINDAT
--- /dev/null
+#include <dlfcn.h>
+
+#include <freeradius-devel/util/backtrace.h>
+#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/fring.h>
+#include <freeradius-devel/util/misc.h>
+
+#ifdef HAVE_BACKTRACE
+# include <freeradius-devel/backtrace/backtrace.h>
+
+struct backtrace_state *backtrace_state = NULL; //!< Backtrace state for the backtrace functions
+ ///< This is initialised to be thread-safe, so we only need one.
+
+/** Used when building without libbacktrace to record frame information
+ */
+typedef struct {
+ char const *library; //!< Backtrace library name.
+ char const *filename; //!< Backtrace file.
+ char const *function; //!< Backtrace function.
+ bool function_guess; //!< Whether dladdr guessed the function.
+ //!< This is true if the function name is not in the
+ //!< symbol table, but was guessed from the program counter.
+ unsigned int lineno; //!< Backtrace line number.
+ unsigned int frameno; //!< Backtrace frame number.
+ uintptr_t pc; //!< Backtrace program counter.
+} fr_bt_info_frame_t;
+#elif defined(HAVE_EXECINFO)
+# include <execinfo.h>
+#endif
+
+# ifndef MAX_BT_FRAMES
+# define MAX_BT_FRAMES 128
+# endif
+# ifndef MAX_BT_CBUFF
+# define MAX_BT_CBUFF 1048576 //!< Should be a power of 2
+# endif
+
+static pthread_mutex_t fr_backtrace_lock = PTHREAD_MUTEX_INITIALIZER;
+
+typedef struct {
+ void *obj; //!< Memory address of the block of allocated memory.
+#ifdef HAVE_BACKTRACE
+ fr_bt_info_frame_t *frames[MAX_BT_FRAMES]; //!< Backtrace frame data
+#else
+ void *frames[MAX_BT_FRAMES]; //!< Backtrace frame data
+#endif
+ int count; //!< Number of frames stored
+} fr_bt_info_t;
+
+struct fr_bt_marker {
+ void *obj; //!< Pointer to the parent object, this is our needle
+ //!< when we iterate over the contents of the circular buffer.
+ fr_fring_t *fring; //!< Where we temporarily store the backtraces
+};
+
+#ifdef HAVE_BACKTRACE
+/** Log faults from libbacktrace
+ *
+ */
+static void _backtrace_error(UNUSED void *data, const char *msg, int errnum)
+{
+ FR_FAULT_LOG("Backtrace error: %s (%d)", msg, errnum);
+}
+
+static void backtrace_info_sanitise(fr_bt_info_frame_t *info)
+{
+ Dl_info dl_info;
+
+ if (dladdr((void *)info->pc, &dl_info) != 0) {
+ info->library = dl_info.dli_fname;
+ if (!info->function) {
+ info->function = dl_info.dli_sname;
+ info->function_guess = true;
+ }
+ }
+}
+
+static void backtrace_info_print(fr_bt_info_frame_t *frame, int fd, bool trim_path)
+{
+ if (!frame->library && !frame->filename) {
+ dprintf(fd, "%u: @ 0x%lx\n",
+ frame->frameno,
+ (unsigned long)frame->pc);
+ return;
+ }
+ else if (!frame->filename) {
+ dprintf(fd, "%u: %s %s() @ 0x%lx\n",
+ frame->frameno,
+ trim_path ? fr_filename(frame->library) : frame->library,
+ frame->function,
+ (unsigned long)frame->pc);
+ return;
+ }
+ dprintf(fd, "%u: %s:%d %s()%s @ 0x%lx\n",
+ frame->frameno,
+ trim_path ? fr_filename_common_trim(frame->filename, TOP_SRCDIR) : frame->filename,
+ frame->lineno,
+ frame->function,
+ frame->function_guess ? "?" : "",
+ (unsigned long)frame->pc);
+}
+
+static int _backtrace_info_record(void *data, uintptr_t pc,
+ const char *filename, int lineno,
+ const char *function)
+{
+ fr_bt_info_t *info = talloc_get_type_abort(data, fr_bt_info_t);
+ fr_bt_info_frame_t *frame;
+
+ if (info->count >= (int)NUM_ELEMENTS(info->frames)) return 0;
+
+ frame = talloc_zero(info, fr_bt_info_frame_t);
+ if (!frame) return -1;
+
+ frame->filename = talloc_strdup(frame, filename);
+ frame->function = talloc_strdup(frame, function);
+ frame->lineno = lineno;
+ frame->frameno = info->count;
+ frame->pc = pc;
+
+ backtrace_info_sanitise(frame);
+
+ info->frames[info->count++] = frame;
+
+ return 0;
+}
+
+static void backtrace_record(fr_bt_info_t *info)
+{
+ backtrace_full(backtrace_state, 0, _backtrace_info_record, _backtrace_error, info);
+}
+
+static int _backtrace_print(void *data, uintptr_t pc,
+ const char *filename, int lineno,
+ const char *function)
+{
+ unsigned int *frame_no = ((unsigned int *)data);
+ fr_bt_info_frame_t frame = {
+ .filename = filename,
+ .lineno = lineno,
+ .function = function,
+ .frameno = *frame_no,
+ .pc = pc,
+ };
+
+ backtrace_info_sanitise(&frame);
+ backtrace_info_print(&frame, fr_fault_log_fd, true);
+
+ (*frame_no)++;
+ return 0;
+}
+
+void fr_backtrace(void)
+{
+ unsigned int frame = 0;
+
+ if (fr_fault_log_fd >= 0) {
+ FR_FAULT_LOG("Backtrace:");
+ backtrace_full(backtrace_state, 0, _backtrace_print, _backtrace_error, &frame);
+ }
+}
+#elif defined(HAVE_EXECINFO)
+void fr_backtrace(void)
+{
+ /*
+ * Produce a simple backtrace - They're very basic but at least give us an
+ * idea of the area of the code we hit the issue in.
+ *
+ * See below in fr_fault_setup() and
+ * https://sourceware.org/bugzilla/show_bug.cgi?id=16159
+ * for why we only print backtraces in debug builds if we're using GLIBC.
+ */
+#if (!defined(NDEBUG) || !defined(__GNUC__))
+ if (fr_fault_log_fd >= 0) {
+ size_t frame_count;
+ void *stack[MAX_BT_FRAMES];
+
+ frame_count = backtrace(stack, MAX_BT_FRAMES);
+
+ FR_FAULT_LOG("Backtrace of last %zu frames:", frame_count);
+
+ backtrace_symbols_fd(stack, frame_count, fr_fault_log_fd);
+ }
+#endif
+ return;
+}
+#else
+void fr_backtrace(void)
+{
+ return;
+}
+#endif
+
+#if defined(HAVE_BACKTRACE) || defined(HAVE_EXECINFO)
+/** Print backtrace entry for a given object
+ *
+ * @param fring to search in.
+ * @param obj pointer to original object
+ */
+void fr_backtrace_print(fr_fring_t *fring, void *obj)
+{
+ fr_bt_info_t *p;
+ bool found = false;
+
+ while ((p = fr_fring_next(fring))) {
+ if ((p->obj == obj) || !obj) {
+ found = true;
+
+ fprintf(stderr, "Stacktrace for: %p\n", p->obj);
+#ifdef HAVE_BACKTRACE
+ {
+ int i;
+
+ for (i = 0; i < p->count; i++) {
+ backtrace_info_print(p->frames[i], fr_fault_log_fd, true);
+ }
+ }
+#else
+ backtrace_symbols_fd(p->frames, p->count, fr_fault_log_fd);
+#endif
+ }
+ }
+
+ if (!found) {
+ fprintf(stderr, "No backtrace available for %p", obj);
+ }
+}
+
+/** Generate a backtrace for an object
+ *
+ * If this is the first entry being inserted
+ */
+static int _backtrace_do(fr_bt_marker_t *marker)
+{
+ fr_bt_info_t *bt;
+
+ if (!fr_cond_assert(marker->obj) || !fr_cond_assert(marker->fring)) return -1;
+
+ bt = talloc_zero(NULL, fr_bt_info_t);
+ if (!bt) return -1;
+
+ bt->obj = marker->obj;
+#ifdef HAVE_BACKTRACE
+
+#else
+ bt->count = backtrace(bt->frames, MAX_BT_FRAMES);
+#endif
+ fr_fring_overwrite(marker->fring, bt);
+
+ return 0;
+}
+
+/** Inserts a backtrace marker into the provided context
+ *
+ * Allows for maximum laziness and will initialise a circular buffer if one has not already been created.
+ *
+ * Code augmentation should look something like:
+@verbatim
+ // Create a static fring pointer, the first call to backtrace_attach will initialise it
+ static fr_fring_t *my_obj_bt;
+
+ my_obj_t *alloc_my_obj(TALLOC_CTX *ctx) {
+ my_obj_t *this;
+
+ this = talloc(ctx, my_obj_t);
+
+ // Attach backtrace marker to object
+ backtrace_attach(&my_obj_bt, this);
+
+ return this;
+ }
+@endverbatim
+ *
+ * Then, later when a double free occurs:
+@verbatim
+ (gdb) call backtrace_print(&my_obj_bt, <pointer to double freed memory>)
+@endverbatim
+ *
+ * which should print a limited backtrace to stderr. Note, this backtrace will not include any argument
+ * values, but should at least show the code path taken.
+ *
+ * @param fring this should be a pointer to a static *fr_fring_buffer.
+ * @param obj we want to generate a backtrace for.
+ */
+fr_bt_marker_t *fr_backtrace_attach(fr_fring_t **fring, TALLOC_CTX *obj)
+{
+ fr_bt_marker_t *marker;
+
+ if (*fring == NULL) {
+ pthread_mutex_lock(&fr_backtrace_lock);
+ if (*fring == NULL) *fring = fr_fring_alloc(NULL, MAX_BT_CBUFF, true);
+ pthread_mutex_unlock(&fr_backtrace_lock);
+ }
+
+ marker = talloc(obj, fr_bt_marker_t);
+ if (!marker) {
+ return NULL;
+ }
+
+ marker->obj = (void *) obj;
+ marker->fring = *fring;
+
+ fprintf(stderr, "Backtrace attached to %s %p\n", talloc_get_name(obj), obj);
+ /*
+ * Generate the backtrace for memory allocation
+ */
+ _backtrace_do(marker);
+ talloc_set_destructor(marker, _backtrace_do);
+
+ return marker;
+}
+#else
+fr_bt_marker_t *fr_backtrace_attach(UNUSED fr_fring_t **fring, UNUSED TALLOC_CTX *obj)
+{
+ fprintf(stderr, "Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo, or libbacktrace\n");
+ abort();
+}
+#endif
+
+void fr_backtrace_init(
+#ifndef HAVE_BACKTRACE
+ UNUSED
+#endif
+ char const *program)
+{
+#ifdef HAVE_BACKTRACE
+ /*
+ * Initialise the state for libbacktrace. As per the docs
+ * these resources can never be freed, and should be ignore
+ * in any leak tracking code.
+ */
+ backtrace_state = backtrace_create_state(program, 1, _backtrace_error, NULL);
+#elif defined(HAVE_EXECINFO) && defined(__GNUC__) && !defined(NDEBUG)
+ /*
+ * We need to pre-load lgcc_s, else we can get into a deadlock
+ * in fr_fault, as backtrace() attempts to dlopen it.
+ *
+ * Apparently there's a performance impact of loading lgcc_s,
+ * so only do it if this is a debug build.
+ *
+ * See: https://sourceware.org/bugzilla/show_bug.cgi?id=16159
+ */
+ {
+ void *stack[10];
+
+ backtrace(stack, 10);
+ }
+#endif
+}
--- /dev/null
+#pragma once
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/** Functions to help with cleanup
+ *
+ * Allows for printing backtraces of memory allocations or after crashes
+ *
+ * @file lib/util/backtrace.h
+ *
+ * @copyright 2025 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+RCSIDH(backtrace_h, "$Id$")
+
+#include <freeradius-devel/util/fring.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+typedef struct fr_bt_marker fr_bt_marker_t;
+
+void fr_backtrace_init(char const *program);
+
+void fr_backtrace_print(fr_fring_t *fring, void *obj);
+
+fr_bt_marker_t *fr_backtrace_attach(fr_fring_t **fring, TALLOC_CTX *obj);
+
+void fr_backtrace(void);
+
+#ifdef __cplusplus
+}
+#endif
* @copyright 2013 The FreeRADIUS server project
* @copyright 2013 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
*/
+#include <freeradius-devel/util/backtrace.h>
#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/hash.h>
#include <freeradius-devel/util/strerror.h>
# include <malloc.h>
#endif
-/*
- * runtime backtrace functions are not POSIX but are included in
- * glibc, OSX >= 10.5 and various BSDs
- */
-#ifdef HAVE_EXECINFO
-# include <execinfo.h>
-#endif
-
#ifdef HAVE_SYS_PRCTL_H
# include <sys/prctl.h>
#endif
#include <sys/sysctl.h>
#endif
-#ifdef HAVE_EXECINFO
-# ifndef MAX_BT_FRAMES
-# define MAX_BT_FRAMES 128
-# endif
-# ifndef MAX_BT_CBUFF
-# define MAX_BT_CBUFF 1048576 //!< Should be a power of 2
-# endif
-
-static pthread_mutex_t fr_debug_init = PTHREAD_MUTEX_INITIALIZER;
-
-typedef struct {
- void *obj; //!< Memory address of the block of allocated memory.
- void *frames[MAX_BT_FRAMES]; //!< Backtrace frame data
- int count; //!< Number of frames stored
-} fr_bt_info_t;
-
-struct fr_bt_marker {
- void *obj; //!< Pointer to the parent object, this is our needle
- //!< when we iterate over the contents of the circular buffer.
- fr_fring_t *fring; //!< Where we temporarily store the backtraces
-};
-#endif
-
static char panic_action[512]; //!< The command to execute when panicking.
static fr_fault_cb_t panic_cb = NULL; //!< Callback to execute whilst panicking, before the
//!< panic_action.
static bool dump_core; //!< Whether we should drop a core on fatal signals.
-static int fr_fault_log_fd = STDERR_FILENO; //!< Where to write debug output.
+int fr_fault_log_fd = STDERR_FILENO; //!< Where to write debug output.
fr_debug_state_t fr_debug_state = DEBUGGER_STATE_UNKNOWN; //!< Whether we're attached to by a debugger.
}
}
-#ifdef HAVE_EXECINFO
-/** Print backtrace entry for a given object
- *
- * @param fring to search in.
- * @param obj pointer to original object
- */
-void backtrace_print(fr_fring_t *fring, void *obj)
-{
- fr_bt_info_t *p;
- bool found = false;
-
- while ((p = fr_fring_next(fring))) {
- if ((p->obj == obj) || !obj) {
- found = true;
-
- fprintf(stderr, "Stacktrace for: %p\n", p->obj);
- backtrace_symbols_fd(p->frames, p->count, STDERR_FILENO);
- }
- }
-
- if (!found) {
- fprintf(stderr, "No backtrace available for %p", obj);
- }
-}
-
-/** Generate a backtrace for an object
- *
- * If this is the first entry being inserted
- */
-int fr_backtrace_do(fr_bt_marker_t *marker)
-{
- fr_bt_info_t *bt;
-
- if (!fr_cond_assert(marker->obj) || !fr_cond_assert(marker->fring)) return -1;
-
- bt = talloc_zero(NULL, fr_bt_info_t);
- if (!bt) return -1;
-
- bt->obj = marker->obj;
- bt->count = backtrace(bt->frames, MAX_BT_FRAMES);
-
- fr_fring_overwrite(marker->fring, bt);
-
- return 0;
-}
-
-/** Inserts a backtrace marker into the provided context
- *
- * Allows for maximum laziness and will initialise a circular buffer if one has not already been created.
- *
- * Code augmentation should look something like:
-@verbatim
- // Create a static fringer pointer, the first call to backtrace_attach will initialise it
- static fr_fring_t *my_obj_bt;
-
- my_obj_t *alloc_my_obj(TALLOC_CTX *ctx) {
- my_obj_t *this;
-
- this = talloc(ctx, my_obj_t);
-
- // Attach backtrace marker to object
- backtrace_attach(&my_obj_bt, this);
-
- return this;
- }
-@endverbatim
- *
- * Then, later when a double free occurs:
-@verbatim
- (gdb) call backtrace_print(&my_obj_bt, <pointer to double freed memory>)
-@endverbatim
- *
- * which should print a limited backtrace to stderr. Note, this backtrace will not include any argument
- * values, but should at least show the code path taken.
- *
- * @param fring this should be a pointer to a static *fr_fring_buffer.
- * @param obj we want to generate a backtrace for.
- */
-fr_bt_marker_t *fr_backtrace_attach(fr_fring_t **fring, TALLOC_CTX *obj)
-{
- fr_bt_marker_t *marker;
-
- if (*fring == NULL) {
- pthread_mutex_lock(&fr_debug_init);
- /* Check again now we hold the mutex - eww*/
- if (*fring == NULL) *fring = fr_fring_alloc(NULL, MAX_BT_CBUFF, true);
- pthread_mutex_unlock(&fr_debug_init);
- }
-
- marker = talloc(obj, fr_bt_marker_t);
- if (!marker) {
- return NULL;
- }
-
- marker->obj = (void *) obj;
- marker->fring = *fring;
-
- fprintf(stderr, "Backtrace attached to %s %p\n", talloc_get_name(obj), obj);
- /*
- * Generate the backtrace for memory allocation
- */
- fr_backtrace_do(marker);
- talloc_set_destructor(marker, fr_backtrace_do);
-
- return marker;
-}
-#else
-void backtrace_print(UNUSED fr_fring_t *fring, UNUSED void *obj)
-{
- fprintf(stderr, "Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo\n");
-}
-fr_bt_marker_t *fr_backtrace_attach(UNUSED fr_fring_t **fring, UNUSED TALLOC_CTX *obj)
-{
- fprintf(stderr, "Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo\n");
- abort();
-}
-#endif /* ifdef HAVE_EXECINFO */
-
static int _panic_on_free(UNUSED char *foo)
{
fr_fault(SIGABRT);
return 0;
}
-/** Split out so it can be sprinkled throughout the server and called via a debugger
- *
- */
-void fr_fault_backtrace(void)
-{
-
- /*
- * Produce a simple backtrace - They're very basic but at least give us an
- * idea of the area of the code we hit the issue in.
- *
- * See below in fr_fault_setup() and
- * https://sourceware.org/bugzilla/show_bug.cgi?id=16159
- * for why we only print backtraces in debug builds if we're using GLIBC.
- */
-#if defined(HAVE_EXECINFO) && (!defined(NDEBUG) || !defined(__GNUC__))
- if (fr_fault_log_fd >= 0) {
- size_t frame_count;
- void *stack[MAX_BT_FRAMES];
-
- frame_count = backtrace(stack, MAX_BT_FRAMES);
-
- FR_FAULT_LOG("Backtrace of last %zu frames:", frame_count);
-
- backtrace_symbols_fd(stack, frame_count, fr_fault_log_fd);
- }
-#endif
- return;
-}
-
/** Prints a simple backtrace (if execinfo is available) and calls panic_action if set.
*
* @param sig caught
*/
if (panic_cb && (panic_cb(sig) < 0)) goto finish;
- fr_fault_backtrace();
+ fr_backtrace();
/* No panic action set... */
if (panic_action[0] == '\0') {
{
FR_FAULT_LOG("talloc abort: %s\n", reason);
-#if defined(HAVE_EXECINFO) && (!defined(NDEBUG) || !defined(__GNUC__))
- if (fr_fault_log_fd >= 0) {
- size_t frame_count;
- void *stack[MAX_BT_FRAMES];
-
- frame_count = backtrace(stack, MAX_BT_FRAMES);
- FR_FAULT_LOG("Backtrace of last %zu frames:", frame_count);
- backtrace_symbols_fd(stack, frame_count, fr_fault_log_fd);
- }
-#endif
+ fr_backtrace();
abort();
}
return 0;
}
-
static int _disable_null_tracking(UNUSED bool *p)
{
talloc_disable_null_tracking();
mallopt(M_CHECK_ACTION, 3);
# endif
#endif
-
-#if defined(HAVE_EXECINFO) && defined(__GNUC__) && !defined(NDEBUG)
- /*
- * We need to pre-load lgcc_s, else we can get into a deadlock
- * in fr_fault, as backtrace() attempts to dlopen it.
- *
- * Apparently there's a performance impact of loading lgcc_s,
- * so only do it if this is a debug build.
- *
- * See: https://sourceware.org/bugzilla/show_bug.cgi?id=16159
- */
- {
- void *stack[10];
-
- backtrace(stack, 10);
- }
-#endif
+ fr_backtrace_init(program);
}
setup = true;
DEBUGGER_STATE_ATTACHED = 1 //!< We can't attach, it's likely a debugger is already tracing.
} fr_debug_state_t;
+extern int fr_fault_log_fd;
extern fr_debug_state_t fr_debug_state;
#define FR_FAULT_LOG(_fmt, ...) fr_fault_log(_fmt "\n", ## __VA_ARGS__)
* - < 0 on failure.
*/
typedef int (*fr_fault_cb_t)(int signum);
-typedef struct fr_bt_marker fr_bt_marker_t;
int fr_get_lsan_state(void);
void fr_debug_break(bool always);
-void backtrace_print(fr_fring_t *fring, void *obj);
-
-int fr_backtrace_do(fr_bt_marker_t *marker);
-
-fr_bt_marker_t *fr_backtrace_attach(fr_fring_t **fring, TALLOC_CTX *obj);
-
void fr_panic_on_free(TALLOC_CTX *ctx);
int fr_set_dumpable_init(void);
int fr_log_talloc_report(TALLOC_CTX const *ctx);
-void fr_fault_backtrace(void);
-
void fr_fault(int sig);
void fr_talloc_fault_setup(void);
SOURCES := \
atexit.c \
+ backtrace.c \
base16.c \
base32.c \
base64.c \
HEADERS := $(subst src/lib/,,$(wildcard src/lib/util/*.h))
-SRC_CFLAGS := -DNO_ASSERT -I$(top_builddir)/src
+SRC_CFLAGS := -DNO_ASSERT -DTOP_SRCDIR=\"${top_srcdir}\" -I$(top_builddir)/src
# System libraries discovered by our top level configure script, links things
# like pthread and the regexp libraries.
TGT_LDLIBS := $(LIBS) $(PCAP_LIBS)
TGT_LDFLAGS := $(LDFLAGS) $(PCAP_LDFLAGS)
+# libbacktrace is checked out as a submodule and linked statically into libfreeradius-util
+# as it's the only library that uses it. Other libraries should not use it directly but
+# instead add the functionality they need to libfreeradius-util.
+ifeq "$(WITH_BACKTRACE)" "yes"
+HEADERS += $(top_srcdir)/src/lib/backtrace/backtrace.h
+TGT_PREREQS += libbacktrace.la
+TGT_LDLIBS += '-lbacktrace'
+TGT_LDFLAGS += -L$(top_builddir)/build/lib/local/.libs
+
+# Actually call the 'sub'-make to build libbacktrace.
+src/lib/backtrace/.libs/libbacktrace.a:
+ $(MAKE) -C $(top_srcdir)/src/lib/backtrace
+
+# We need to do this so jlibtool can find the library.
+build/lib/.libs/libbacktrace.a: src/lib/backtrace/.libs/libbacktrace.a
+ cp $< $@
+
+# Boilermake needs this target to exist
+build/lib/libbacktrace.la: src/lib/backtrace/libbacktrace.la build/lib/.libs/libbacktrace.a
+ cp $< $@
+
+# We need to do this so jlibtool can find the library.
+build/lib/local/.libs/libbacktrace.a: src/lib/backtrace/.libs/libbacktrace.a
+ cp $< $@
+
+# Boilermake needs this target to exist
+build/lib/local/libbacktrace.la: src/lib/backtrace/libbacktrace.la build/lib/local/.libs/libbacktrace.a
+ cp $< $@
+endif
+
ifeq "$(TARGET_IS_WASM)" "yes"
SRC_CFLAGS += -sMAIN_MODULE=1 -sUSE_PTHREADS=1
TGT_LDFLAGS += --no-entry -sALLOW_MEMORY_GROWTH=1 -sFORCE_FILESYSTEM=1 -sEXPORT_ALL=1 -sLINKABLE=1 -sMODULARIZE=1 -sEXPORT_ES6=1 -sEXPORT_NAME=libfreeradiusUtil -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,setValue,getValue --preload-file=$(top_builddir)/share/dictionary@/share/dictionary