From: Willy Tarreau Date: Fri, 25 Feb 2022 07:55:11 +0000 (+0100) Subject: DEBUG: add a new WARN_ON_ONCE() macro X-Git-Tag: v2.6-dev2~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4e0a8b12241c464760ecb8dc965b913e8a17282c;p=thirdparty%2Fhaproxy.git DEBUG: add a new WARN_ON_ONCE() macro This one will maintain a static counter per call place and will only emit the warning on the first call. It may be used to invite users to report an unexpected event without spamming them with messages. --- diff --git a/include/haproxy/bug.h b/include/haproxy/bug.h index 9a760c7332..046ea7607e 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 5d34389e5d..e0d3c07f67 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 },