MINOR: activity: allow "show activity" to restart in the middle of a line

16kB buffers are not enough to dump 4096 threads with up to 10 bytes value
on each line. By storing the column number in the applet's context, we can
now restart from the last attempted column. This requires to dump all values
as they are produced, but it doesn't cost that much: a 4096-thread output
from a fesh process produces 300kB of output in ~8ms, or ~400us per call
(19*16kB), most of which are spent in vfprintf(). Given that we don't print
more than needed, it doesn't really change anything.

The main caveat is that when interrupted on such large lines, there's a
great possibility that the total or average on the first column doesn't
match anymore the sum or average of all dumped values. In order to avoid
this whenever possible (typically less than ~1500 threads), we first try
to dump entire lines and only proceed one column at a time when we have
to retry a failed dump. This is already the same for other stats that are
dumped in an interruptible way anyway and there's little that can be done
about it at this point (and not much immediately perceived benefit in
doing this with extreme accuracy for >1500 threads).
This commit is contained in:
Willy Tarreau 2023-05-03 16:18:30 +02:00
parent 6ed0b9885d
commit 8b3e39e37b
1 changed files with 25 additions and 1 deletions

View File

@ -36,6 +36,7 @@ struct show_prof_ctx {
struct show_activity_ctx {
int thr; /* thread ID to show or -1 for all */
int line; /* line number being dumped */
int col; /* columnline being dumped, 0 to nbt+1 */
};
#if defined(DEBUG_MEM_STATS)
@ -1037,6 +1038,13 @@ static int cli_io_handler_show_activity(struct appctx *appctx)
/* this macro is used below to dump values. The thread number is "thr",
* and runs from 0 to nbt-1 when values are printed using the formula.
* We normally try to dmup integral lines in order to keep counters
* consistent. If we fail once on a line, we'll detect it next time
* because we'll have committed actctx->col=1 thanks to the header
* always being dumped individually. We'll be called again thanks to
* the header being present, leaving some data in the buffer. In this
* case once we restart we'll proceed one column at a time to make sure
* we don't overflow the buffer again.
*/
#undef SHOW_VAL
#define SHOW_VAL(header, x, formula) \
@ -1044,12 +1052,13 @@ static int cli_io_handler_show_activity(struct appctx *appctx)
unsigned int _v[MAX_THREADS]; \
unsigned int _tot; \
const int _nbt = global.nbthread; \
int restarted = actctx->col > 0; \
int thr; \
_tot = thr = 0; \
do { \
_tot += _v[thr] = (x); \
} while (++thr < _nbt); \
for (thr = -2; thr <= _nbt; thr++) { \
for (thr = actctx->col - 2; thr <= _nbt; thr++) { \
if (thr == -2) { \
/* line header */ \
chunk_appendf(&trash, "%s", header); \
@ -1073,7 +1082,20 @@ static int cli_io_handler_show_activity(struct appctx *appctx)
(_nbt > 1 && tgt < 0) ? \
" ]" : ""); \
} \
if (thr == -2 || restarted) { \
/* failed once, emit one column at a time */\
if (applet_putchk(appctx, &trash) == -1) \
break; /* main loop handles it */ \
chunk_reset(&trash); \
actctx->col = thr + 3; \
} \
} \
if (applet_putchk(appctx, &trash) == -1) \
break; /* main loop will handle it */ \
/* OK dump done for this line */ \
chunk_reset(&trash); \
if (thr > _nbt) \
actctx->col = 0; \
} while (0)
/* retrieve uptime */
@ -1130,6 +1152,8 @@ static int cli_io_handler_show_activity(struct appctx *appctx)
}
#undef SHOW_VAL
/* try to dump what was possibly not dumped yet */
if (applet_putchk(appctx, &trash) == -1) {
/* buffer full, retry later */
return 0;