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 <yi.zhu5@cn.bosch.com> Signed-off-by: Hubert Streidl <hubert.streidl@de.bosch.com> Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com> Link: https://lore.kernel.org/r/20250120162332.19157-2-mark.jonas@de.bosch.com Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>pull/571/head
parent
6eb97b57c5
commit
b85418d75c
92
canbusload.c
92
canbusload.c
|
|
@ -45,14 +45,17 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/param.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
@ -72,6 +75,24 @@
|
||||||
#define NUMBAR (100 / PERCENTRES) /* number of bargraph elements */
|
#define NUMBAR (100 / PERCENTRES) /* number of bargraph elements */
|
||||||
#define BRSTRLEN 20
|
#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;
|
extern int optind, opterr, optopt;
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
|
|
@ -85,6 +106,11 @@ static struct {
|
||||||
unsigned int recv_bits_total;
|
unsigned int recv_bits_total;
|
||||||
unsigned int recv_bits_payload;
|
unsigned int recv_bits_payload;
|
||||||
unsigned int recv_bits_dbitrate;
|
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];
|
} stat[MAXDEVS + 1];
|
||||||
|
|
||||||
static volatile int running = 1;
|
static volatile int running = 1;
|
||||||
|
|
@ -96,8 +122,11 @@ static unsigned char redraw;
|
||||||
static unsigned char timestamp;
|
static unsigned char timestamp;
|
||||||
static unsigned char color;
|
static unsigned char color;
|
||||||
static unsigned char bargraph;
|
static unsigned char bargraph;
|
||||||
|
static bool statistic;
|
||||||
|
static bool reset;
|
||||||
static enum cfl_mode mode = CFL_WORSTCASE;
|
static enum cfl_mode mode = CFL_WORSTCASE;
|
||||||
static char *prg;
|
static char *prg;
|
||||||
|
static struct termios old;
|
||||||
|
|
||||||
static void print_usage(char *prg)
|
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, " -r (redraw the terminal - similar to top)\n");
|
||||||
fprintf(stderr, " -i (ignore bitstuffing in bandwidth calculation)\n");
|
fprintf(stderr, " -i (ignore bitstuffing in bandwidth calculation)\n");
|
||||||
fprintf(stderr, " -e (exact calculation of stuffed bits)\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, "\n");
|
||||||
fprintf(stderr, "Up to %d CAN interfaces with mandatory bitrate can be specified on the \n", MAXDEVS);
|
fprintf(stderr, "Up to %d CAN interfaces with mandatory bitrate can be specified on the \n", MAXDEVS);
|
||||||
fprintf(stderr, "commandline in the form: <ifname>@<bitrate>[,<dbitrate>]\n");
|
fprintf(stderr, "commandline in the form: <ifname>@<bitrate>[,<dbitrate>]\n");
|
||||||
|
|
@ -161,6 +191,16 @@ static void create_bitrate_string(int stat_idx, int *max_bitratestr_len)
|
||||||
*max_bitratestr_len = ptr;
|
*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)
|
static void printstats(int signo)
|
||||||
{
|
{
|
||||||
int i, j, percent;
|
int i, j, percent;
|
||||||
|
|
@ -234,6 +274,30 @@ static void printstats(int signo)
|
||||||
stat[i].recv_bits_dbitrate,
|
stat[i].recv_bits_dbitrate,
|
||||||
percent);
|
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) {
|
if (bargraph) {
|
||||||
|
|
||||||
printf(" |");
|
printf(" |");
|
||||||
|
|
@ -264,6 +328,8 @@ static void printstats(int signo)
|
||||||
stat[i].recv_direction = '.';
|
stat[i].recv_direction = '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset = false;
|
||||||
|
|
||||||
if (!redraw)
|
if (!redraw)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
|
|
@ -272,6 +338,11 @@ static void printstats(int signo)
|
||||||
alarm(1);
|
alarm(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cleanup()
|
||||||
|
{
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &old);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
fd_set rdfs;
|
fd_set rdfs;
|
||||||
|
|
@ -288,6 +359,13 @@ int main(int argc, char **argv)
|
||||||
unsigned int anydev_bitrate = 0;
|
unsigned int anydev_bitrate = 0;
|
||||||
unsigned int anydev_dbitrate = 0;
|
unsigned int anydev_dbitrate = 0;
|
||||||
char anydev_bitratestr[BRSTRLEN]; /* 100000/2000000 => 100k/2M */
|
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(SIGTERM, sigterm);
|
||||||
signal(SIGHUP, sigterm);
|
signal(SIGHUP, sigterm);
|
||||||
|
|
@ -297,7 +375,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
prg = basename(argv[0]);
|
prg = basename(argv[0]);
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv, "rtbcieh?")) != -1) {
|
while ((opt = getopt(argc, argv, "rtbciesh?")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'r':
|
case 'r':
|
||||||
redraw = 1;
|
redraw = 1;
|
||||||
|
|
@ -323,6 +401,11 @@ int main(int argc, char **argv)
|
||||||
mode = CFL_EXACT;
|
mode = CFL_EXACT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
statistic = true;
|
||||||
|
reset = true;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
print_usage(prg);
|
print_usage(prg);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
@ -449,12 +532,19 @@ int main(int argc, char **argv)
|
||||||
while (running) {
|
while (running) {
|
||||||
FD_ZERO(&rdfs);
|
FD_ZERO(&rdfs);
|
||||||
FD_SET(s, &rdfs);
|
FD_SET(s, &rdfs);
|
||||||
|
FD_SET(STDIN_FILENO, &rdfs);
|
||||||
|
|
||||||
if (select(s + 1, &rdfs, NULL, NULL, NULL) < 0) {
|
if (select(s + 1, &rdfs, NULL, NULL, NULL) < 0) {
|
||||||
//perror("pselect");
|
//perror("pselect");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FD_ISSET(STDIN_FILENO, &rdfs)) {
|
||||||
|
if (getchar() == 'r') {
|
||||||
|
reset = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* these settings may be modified by recvmsg() */
|
/* these settings may be modified by recvmsg() */
|
||||||
iov.iov_len = sizeof(frame);
|
iov.iov_len = sizeof(frame);
|
||||||
msg.msg_namelen = sizeof(addr);
|
msg.msg_namelen = sizeof(addr);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue