From 00daed48e7658b6d4337f693bbf0ad4808cc3a04 Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 3 Aug 2026 21:48:51 -0500 Subject: [PATCH 1/4] build: emit BACKTRACE_FLAGS before debug-level flags BACKTRACE_FLAGS carries -gdwarf-4, which pins the DWARF version for libbacktrace but, being a member of the -g option family, also implies debug level 2. Placed last in AM_CFLAGS/AM_CXXFLAGS it silently overrode every debug level requested earlier on the command line: the '-g1 -fno-omit-frame-pointer' floor that configure.ac declares for crash-info stacktraces ('We always enable at least -g1') was escalated to full -g2 in every build, and --enable-debug's -g3 was clamped down to -g2. Measured on src/validation.cpp with gcc and clang: '-g1 ... -gdwarf-4 -O2' produces a 9.7 MB object, '-gdwarf-4 ... -g1 -O2' produces 2.5 MB. Move BACKTRACE_FLAGS to the front so the DWARF version is pinned first and the debug level is decided by what follows: DEBUG_*FLAGS' -g1 floor, --enable-debug's -g3, or the user's own CFLAGS/CXXFLAGS, which automake always places last. Builds that pass an explicit -g (e.g. Guix releases with '-O2 -g') are unchanged. The main effect is that builds without a -g in their flags now get the documented -g1 instead of an accidental -g2, cutting roughly 90% of DWARF from ~1000 objects per CI target. --- configure.ac | 4 ++-- src/Makefile.am | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 4dd273e57548..3215bf48d5d2 100644 --- a/configure.ac +++ b/configure.ac @@ -2139,10 +2139,10 @@ echo " target os = $host_os" echo " build os = $build_os" echo echo " CC = $CC" -echo " CFLAGS = $DEBUG_CFLAGS $PTHREAD_CFLAGS $BACKTRACE_FLAGS $CFLAGS" +echo " CFLAGS = $BACKTRACE_FLAGS $DEBUG_CFLAGS $PTHREAD_CFLAGS $CFLAGS" echo " CPPFLAGS = $DEBUG_CPPFLAGS $HARDENED_CPPFLAGS $CORE_CPPFLAGS $CPPFLAGS" echo " CXX = $CXX" -echo " CXXFLAGS = $DEBUG_CXXFLAGS $HARDENED_CXXFLAGS $WARN_CXXFLAGS $NOWARN_CXXFLAGS $ERROR_CXXFLAGS $CORE_CXXFLAGS $BACKTRACE_FLAGS $CXXFLAGS" +echo " CXXFLAGS = $BACKTRACE_FLAGS $DEBUG_CXXFLAGS $HARDENED_CXXFLAGS $WARN_CXXFLAGS $NOWARN_CXXFLAGS $ERROR_CXXFLAGS $CORE_CXXFLAGS $CXXFLAGS" echo " LDFLAGS = $PTHREAD_LIBS $HARDENED_LDFLAGS $CORE_LDFLAGS $BACKTRACE_LDFLAGS $LDFLAGS" echo " AR = $AR" echo " ARFLAGS = $ARFLAGS" diff --git a/src/Makefile.am b/src/Makefile.am index 501a695cf852..27f7e73d85a7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,8 +10,11 @@ print-%: FORCE DIST_SUBDIRS = secp256k1 AM_LDFLAGS = $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(SANITIZER_LDFLAGS) $(CORE_LDFLAGS) $(BACKTRACE_LDFLAGS) -AM_CFLAGS = $(DEBUG_CFLAGS) $(BACKTRACE_FLAGS) -AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(WARN_CXXFLAGS) $(NOWARN_CXXFLAGS) $(ERROR_CXXFLAGS) $(SANITIZER_CXXFLAGS) $(CORE_CXXFLAGS) $(BACKTRACE_FLAGS) +# BACKTRACE_FLAGS must precede the debug-level flags: -gdwarf-4 pins the DWARF +# version but also implies -g2, so the intended level (DEBUG_*FLAGS' -g1 floor, +# --enable-debug's -g3, or the user's CXXFLAGS) must come later to take effect. +AM_CFLAGS = $(BACKTRACE_FLAGS) $(DEBUG_CFLAGS) +AM_CXXFLAGS = $(BACKTRACE_FLAGS) $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(WARN_CXXFLAGS) $(NOWARN_CXXFLAGS) $(ERROR_CXXFLAGS) $(SANITIZER_CXXFLAGS) $(CORE_CXXFLAGS) AM_OBJCXXFLAGS = $(AM_CXXFLAGS) AM_CPPFLAGS = $(DEBUG_CPPFLAGS) $(HARDENED_CPPFLAGS) $(CORE_CPPFLAGS) AM_LIBTOOLFLAGS = --preserve-dup-deps From 30b9f1435a411f493d52a4297cd03411b7e496f0 Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 3 Aug 2026 21:50:38 -0500 Subject: [PATCH 2/4] build: drop the -gdwarf-4 pin from stacktrace flags The pin was added when libbacktrace could not parse DWARF 5 and modern compilers had started emitting it by default. The libbacktrace pinned in depends (b9e40069, 2025-11-06) has had DWARF 5 support since 2021 and is what dashd statically links, so runtime crash symbolization always uses a DWARF 5 capable reader. All CI and release compilers (gcc 11+, clang 14+) default to DWARF 5. Dropping the pin lets the compiler use its default debug format and shrinks debug info a further ~10-25% on top of the -g1 level fix (measured at -g1 -O2: g++ 67.3 KB to 61.5 KB, clang++ 39.3 KB to 30.0 KB for a representative object). The valgrind CI jobs keep their own explicit -gdwarf-4 in their environment files, which is a separate valgrind-compatibility concern. --- configure.ac | 14 ++++---------- src/Makefile.am | 7 ++++--- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/configure.ac b/configure.ac index 3215bf48d5d2..1fcbafbd99fd 100644 --- a/configure.ac +++ b/configure.ac @@ -1228,16 +1228,10 @@ if test "$enable_stacktraces" != "no"; then ]) fi BACKTRACE_LIBS="$BACKTRACE_LIBS -lbacktrace" - dnl More modern compilers may emit DWARF 5 binaries by default, use DWARF 4 out of precaution - if test "$TARGET_OS" != "windows"; then - AX_CHECK_COMPILE_FLAG([-gdwarf-4], [BACKTRACE_FLAGS="$BACKTRACE_FLAGS -gdwarf-4"], [ - if test "$enable_stacktraces" = "yes"; then - AC_MSG_ERROR([--enable-stacktraces was specified but cannot set -gdwarf-4]) - fi - AC_MSG_WARN([cannot set -gdwarf-4, stacktraces will be disabled]) - enable_stacktraces=no - ], [$CXXFLAG_WERROR]) - fi + dnl The libbacktrace pinned in depends understands DWARF 5, so the + dnl compiler's default debug format is used as-is. A -gdwarf-4 pin + dnl previously lived here and, being a -g-family flag, also forced + dnl debug level 2 onto every build. if test "$TARGET_OS" = "darwin"; then AX_CHECK_COMPILE_FLAG([-fno-standalone-debug], [BACKTRACE_FLAGS="$BACKTRACE_FLAGS -fno-standalone-debug"], [ if test "$enable_stacktraces" = "yes"; then diff --git a/src/Makefile.am b/src/Makefile.am index 27f7e73d85a7..30fdf4702361 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,9 +10,10 @@ print-%: FORCE DIST_SUBDIRS = secp256k1 AM_LDFLAGS = $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(SANITIZER_LDFLAGS) $(CORE_LDFLAGS) $(BACKTRACE_LDFLAGS) -# BACKTRACE_FLAGS must precede the debug-level flags: -gdwarf-4 pins the DWARF -# version but also implies -g2, so the intended level (DEBUG_*FLAGS' -g1 floor, -# --enable-debug's -g3, or the user's CXXFLAGS) must come later to take effect. +# BACKTRACE_FLAGS must precede the debug-level flags: -g-family members placed +# there (e.g. a -gdwarf-N version pin) imply -g2, so the intended level +# (DEBUG_*FLAGS' -g1 floor, --enable-debug's -g3, or the user's CXXFLAGS) must +# come later to take effect. AM_CFLAGS = $(BACKTRACE_FLAGS) $(DEBUG_CFLAGS) AM_CXXFLAGS = $(BACKTRACE_FLAGS) $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(WARN_CXXFLAGS) $(NOWARN_CXXFLAGS) $(ERROR_CXXFLAGS) $(SANITIZER_CXXFLAGS) $(CORE_CXXFLAGS) AM_OBJCXXFLAGS = $(AM_CXXFLAGS) From 11b181533e65758dae639b65a428f646ae896ba6 Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 3 Aug 2026 22:10:26 -0500 Subject: [PATCH 3/4] ci: drop the tsan job's explicit CXXFLAGS The depends config.site prepends the host release flags (-O2) to user CXXFLAGS, so this job has always built at -O2 and the trailing '-g' only escalated debug info to level 2 -- at ~540 MB compressed the largest ccache entry of any target. ThreadSanitizer reports only consume function names and line tables. With the -g1 floor now effective, removing the override lets tsan pick up the same '-O2 -g1' as every other job instead of remaining a special case. A fork CI run of the full tsan test suite at debug level 1 with -O2 passed (run 30867089833). --- ci/test/00_setup_env_native_tsan.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh index f84f594735f6..9a7f15cf8b44 100755 --- a/ci/test/00_setup_env_native_tsan.sh +++ b/ci/test/00_setup_env_native_tsan.sh @@ -30,6 +30,6 @@ export DEP_OPTS="CC=clang-19 CXX='clang++-19 -stdlib=libc++'" export TEST_RUNNER_EXTRA="--extended --exclude feature_pruning,feature_dbcrash,wallet_multiwallet.py" # Temporarily suppress ASan heap-use-after-free (see issue #14163) export TEST_RUNNER_EXTRA="${TEST_RUNNER_EXTRA} --timeout-factor=4" # Increase timeout because sanitizers slow down export GOAL="install" -export BITCOIN_CONFIG="--enable-zmq --with-sanitizers=thread CC=clang-19 CXX=clang++-19 CXXFLAGS='-g'" +export BITCOIN_CONFIG="--enable-zmq --with-sanitizers=thread CC=clang-19 CXX=clang++-19" export CPPFLAGS="-DARENA_DEBUG -DDEBUG_LOCKORDER -DDEBUG_LOCKCONTENTION" export PYZMQ=true From 74fcc117ce587325cf914d8c69ae3848b0d72581 Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 3 Aug 2026 22:17:06 -0500 Subject: [PATCH 4/4] guix: keep full debug info in darwin release dSYMs The darwin case unsets HOST_CFLAGS, so the mac release build's debug level was never requested explicitly: it came from the main build's since-removed -gdwarf-4 backtrace flag escalating configure's -g1 floor to -g2. Without it the shipped dSYM debug artifacts (make osx_debug) would silently drop variable and type DWARF while linux and mingw releases keep theirs via their explicit '-O2 -g'. Request -g explicitly for darwin instead. Optimization (-O2) and target flags still come from depends' config.site, whose flags are prepended to user CFLAGS/CXXFLAGS, so the user-supplied -g lands last and selects level 2 exactly as before. --- contrib/guix/libexec/build.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh index 8c517368d17a..0c9fda7ad11d 100755 --- a/contrib/guix/libexec/build.sh +++ b/contrib/guix/libexec/build.sh @@ -222,7 +222,13 @@ HOST_CFLAGS+=$(find /gnu/store -maxdepth 1 -mindepth 1 -type d -exec echo -n " - case "$HOST" in *linux*) HOST_CFLAGS+=" -ffile-prefix-map=${PWD}=." ;; *mingw*) HOST_CFLAGS+=" -fno-ident" ;; - *darwin*) unset HOST_CFLAGS ;; + *darwin*) + # Optimization and target flags come from depends' config.site; only + # the debug level is requested here. The shipped dSYM debug artifacts + # previously carried full -g2 DWARF via the since-removed -gdwarf-4 + # backtrace flag escalating configure's -g1 floor, so keep requesting + # it explicitly. + HOST_CFLAGS="-g" ;; esac # CXXFLAGS