From: Yongqiang Tian Date: Wed, 19 Aug 2026 23:27:13 +0000 (+1000) Subject: BUG/MINOR: debug: restore complain tail jumps with clang X-Git-Tag: v3.5-dev6~5 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=b960ab409aa01797ee75a857849c48600a423e37;p=haproxy.git BUG/MINOR: debug: restore complain tail jumps with clang Commit 9752f802b3869c96446f7e84e43d89360695c0f8 introduced eliminate_tail_calls to turn the final call of a function into a jump. Commit 431b6b1cd92a54f4a53b66a5ff27a64381ff9a23 then applied it to complain() and complain_with_dbg() so that these wrappers would not appear in diagnostic backtraces. The GCC expansion enables sibling-call optimization. The Clang expansion currently uses disable_tail_calls, which has the opposite meaning: Clang documents it as preventing tail-call optimization inside the attributed function. This is visible through HAProxy's own "debug dev check" command. GCC 11 omits complain_with_dbg() from the resulting backtrace, while Clang 14 and Clang 18 retain one complain_with_dbg() frame. The builds otherwise succeed, so build success alone does not expose the difference. Making eliminate_tail_calls empty for Clang removes the inverse attribute, but is not sufficient by itself: both tested Clang versions then inline ha_backtrace_to_stderr() into the wrapper and still retain its frame. Also mark the callee noinline so that Clang emits the intended final jump. The resulting source was tested on x86-64 at master revision 3081fdb5846b1b7ab8f93d1f28b31af13502f5db. Normal GCC 11, Clang 14, and Clang 18 builds passed, and "debug dev check" omitted the wrapper frame in all three repaired builds. Object-code inspection confirmed a final jump. CI-aligned no-features builds with GCC 13 and Clang 18 also passed, including 143 default, bug, and devel regression tests for each compiler. A broader Clang 14 run passed 167 regression tests. The enabled unit tests passed for the repaired GCC and Clang builds. Clang 13 never optimizes the call in any case. The user-visible effect is limited to developer backtrace quality. This is not a security or performance fix. This is submitted as an RFC because a maintainer may prefer a Clang-specific spelling or a reusable noinline abstraction. This is the approach I had in mind, and any suggestions would be appreciated. No backport is needed because both introducing commits are only in the 3.5 development branch. --- diff --git a/include/haproxy/compiler.h b/include/haproxy/compiler.h index 72792476c..697eb2787 100644 --- a/include/haproxy/compiler.h +++ b/include/haproxy/compiler.h @@ -124,14 +124,12 @@ #endif /* tail-call elimination (turn the final call of a function into a jump). This - * applies to the calling function. Clang doesn't support -f type optimizations - * but has a dedicated attribute that we can use. Modern versions have a - * "musttail" attribute at the statement level (per-return), but that's harder - * to turn into something portable. - */ -#if defined(__clang__) && __has_attribute(disable_tail_calls) -# define eliminate_tail_calls __attribute__((disable_tail_calls)) -#elif !defined(__clang__) && __has_attribute(optimize) + * applies to the calling function. Clang has no function attribute to enable + * this optimization. Modern versions have a "musttail" attribute at the + * statement level (per-return), but that's harder to turn into something + * portable. + */ +#if !defined(__clang__) && __has_attribute(optimize) # define eliminate_tail_calls __attribute__((optimize("optimize-sibling-calls"))) #else # define eliminate_tail_calls diff --git a/src/debug.c b/src/debug.c index 20b15a761..b3cb4e03a 100644 --- a/src/debug.c +++ b/src/debug.c @@ -293,7 +293,7 @@ void ha_dump_backtrace(struct buffer *buf, const char *prefix, int dump) /* dump a backtrace of current thread's stack to stderr. Displays the hint about * the core if hint & 1. */ -void ha_backtrace_to_stderr(int hint) +__attribute__((noinline)) void ha_backtrace_to_stderr(int hint) { char area[8192]; struct buffer b = b_make(area, sizeof(area), 0, 0);