]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: activity: allow "show activity" to restart in the middle of a line
authorWilly Tarreau <w@1wt.eu>
Wed, 3 May 2023 14:18:30 +0000 (16:18 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 3 May 2023 15:26:11 +0000 (17:26 +0200)
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).

src/activity.c

index 63decfe7d17d82cb412c993469543e444891b44a..1fafeab0bffd4daa61feb720cb171f0983f67fe5 100644 (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;