MEDIUM: debug: now always print a backtrace on CRASH_NOW() and friends
The purpose is to enable the dumping of a backtrace on BUG_ON(). While
it's very useful to know that a condition was met, very often some
caller context is missing to figure how the condition could happen.
From now on, on systems featuring backtrace, a backtrace of the calling
thread will also be dumped to stderr in addition to the unexpected
condition. This will help users of DEBUG_STRICT as they'll most often
find this backtrace in their logs even if they can't find their core
file.
A new "debug dev bug" expert-mode CLI command was added to test the
feature.
diff --git a/include/haproxy/bug.h b/include/haproxy/bug.h
index f5ed7f4..a2cf6ba 100644
--- a/include/haproxy/bug.h
+++ b/include/haproxy/bug.h
@@ -39,12 +39,12 @@
#ifdef DEBUG_USE_ABORT
/* abort() is better recognized by code analysis tools */
-#define ABORT_NOW() abort()
+#define ABORT_NOW() do { extern void ha_backtrace_to_stderr(); ha_backtrace_to_stderr(); abort(); } while (0)
#else
/* More efficient than abort() because it does not mangle the
* stack and stops at the exact location we need.
*/
-#define ABORT_NOW() (*(volatile int*)1=0)
+#define ABORT_NOW() do { extern void ha_backtrace_to_stderr(); ha_backtrace_to_stderr(); (*(volatile int*)1=0); } while (0)
#endif
/* BUG_ON: complains if <cond> is true when DEBUG_STRICT or DEBUG_STRICT_NOCRASH
@@ -55,7 +55,7 @@
#if defined(DEBUG_STRICT)
#define CRASH_NOW() ABORT_NOW()
#else
-#define CRASH_NOW()
+#define CRASH_NOW() do { ha_backtrace_to_stderr(); } while (0)
#endif
#define BUG_ON(cond) _BUG_ON(cond, __FILE__, __LINE__)
diff --git a/src/debug.c b/src/debug.c
index 2a5e164..7d73fdc 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -322,6 +322,19 @@
return 1;
}
+/* parse a "debug dev bug" command. It always returns 1, though it should never return.
+ * Note: we make sure not to make the function static so that it appears in the trace.
+ */
+int debug_parse_cli_bug(char **args, char *payload, struct appctx *appctx, void *private)
+{
+ if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+ return 1;
+
+ _HA_ATOMIC_ADD(&debug_commands_issued, 1);
+ BUG_ON(one > zero);
+ return 1;
+}
+
/* parse a "debug dev close" command. It always returns 1. */
static int debug_parse_cli_close(char **args, char *payload, struct appctx *appctx, void *private)
{
@@ -1144,6 +1157,7 @@
/* register cli keywords */
static struct cli_kw_list cli_kws = {{ },{
+ {{ "debug", "dev", "bug", NULL }, "debug dev bug : call BUG_ON()", debug_parse_cli_bug, NULL, NULL, NULL, ACCESS_EXPERT },
{{ "debug", "dev", "close", NULL }, "debug dev close <fd> : close this file descriptor", debug_parse_cli_close, NULL, NULL, NULL, ACCESS_EXPERT },
{{ "debug", "dev", "delay", NULL }, "debug dev delay [ms] : sleep this long", debug_parse_cli_delay, NULL, NULL, NULL, ACCESS_EXPERT },
#if defined(DEBUG_DEV)