MINOR: cli: support filtering on FD types in "show fd"

Depending on what we're debugging, some FDs can represent pollution in
the "show fd" output. Here we add a set of filters allowing to pick (or
exclude) any combination of listener, frontend conn, backend conn, pipes,
etc. "show fd l" will only list listening connections for example.
This commit is contained in:
Willy Tarreau 2023-03-31 16:33:53 +02:00
parent 5d5afe7900
commit 1cb041a6ee
2 changed files with 75 additions and 4 deletions

View File

@ -2685,9 +2685,19 @@ show events [<sink>] [-w] [-n]
combined with "-w" to only report new events. For convenience, "-wn" or "-nw"
may be used to enable both options at once.
show fd [<fd>]
show fd [-!plcfbsd]* [<fd>]
Dump the list of either all open file descriptors or just the one number <fd>
if specified. This is only aimed at developers who need to observe internal
if specified. A set of flags may optionally be passed to restrict the dump
only to certain FD types or to omit certain FD types. When '-' or '!' are
encountered, the selection is inverted for the following characters in the
same argument. The inversion is reset before each argument word delimited by
white spaces. Selectable FD types include 'p' for pipes, 'l' for listeners,
'c' for connections (any type), 'f' for frontend connections, 'b' for backend
connections (any type), 's' for connections to servers, 'd' for connections
to the "dispatch" address or the backend's transparent address. With this,
'b' is a shortcut for 'sd' and 'c' for 'fb' or 'fsd'. 'c!f' is equivalent to
'b' ("any connections except frontend connections" are indeed backend
connections). This is only aimed at developers who need to observe internal
states in order to debug complex issues such as abnormal CPU usages. One fd
is reported per lines, and for each of them, its state in the poller using
upper case letters for enabled flags and lower case for disabled flags, using

View File

@ -93,9 +93,20 @@ struct show_env_ctx {
};
/* CLI context for the "show fd" command */
/* flags for show_fd_ctx->show_mask */
#define CLI_SHOWFD_F_PI 0x00000001 /* pipes */
#define CLI_SHOWFD_F_LI 0x00000002 /* listeners */
#define CLI_SHOWFD_F_FE 0x00000004 /* frontend conns */
#define CLI_SHOWFD_F_SV 0x00000010 /* server-only conns */
#define CLI_SHOWFD_F_PX 0x00000020 /* proxy-only conns */
#define CLI_SHOWFD_F_BE 0x00000030 /* backend: srv+px */
#define CLI_SHOWFD_F_CO 0x00000034 /* conn: be+fe */
#define CLI_SHOWFD_F_ANY 0x0000003f /* any type */
struct show_fd_ctx {
int fd; /* first FD to show */
int show_one; /* stop after showing one FD */
uint show_mask; /* CLI_SHOWFD_F_xxx */
};
/* CLI context for the "show cli sockets" command */
@ -1259,6 +1270,7 @@ static int cli_io_handler_show_fd(struct appctx *appctx)
{
struct stconn *sc = appctx_sc(appctx);
struct show_fd_ctx *fdctx = appctx->svcctx;
uint match = fdctx->show_mask;
int fd = fdctx->fd;
int ret = 1;
@ -1321,6 +1333,17 @@ static int cli_io_handler_show_fd(struct appctx *appctx)
else if (fdt.iocb == sock_accept_iocb)
li = fdt.owner;
if (!((conn &&
((match & CLI_SHOWFD_F_SV && sv) ||
(match & CLI_SHOWFD_F_PX && px) ||
(match & CLI_SHOWFD_F_FE && li))) ||
(!conn &&
((match & CLI_SHOWFD_F_LI && li) ||
(match & CLI_SHOWFD_F_PI && !li /* only pipes match this */))))) {
/* not a desired type */
goto skip;
}
if (!fdt.thread_mask)
suspicious = 1;
@ -1573,14 +1596,52 @@ static int cli_parse_show_env(char **args, char *payload, struct appctx *appctx,
static int cli_parse_show_fd(char **args, char *payload, struct appctx *appctx, void *private)
{
struct show_fd_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
const char *c;
int arg;
if (!cli_has_level(appctx, ACCESS_LVL_OPER))
return 1;
if (*args[2]) {
arg = 2;
/* when starting with an inversion we preset every flag */
if (*args[arg] == '!' || *args[arg] == '-')
ctx->show_mask = CLI_SHOWFD_F_ANY;
while (*args[arg] && !isdigit((uchar)*args[arg])) {
uint flag = 0, inv = 0;
c = args[arg];
while (*c) {
switch (*c) {
case '!': inv = !inv; break;
case '-': inv = !inv; break;
case 'p': flag = CLI_SHOWFD_F_PI; break;
case 'l': flag = CLI_SHOWFD_F_LI; break;
case 'c': flag = CLI_SHOWFD_F_CO; break;
case 'f': flag = CLI_SHOWFD_F_FE; break;
case 'b': flag = CLI_SHOWFD_F_BE; break;
case 's': flag = CLI_SHOWFD_F_SV; break;
case 'd': flag = CLI_SHOWFD_F_PX; break;
default: return cli_err(appctx, "Invalid FD type\n");
}
c++;
if (!inv)
ctx->show_mask |= flag;
else
ctx->show_mask &= ~flag;
}
arg++;
}
/* default mask is to show everything */
if (!ctx->show_mask)
ctx->show_mask = CLI_SHOWFD_F_ANY;
if (*args[arg]) {
ctx->fd = atoi(args[2]);
ctx->show_one = 1;
}
return 0;
}
@ -3158,7 +3219,7 @@ static struct cli_kw_list cli_kws = {{ },{
{ { "show", "env", NULL }, "show env [var] : dump environment variables known to the process", cli_parse_show_env, cli_io_handler_show_env, NULL },
{ { "show", "cli", "sockets", NULL }, "show cli sockets : dump list of cli sockets", cli_parse_default, cli_io_handler_show_cli_sock, NULL, NULL, ACCESS_MASTER },
{ { "show", "cli", "level", NULL }, "show cli level : display the level of the current CLI session", cli_parse_show_lvl, NULL, NULL, NULL, ACCESS_MASTER},
{ { "show", "fd", NULL }, "show fd [num] : dump list of file descriptors in use or a specific one", cli_parse_show_fd, cli_io_handler_show_fd, NULL },
{ { "show", "fd", NULL }, "show fd [-!plcfbsd]* [num] : dump list of file descriptors in use or a specific one", cli_parse_show_fd, cli_io_handler_show_fd, NULL },
{ { "show", "version", NULL }, "show version : show version of the current process", cli_parse_show_version, NULL, NULL, NULL, ACCESS_MASTER },
{ { "operator", NULL }, "operator : lower the level of the current CLI session to operator", cli_parse_set_lvl, NULL, NULL, NULL, ACCESS_MASTER},
{ { "user", NULL }, "user : lower the level of the current CLI session to user", cli_parse_set_lvl, NULL, NULL, NULL, ACCESS_MASTER},