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).
diff --git a/src/activity.c b/src/activity.c
index 63decfe..1fafeab 100644
--- a/src/activity.c
+++ b/src/activity.c
@@ -36,6 +36,7 @@
 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 @@
 
 	/* 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 @@
 		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 @@
 					      (_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 @@
 		}
 #undef SHOW_VAL
 
+		/* try to dump what was possibly not dumped yet */
+
 		if (applet_putchk(appctx, &trash) == -1) {
 			/* buffer full, retry later */
 			return 0;