From: Willy Tarreau Date: Fri, 22 Jan 2021 13:15:46 +0000 (+0100) Subject: MEDIUM: debug: now always print a backtrace on CRASH_NOW() and friends X-Git-Tag: v2.4-dev6~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5baf4fe31ad2a5b1e0402c0a6d580dda203a2dab;p=thirdparty%2Fhaproxy.git 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 f5ed7f419a..a2cf6ba0b8 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 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 2a5e164c6d..7d73fdc9e0 100644 --- a/src/debug.c +++ b/src/debug.c @@ -322,6 +322,19 @@ static int debug_parse_cli_exit(char **args, char *payload, struct appctx *appct 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_PER_THREAD_INIT(init_debug_per_thread); /* 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 : 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)