From 623dbf6f16d8dedac5a361a0d1daadc772ac842b Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 19 Sep 2025 14:53:58 +0200 Subject: Add --stats option to display event statistics Introduce a new --stats DELAY option that displays event statistics tables showing counts by epoll event type. Statistics are printed to stderr with a minimum delay between updates, and only when events occur. Signed-off-by: Laurent Vivier Signed-off-by: Stefano Brivio --- conf.c | 8 ++++++++ passt.1 | 5 +++++ passt.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ passt.h | 2 ++ 4 files changed, 73 insertions(+) diff --git a/conf.c b/conf.c index 02e903b..66b9e63 100644 --- a/conf.c +++ b/conf.c @@ -835,6 +835,8 @@ static void usage(const char *name, FILE *f, int status) "\n" " -d, --debug Be verbose\n" " --trace Be extra verbose, implies --debug\n" + " --stats DELAY Display events statistics\n" + " minimum DELAY seconds between updates\n" " -q, --quiet Don't print informational messages\n" " -f, --foreground Don't run in background\n" " default: run in background\n" @@ -1480,6 +1482,7 @@ void conf(struct ctx *c, int argc, char **argv) {"repair-path", required_argument, NULL, 28 }, {"migrate-exit", no_argument, NULL, 29 }, {"migrate-no-linger", no_argument, NULL, 30 }, + {"stats", required_argument, NULL, 31 }, { 0 }, }; const char *optstring = "+dqfel:hs:F:I:p:P:m:a:n:M:g:i:o:D:S:H:461t:u:T:U:"; @@ -1708,6 +1711,11 @@ void conf(struct ctx *c, int argc, char **argv) die("--migrate-no-linger is for vhost-user mode only"); c->migrate_no_linger = true; + break; + case 31: + if (!c->foreground) + die("Can't display statistics if not running in foreground"); + c->stats = strtol(optarg, NULL, 0); break; case 'd': c->debug = 1; diff --git a/passt.1 b/passt.1 index af5726a..0c17454 100644 --- a/passt.1 +++ b/passt.1 @@ -84,6 +84,11 @@ Be verbose, don't log to the system logger. .BR \-\-trace Be extra verbose, show single packets. Implies \fB--debug\fR. +.TP +.BR \-\-stats " " \fIDELAY\fR +Display events statistics with a minimum \fIDELAY\fR seconds between updates. +If there is no event, statistics are not displayed. + .TP .BR \-q ", " \-\-quiet Don't print informational messages. diff --git a/passt.c b/passt.c index a4ec115..31fbb75 100644 --- a/passt.c +++ b/passt.c @@ -83,6 +83,14 @@ char *epoll_type_str[] = { static_assert(ARRAY_SIZE(epoll_type_str) == EPOLL_NUM_TYPES, "epoll_type_str[] doesn't match enum epoll_type"); +/** + * struct passt_stats - Statistics + * @events: Event counters for epoll type events + */ +struct passt_stats { + unsigned long events[EPOLL_NUM_TYPES]; +}; + /** * post_handler() - Run periodic and deferred tasks for L4 protocol handlers * @c: Execution context @@ -174,6 +182,53 @@ static void exit_handler(int signal) _exit(EXIT_SUCCESS); } +/** + * print_stats() - Print event statistics table to stderr + * @c: Execution context + * @stats: Event counters + * @now: Current timestamp + */ +static void print_stats(const struct ctx *c, const struct passt_stats *stats, + const struct timespec *now) +{ + static struct timespec before; + static int lines_printed; + long long elapsed_ns; + int i; + + if (!c->stats) + return; + + elapsed_ns = (now->tv_sec - before.tv_sec) * 1000000000LL + + (now->tv_nsec - before.tv_nsec); + + if (elapsed_ns < c->stats * 1000000000LL) + return; + + before = *now; + + if (!(lines_printed % 20)) { + /* Table header */ + for (i = 1; i < EPOLL_NUM_TYPES; i++) { + int j; + + for (j = 0; j < i * (6 + 1); j++) { + if (j && !(j % (6 + 1))) + FPRINTF(stderr, "|"); + else + FPRINTF(stderr, " "); + } + FPRINTF(stderr, "%s\n", epoll_type_str[i]); + } + } + + FPRINTF(stderr, " "); + for (i = 1; i < EPOLL_NUM_TYPES; i++) + FPRINTF(stderr, " %6lu", stats->events[i]); + FPRINTF(stderr, "\n"); + lines_printed++; +} + /** * main() - Entry point and main loop * @argc: Argument count @@ -191,6 +246,7 @@ static void exit_handler(int signal) int main(int argc, char **argv) { struct epoll_event events[EPOLL_EVENTS]; + struct passt_stats stats = { 0 }; int nfds, i, devnull_fd = -1; struct ctx c = { 0 }; struct rlimit limit; @@ -362,6 +418,8 @@ loop: /* Can't happen */ ASSERT(0); } + stats.events[ref.type]++; + print_stats(&c, &stats, &now); } post_handler(&c, &now); diff --git a/passt.h b/passt.h index 3ffc19f..0075eb4 100644 --- a/passt.h +++ b/passt.h @@ -187,6 +187,7 @@ struct ip6_ctx { * @mode: Operation mode, qemu/UNIX domain socket or namespace/tap * @debug: Enable debug mode * @trace: Enable tracing (extra debug) mode + * @stats: Events statistics delay (0 means disabled) * @quiet: Don't print informational messages * @foreground: Run in foreground, don't log to stderr by default * @nofile: Maximum number of open files (ulimit -n) @@ -248,6 +249,7 @@ struct ctx { enum passt_modes mode; int debug; int trace; + int stats; int quiet; int foreground; int nofile; -- cgit v1.2.3