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.
This commit is contained in:
Willy Tarreau 2022-09-09 14:34:12 +02:00
parent 20273ceec0
commit 77acaf5af5
1 changed files with 79 additions and 0 deletions

View File

@ -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 */