From b85418d75cf52c9b282c8711b6fd34a80e3a4632 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 20 Jan 2025 17:23:31 +0100 Subject: [PATCH] canbusload: support busload statistic Add '-s' option for display busload statistic, the output contains minimal, maximum and exponentially-damped moving sums of one second average (borrowed from Linux load average algorithm) since start or reset (press 'r' while running). canbusload 2024-09-23 17:15:18 (exact bitstuffing) can0@500k 942 107535 60168 0 18% min: 0%, max: 21%, load: 16% 6% 2% |XXX.................| Signed-off-by: Zhu Yi Signed-off-by: Hubert Streidl Signed-off-by: Mark Jonas Link: https://lore.kernel.org/r/20250120162332.19157-2-mark.jonas@de.bosch.com Signed-off-by: Marc Kleine-Budde --- canbusload.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/canbusload.c b/canbusload.c index 753d658..4679da5 100644 --- a/canbusload.c +++ b/canbusload.c @@ -45,14 +45,17 @@ #include #include #include +#include #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -72,6 +75,24 @@ #define NUMBAR (100 / PERCENTRES) /* number of bargraph elements */ #define BRSTRLEN 20 +/* + * Inspired from + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ + * include/linux/sched/loadavg.h + * + * Following are the fixed-point math constants and the exponential-damping + * factors for: + * - 1 samples/s in 1 minute + * - 1 samples/s in 5 minutes + * - 1 samples/s in 15 minutes + * in fixed-point representation. + */ +#define FP_SHIFT 12 /* bits of precision */ +#define FP_ONE (1 << FP_SHIFT) /* 1.0 fixed-point representation */ +#define EXP_1 4028 /* (1 / e ^ (1 / 60)) * FP_ONE */ +#define EXP_5 4082 /* (1 / e ^ (1 / 300)) * FP_ONE */ +#define EXP_15 4091 /* (1 / e ^ (1 / 900)) * FP_ONE */ + extern int optind, opterr, optopt; static struct { @@ -85,6 +106,11 @@ static struct { unsigned int recv_bits_total; unsigned int recv_bits_payload; unsigned int recv_bits_dbitrate; + unsigned int load_min; + unsigned int load_max; + unsigned int load_1m; + unsigned int load_5m; + unsigned int load_15m; } stat[MAXDEVS + 1]; static volatile int running = 1; @@ -96,8 +122,11 @@ static unsigned char redraw; static unsigned char timestamp; static unsigned char color; static unsigned char bargraph; +static bool statistic; +static bool reset; static enum cfl_mode mode = CFL_WORSTCASE; static char *prg; +static struct termios old; static void print_usage(char *prg) { @@ -111,6 +140,7 @@ static void print_usage(char *prg) fprintf(stderr, " -r (redraw the terminal - similar to top)\n"); fprintf(stderr, " -i (ignore bitstuffing in bandwidth calculation)\n"); fprintf(stderr, " -e (exact calculation of stuffed bits)\n"); + fprintf(stderr, " -s (show statistics, press 'r' to reset)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Up to %d CAN interfaces with mandatory bitrate can be specified on the \n", MAXDEVS); fprintf(stderr, "commandline in the form: @[,]\n"); @@ -161,6 +191,16 @@ static void create_bitrate_string(int stat_idx, int *max_bitratestr_len) *max_bitratestr_len = ptr; } +static unsigned int calc_load(unsigned int load_fp, + unsigned int exp_fp, + unsigned int sample) +{ + unsigned int sample_fp = sample << FP_SHIFT; + unsigned int damped_sum = (load_fp * exp_fp) + + (sample_fp * (FP_ONE - exp_fp)); + return damped_sum >> FP_SHIFT; +} + static void printstats(int signo) { int i, j, percent; @@ -234,6 +274,30 @@ static void printstats(int signo) stat[i].recv_bits_dbitrate, percent); + if (statistic) { + if (reset) { + stat[i].load_min = UINT_MAX; + stat[i].load_max = 0; + stat[i].load_1m = 0; + stat[i].load_5m = 0; + stat[i].load_15m = 0; + } + + stat[i].load_min = MIN(stat[i].load_min, percent); + stat[i].load_max = MAX(stat[i].load_max, percent); + + stat[i].load_1m = calc_load(stat[i].load_1m, EXP_1, percent); + stat[i].load_5m = calc_load(stat[i].load_5m, EXP_5, percent); + stat[i].load_15m = calc_load(stat[i].load_15m, EXP_15, percent); + + printf(" min:%3d%%, max:%3d%%, load:%3d%% %3d%% %3d%%", + stat[i].load_min, + stat[i].load_max, + (stat[i].load_1m + (FP_ONE >> 1)) >> FP_SHIFT, + (stat[i].load_5m + (FP_ONE >> 1)) >> FP_SHIFT, + (stat[i].load_15m + (FP_ONE >> 1)) >> FP_SHIFT); + } + if (bargraph) { printf(" |"); @@ -264,6 +328,8 @@ static void printstats(int signo) stat[i].recv_direction = '.'; } + reset = false; + if (!redraw) printf("\n"); @@ -272,6 +338,11 @@ static void printstats(int signo) alarm(1); } +void cleanup() +{ + tcsetattr(STDIN_FILENO, TCSANOW, &old); +} + int main(int argc, char **argv) { fd_set rdfs; @@ -288,6 +359,13 @@ int main(int argc, char **argv) unsigned int anydev_bitrate = 0; unsigned int anydev_dbitrate = 0; char anydev_bitratestr[BRSTRLEN]; /* 100000/2000000 => 100k/2M */ + struct termios temp; + + tcgetattr(STDIN_FILENO, &old); + atexit(cleanup); + temp = old; + temp.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &temp); signal(SIGTERM, sigterm); signal(SIGHUP, sigterm); @@ -297,7 +375,7 @@ int main(int argc, char **argv) prg = basename(argv[0]); - while ((opt = getopt(argc, argv, "rtbcieh?")) != -1) { + while ((opt = getopt(argc, argv, "rtbciesh?")) != -1) { switch (opt) { case 'r': redraw = 1; @@ -323,6 +401,11 @@ int main(int argc, char **argv) mode = CFL_EXACT; break; + case 's': + statistic = true; + reset = true; + break; + default: print_usage(prg); exit(1); @@ -449,12 +532,19 @@ int main(int argc, char **argv) while (running) { FD_ZERO(&rdfs); FD_SET(s, &rdfs); + FD_SET(STDIN_FILENO, &rdfs); if (select(s + 1, &rdfs, NULL, NULL, NULL) < 0) { //perror("pselect"); continue; } + if (FD_ISSET(STDIN_FILENO, &rdfs)) { + if (getchar() == 'r') { + reset = true; + } + } + /* these settings may be modified by recvmsg() */ iov.iov_len = sizeof(frame); msg.msg_namelen = sizeof(addr);