From 77acaf5af528d45e88e803f55b448d5b17e4762d Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 9 Sep 2022 14:34:12 +0200 Subject: [PATCH] MINOR: flags: add a new file to host flag dumping macros The "flags" utility is useful but painful to maintain up to date. This commit aims at providing a low-maintenance solution to keep flags up to date, by proposing some macros that build a string from a set of flags in a way that requires the least possible verbosity. The idea will be to add an inline function dedicated to this just after the flags declaration, and enumerate the flags one is interested in, and that function will fill a string based on them. Placing this inside the type files allows both haproxy and external tools like "flags" to use it, but comes with a few constraints. First, the files will be slightly less readable if these functions are huge, so they need to stay as compact as possible. Second, the function will need anprintf() and we don't want to include stdio.h in type files as it proved to be particularly heavy and to cause definition headaches in the past. As such the file here only contains a macro enclosed in #ifdef EOF (that is defined in stdio), and provides an alternate empty one when no stdio is defined. This way it's the caller that has to include stdio first or it won't get anything back, and in practice the locations relying on this always have it. The macro has to be used in 3 steps: - prologue: dumps 0 and exits if the value is zero - flags: the macro can be recursively called and it will push the flag from bottom to top so that they appear in the same order as today without requiring to be declared the other way around - epilogue: dump remaining flags that were not identified The macro was arranged so that a single character can be used with no other argument to declare all flags at once. Example: #define _(n, ...) __APPEND_FLAG(buf, len, del, flg, n, #n, __VA_ARGS__) _(0); _(X_FLAG1, _(X_FLAG2, _(X_FLAG3, _(X_FLAG4)))); _(~0); #undef _ Existing files will have to be updated to rely on it, and more files could come soon. --- include/haproxy/show_flags-t.h | 79 ++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 include/haproxy/show_flags-t.h diff --git a/include/haproxy/show_flags-t.h b/include/haproxy/show_flags-t.h new file mode 100644 index 0000000000..3c045944ed --- /dev/null +++ b/include/haproxy/show_flags-t.h @@ -0,0 +1,79 @@ +/* + * include/haproxy/show_flags.h + * These are helper macros used to decode flags for debugging + * + * Copyright (C) 2022 Willy Tarreau - w@1wt.eu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _HAPROXY_SHOW_FLAGS_H +#define _HAPROXY_SHOW_FLAGS_H + +/* Only define the macro below if the caller included stdio, that we need for + * snprintf(). It will be used by many low-level includes and we don't want to + * include the huge stdio here by default. The macro is used to make a string + * of a set of flags (and handles one flag at a time). It will append into + * <_buf>:<_len> the state of flag <_val> in <_flg>, appending string <_del> as + * delimiters till the last flag is dumped, then updating <_buf> and <_len> + * accordingly. <_nam> is used as the name for value <_val>. <_flg> loses all + * dumped flags. If <_flg> is zero and <_val> is 0, a "0" is reported, this can + * be used as a prologue to the dump. If <_val> contains more than one bit set, + * <_flg>'s hexadecimal output is reported instead of a name. + * + * It is possible to use it to enumerate all flags from right to left so that + * they are easier to check in the code. It will start by executing the optional + * code block in the extra flags (if any) before proceeding with the dump using + * the arguments. It is suggested to locally rename it to a single-char macro + * locally for readability, e.g: + * + * #define _(n, ...) __APPEND_FLAG(buf, len, del, flg, n, #n, __VA_ARGS__) + * _(0); + * _(X_FLAG1, _(X_FLAG2, _(X_FLAG3))); + * _(~0); + * #undef _ + */ +#ifdef EOF + +#define __APPEND_FLAG(_buf, _len, _del, _flg, _val, _nam, ...) \ + do { \ + size_t _ret = 0; \ + unsigned int _flg0 = (_flg); \ + do { __VA_ARGS__; } while (0); \ + (_flg) &= ~(_val); \ + if (!(_val) && !(_flg)) \ + _ret = snprintf(_buf, _len, "0%s", \ + (_flg) ? (_del) : ""); \ + else if ((_flg0) & (_val)) { \ + if ((_val) & ((_val) - 1)) \ + _ret = snprintf(_buf, _len, "%#x%s", \ + (_flg0), (_flg) ? (_del) : ""); \ + else \ + _ret = snprintf(_buf, _len, _nam "%s", \ + (_flg) ? (_del) : ""); \ + } \ + if (_ret < _len) { \ + _len -= _ret; \ + _buf += _ret; \ + } \ + } while (0) + +#else /* EOF not defined => no stdio, do nothing */ + +#define __APPEND_FLAG(_buf, _len, _del, _flg, _val, _nam) do { } while (0) + +#endif /* EOF */ + +#endif /* _HAPROXY_SHOW_FLAGS_H */