From: Daniel Stenberg Date: Tue, 18 Nov 2025 13:29:09 +0000 (+0100) Subject: memdebug: produce stack trace dump with libbacktrace X-Git-Tag: rc-8_18_0-1~180 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c77bed81a2e1325ffdebc223c27e3d1355147529;p=thirdparty%2Fcurl.git memdebug: produce stack trace dump with libbacktrace Enable with "configure --enable-backtrace", inserts a backtrace in the memdump log when a torture test limit is reached. Closes #19657 --- diff --git a/configure.ac b/configure.ac index d72b33d528..1481a096b2 100644 --- a/configure.ac +++ b/configure.ac @@ -1108,6 +1108,22 @@ AS_HELP_STRING([--enable-libgcc],[use libgcc when linking]), AC_MSG_RESULT(no) ) +AC_MSG_CHECKING([whether to use libbacktrace]) +AC_ARG_WITH(backtrace, +AS_HELP_STRING([--enable-backtrace],[use libbacktrace when linking]), +[ case "$enableval" in + yes) + LIBS="-lbacktrace $LIBS" + AC_DEFINE(USE_BACKTRACE, 1, [if libbacktrace is in use]) + AC_MSG_RESULT(yes) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + CURL_CHECK_LIB_XNET dnl gethostbyname without lib or in the nsl lib? diff --git a/lib/memdebug.c b/lib/memdebug.c index 758c7b6aa7..cbda4a2be1 100644 --- a/lib/memdebug.c +++ b/lib/memdebug.c @@ -31,6 +31,10 @@ #include "urldata.h" #include "curlx/fopen.h" /* for CURLX_FOPEN_LOW(), CURLX_FREOPEN_LOW() */ +#ifdef USE_BACKTRACE +#include "backtrace.h" +#endif + /* The last 2 #include files should be in this order */ #include "curl_memory.h" #include "memdebug.h" @@ -58,6 +62,9 @@ FILE *curl_dbg_logfile = NULL; static bool registered_cleanup = FALSE; /* atexit registered cleanup */ static bool memlimit = FALSE; /* enable memory limit */ static long memsize = 0; /* set number of mallocs allowed */ +#ifdef USE_BACKTRACE +struct backtrace_state *btstate; +#endif /* LeakSantizier (LSAN) calls _exit() instead of exit() when a leak is detected on exit so the logfile must be closed explicitly or data could be lost. @@ -73,6 +80,33 @@ static void curl_dbg_cleanup(void) } curl_dbg_logfile = NULL; } +#ifdef USE_BACKTRACE +static void error_callback(void *data, const char *message, int error_number) +{ + (void)data; + if(error_number == -1) + curl_dbg_log("compile with -g\n\n"); + else + curl_dbg_log("Backtrace error %d: %s\n", error_number, message); +} + +static int full_callback(void *data, uintptr_t pc, const char *pathname, + int line_number, const char *function) +{ + (void)data; + (void)pc; + if(pathname || function || line_number) + curl_dbg_log("BT %s:%d -- %s\n", pathname, line_number, function); + return 0; +} + +static void dump_bt(void) +{ + backtrace_full(btstate, 0, full_callback, error_callback, NULL); +} +#else +#define dump_bt() /* nothing to do */ +#endif /* this sets the log filename */ void curl_dbg_memdebug(const char *logname) @@ -88,6 +122,9 @@ void curl_dbg_memdebug(const char *logname) setbuf(curl_dbg_logfile, (char *)NULL); #endif } +#ifdef USE_BACKTRACE + btstate = backtrace_create_state(NULL, 0, error_callback, NULL); +#endif if(!registered_cleanup) registered_cleanup = !atexit(curl_dbg_cleanup); } @@ -115,6 +152,7 @@ static bool countcheck(const char *func, int line, const char *source) /* log to stderr also */ curl_mfprintf(stderr, "LIMIT %s:%d %s reached memlimit\n", source, line, func); + dump_bt(); fflush(curl_dbg_logfile); /* because it might crash now */ /* !checksrc! disable ERRNOVAR 1 */ errno = ENOMEM; diff --git a/tests/memanalyze.pl b/tests/memanalyze.pl index e35b2e7aba..2b38e5e8a4 100755 --- a/tests/memanalyze.pl +++ b/tests/memanalyze.pl @@ -130,7 +130,10 @@ while(<$fileh>) { chomp $_; my $line = $_; $lnum++; - if($line =~ /^LIMIT ([^ ]*):(\d*) (.*)/) { + if($line =~ /^BT/) { + # back-trace, ignore + } + elsif($line =~ /^LIMIT ([^ ]*):(\d*) (.*)/) { # new memory limit test prefix my $i = $3; my ($source, $linenum) = ($1, $2);