diff --git a/include/haproxy/bug.h b/include/haproxy/bug.h index 9a760c733..046ea7607 100644 --- a/include/haproxy/bug.h +++ b/include/haproxy/bug.h @@ -72,19 +72,46 @@ __bug_cond; /* let's return the condition */ \ }) +/* This one is equivalent except that it only emits the message once by + * maintaining a static counter. This may be used with warnings to detect + * certain unexpected conditions in field. Later on, in cores it will be + * possible to verify these counters. + */ +#define _BUG_ON_ONCE(cond, file, line, crash, pfx, sfx) \ + __BUG_ON_ONCE(cond, file, line, crash, pfx, sfx) + +#define __BUG_ON_ONCE(cond, file, line, crash, pfx, sfx) \ + ({ \ + static int __match_count_##line; \ + int __bug_cond = !!(cond); \ + if (unlikely(__bug_cond) && \ + !_HA_ATOMIC_FETCH_ADD(&__match_count_##line, 1)) { \ + const char msg[] = "\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n"; \ + DISGUISE(write(2, msg, __builtin_strlen(msg))); \ + if (crash) \ + ABORT_NOW(); \ + else \ + DUMP_TRACE(); \ + } \ + __bug_cond; /* let's return the condition */ \ + }) + /* BUG_ON: complains if is true when DEBUG_STRICT or DEBUG_STRICT_NOCRASH * are set, does nothing otherwise. With DEBUG_STRICT in addition it immediately * crashes using ABORT_NOW() above. */ #if defined(DEBUG_STRICT) -#define BUG_ON(cond) _BUG_ON(cond, __FILE__, __LINE__, 1, "FATAL: bug ", "") -#define WARN_ON(cond) _BUG_ON(cond, __FILE__, __LINE__, 0, "WARNING: ", " (please report to developers)") +#define BUG_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 1, "FATAL: bug ", "") +#define WARN_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 0, "WARNING: ", " (please report to developers)") +#define WARN_ON_ONCE(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: ", " (please report to developers)") #elif defined(DEBUG_STRICT_NOCRASH) -#define BUG_ON(cond) _BUG_ON(cond, __FILE__, __LINE__, 0, "FATAL: bug ", " (not crashing but process is untrusted now)") -#define WARN_ON(cond) _BUG_ON(cond, __FILE__, __LINE__, 0, "WARNING: ", " (please report to developers)") +#define BUG_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 0, "FATAL: bug ", " (not crashing but process is untrusted now)") +#define WARN_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 0, "WARNING: ", " (please report to developers)") +#define WARN_ON_ONCE(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: ", " (please report to developers)") #else #define BUG_ON(cond) #define WARN_ON(cond) +#define WARN_ON_ONCE(cond) #endif /* When not optimizing, clang won't remove that code, so only compile it in when optimizing */ diff --git a/src/debug.c b/src/debug.c index 5d34389e5..e0d3c07f6 100644 --- a/src/debug.c +++ b/src/debug.c @@ -376,6 +376,19 @@ int debug_parse_cli_warn(char **args, char *payload, struct appctx *appctx, void return 1; } +/* parse a "debug dev warn1" command. It always returns 1. + * Note: we make sure not to make the function static so that it appears in the trace. + */ +int debug_parse_cli_warn1(char **args, char *payload, struct appctx *appctx, void *private) +{ + if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) + return 1; + + _HA_ATOMIC_INC(&debug_commands_issued); + WARN_ON_ONCE(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) { @@ -1404,6 +1417,7 @@ static struct cli_kw_list cli_kws = {{ },{ {{ "debug", "dev", "sym", NULL }, "debug dev sym : resolve symbol address", debug_parse_cli_sym, NULL, NULL, NULL, ACCESS_EXPERT }, {{ "debug", "dev", "tkill", NULL }, "debug dev tkill [thr] [sig] : send signal to thread", debug_parse_cli_tkill, NULL, NULL, NULL, ACCESS_EXPERT }, {{ "debug", "dev", "warn", NULL }, "debug dev warn : call WARN_ON() and possibly crash", debug_parse_cli_warn, NULL, NULL, NULL, ACCESS_EXPERT }, + {{ "debug", "dev", "warn1", NULL }, "debug dev warn1 : call WARN_ON_ONCE() and possibly crash", debug_parse_cli_warn1, NULL, NULL, NULL, ACCESS_EXPERT }, {{ "debug", "dev", "write", NULL }, "debug dev write [size] : write that many bytes in return", debug_parse_cli_write, NULL, NULL, NULL, ACCESS_EXPERT }, #if defined(HA_HAVE_DUMP_LIBS) {{ "show", "libs", NULL, NULL }, "show libs : show loaded object files and libraries", debug_parse_cli_show_libs, NULL, NULL },