]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: debug: make ABORT_NOW() store the caller's line number when using abort
authorWilly Tarreau <w@1wt.eu>
Fri, 2 Feb 2024 16:09:09 +0000 (17:09 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 2 Feb 2024 16:12:06 +0000 (17:12 +0100)
Placing DO_NOT_FOLD() before abort() only works in -O2 but not in -Os which
continues to place only 5 calls to abort() in h3.o for call places. The
approach taken here is to replace abort() with a new function that wraps
it and stores the line number in the stack. This slightly increases the
code size (+0.1%) but when unwinding a crash, the line number remains
present now. This is a very low cost, especially if we consider that
DEBUG_USE_ABORT is almost only used by code coverage tools and occasional
debugging sessions.

include/haproxy/bug.h

index 24a38ff1728191d3400d725429a9465cf1ce7b50..bee4dab12555da4de6feaced9d95cdcf497a7cc9 100644 (file)
@@ -87,7 +87,24 @@ static inline __attribute((always_inline)) void ha_crash_now(void)
 
 #ifdef DEBUG_USE_ABORT
 /* abort() is better recognized by code analysis tools */
-#define ABORT_NOW() do { DUMP_TRACE(); abort(); } while (0)
+
+/* abort() is generally tagged noreturn, so there's no 100% safe way to prevent
+ * the compiler from doing a tail-merge here. Tests show that stopping folding
+ * just before calling abort() does work in practice at -O2, increasing the
+ * number of abort() calls in h3.o from 18 to 26, probably because there's no
+ * more savings to be made by replacing a call with a jump. However, as -Os it
+ * drops to 5 regardless of the build option. In order to help here, instead we
+ * wrap abort() into another function, with the line number stored into a local
+ * variable on the stack and we pretend to use it, so that unwinding the stack
+ * from abort() will reveal its value even if the call was folded.
+ */
+static __attribute__((noinline,noreturn,unused)) void abort_with_line(uint line)
+{
+       DISGUISE(&line);
+       abort();
+}
+
+#define ABORT_NOW() do { DUMP_TRACE(); abort()_with_line(__LINE__); } while (0)
 #else
 /* More efficient than abort() because it does not mangle the
  * stack and stops at the exact location we need.