Message ID | 20210501031059.529906-1-kuba@kernel.org |
---|---|
State | New |
Headers | show |
Series | [PACTH,iproute2-next] ip: dynamically size columns when printing stats | expand |
On Fri, 30 Apr 2021 20:10:59 -0700 Jakub Kicinski <kuba@kernel.org> wrote: > This change makes ip -s -s output size the columns > automatically. I often find myself using json > output because the normal output is unreadable. > Even on a laptop after 2 days of uptime byte > and packet counters almost overflow their columns, > let alone a busy server. > > For max readability switch to right align. > > Before: > > RX: bytes packets errors dropped missed mcast > 8227918473 8617683 0 0 0 0 > RX errors: length crc frame fifo overrun > 0 0 0 0 0 > TX: bytes packets errors dropped carrier collsns > 691937917 4727223 0 0 0 0 > TX errors: aborted fifo window heartbeat transns > 0 0 0 0 10 > > After: > > RX: bytes packets errors dropped missed mcast > 8228633710 8618408 0 0 0 0 > RX errors: length crc frame fifo overrun > 0 0 0 0 0 > TX: bytes packets errors dropped carrier collsns > 692006303 4727740 0 0 0 0 > TX errors: aborted fifo window heartbt transns > 0 0 0 0 10 > > More importantly, with large values before: > > RX: bytes packets errors dropped overrun mcast > 126570234447969 15016149200 0 0 0 0 > RX errors: length crc frame fifo missed > 0 0 0 0 0 > TX: bytes packets errors dropped carrier collsns > 126570234447969 15016149200 0 0 0 0 > TX errors: aborted fifo window heartbeat transns > 0 0 0 0 10 > > Note that in this case we have full shift by a column, > e.g. the value under "dropped" is actually for "errors" etc. > > After: > > RX: bytes packets errors dropped missed mcast > 126570234447969 15016149200 0 0 0 0 > RX errors: length crc frame fifo overrun > 0 0 0 0 0 > TX: bytes packets errors dropped carrier collsns > 126570234447969 15016149200 0 0 0 0 > TX errors: aborted fifo window heartbt transns > 0 0 0 0 10 > > Signed-off-by: Jakub Kicinski <kuba@kernel.org> Looks good to me. Maybe good time to refactor the code to make it table driven rather than individual statistic items.
On Mon, 3 May 2021 07:57:39 -0700 Stephen Hemminger wrote: > Maybe good time to refactor the code to make it table driven rather > than individual statistic items. 🤔 should be doable. Can I do it on top or before making the change to the columns?
On 5/3/21 10:00 AM, Jakub Kicinski wrote: > On Mon, 3 May 2021 07:57:39 -0700 Stephen Hemminger wrote: >> Maybe good time to refactor the code to make it table driven rather >> than individual statistic items. > > 🤔 should be doable. > > Can I do it on top or before making the change to the columns? > I think it can be a follow on change. This one is clearly an improvement for large numbers.
On Mon, 3 May 2021 11:16:41 -0600 David Ahern wrote: > On 5/3/21 10:00 AM, Jakub Kicinski wrote: > > On Mon, 3 May 2021 07:57:39 -0700 Stephen Hemminger wrote: > >> Maybe good time to refactor the code to make it table driven rather > >> than individual statistic items. > > > > 🤔 should be doable. > > > > Can I do it on top or before making the change to the columns? > > > > I think it can be a follow on change. This one is clearly an improvement > for large numbers. Fun little discrepancy b/w JSON and plain on what's printed with -s vs -s -s: JSON output prints rx_over_errors where plain would print rx_missed_errors. I will change plain to follow JSON, since presumably we should worry more about breaking machine-readable format.
On 5/3/21 1:22 PM, Jakub Kicinski wrote: > On Mon, 3 May 2021 11:16:41 -0600 David Ahern wrote: >> On 5/3/21 10:00 AM, Jakub Kicinski wrote: >>> On Mon, 3 May 2021 07:57:39 -0700 Stephen Hemminger wrote: >>>> Maybe good time to refactor the code to make it table driven rather >>>> than individual statistic items. >>> >>> 🤔 should be doable. >>> >>> Can I do it on top or before making the change to the columns? >>> >> >> I think it can be a follow on change. This one is clearly an improvement >> for large numbers. > > Fun little discrepancy b/w JSON and plain on what's printed with -s > vs -s -s: JSON output prints rx_over_errors where plain would print > rx_missed_errors. I will change plain to follow JSON, since presumably > we should worry more about breaking machine-readable format. > you mean from this commit? commit b8663da04939dd5de223ca0de23e466ce0a372f7 Author: Jakub Kicinski <kuba@kernel.org> Date: Wed Sep 16 12:42:49 2020 -0700 ip: promote missed packets to the -s row
On Mon, 3 May 2021 20:15:27 -0600 David Ahern wrote: > On 5/3/21 1:22 PM, Jakub Kicinski wrote: > > On Mon, 3 May 2021 11:16:41 -0600 David Ahern wrote: > >> I think it can be a follow on change. This one is clearly an improvement > >> for large numbers. > > > > Fun little discrepancy b/w JSON and plain on what's printed with -s > > vs -s -s: JSON output prints rx_over_errors where plain would print > > rx_missed_errors. I will change plain to follow JSON, since presumably > > we should worry more about breaking machine-readable format. > > you mean from this commit? > > commit b8663da04939dd5de223ca0de23e466ce0a372f7 > Author: Jakub Kicinski <kuba@kernel.org> > Date: Wed Sep 16 12:42:49 2020 -0700 > > ip: promote missed packets to the -s row Yes, with table driven printing we will lose that again. With the implementation I have we don't save that much LoC: ip/ipaddress.c | 339 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 170 insertions(+), 169 deletions(-) ----->8------ diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 06ca7273c531..6b3b03002a52 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -10,7 +10,6 @@ * */ -#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -545,27 +544,18 @@ static void print_vfinfo(FILE *fp, struct ifinfomsg *ifi, struct rtattr *vfinfo) print_vf_stats64(fp, vf[IFLA_VF_STATS]); } -static void size_columns(unsigned int cols[], unsigned int n, ...) +static void size_column(unsigned int *col, uint64_t val) { - unsigned int i, len; - uint64_t val, powi; - va_list args; - - va_start(args, n); - - for (i = 0; i < n; i++) { - val = va_arg(args, unsigned long long); - - if (human_readable) - continue; + unsigned int len; + uint64_t powi; - for (len = 1, powi = 10; powi < val; len++, powi *= 10) - /* nothing */; - if (len > cols[i]) - cols[i] = len; - } + if (human_readable) + return; - va_end(args); + for (len = 1, powi = 10; powi < val; len++, powi *= 10) + /* nothing */; + if (len > *col) + *col = len; } void print_num(FILE *fp, unsigned int width, uint64_t count) @@ -679,192 +669,203 @@ static void print_vf_stats64(FILE *fp, struct rtattr *vfstats) } } +struct stats_desc { + const char *json_key; + const char *hdr; + unsigned int offset; + bool hide; + bool empty; + bool direct; +}; + +#define STATS_DESC(_json_key, _hdr, _memb) \ + { \ + .json_key = (_json_key), \ + .hdr = (_hdr), \ + .offset = offsetof(struct rtnl_link_stats64, _memb), \ + } +#define STATS_EMPTY { .hdr = "", .empty = true, } + +#define N_STAT_COLUMNS 7 + +static const struct stats_desc _stats_rx[N_STAT_COLUMNS] = { + STATS_DESC("bytes", "bytes", rx_bytes), + STATS_DESC("packets", "packets", rx_packets), + STATS_DESC("errors", "errors", rx_errors), + STATS_DESC("dropped", "dropped", rx_dropped), + STATS_DESC("over_errors", "overrun", rx_over_errors), + STATS_DESC("multicast", "mcast", multicast), + STATS_DESC("compressed", "compressed", rx_compressed), +}; + +static const struct stats_desc _stats_rx_err[N_STAT_COLUMNS] = { + STATS_EMPTY, + STATS_DESC("length_errors", "length", rx_length_errors), + STATS_DESC("crc_errors", "crc", rx_crc_errors), + STATS_DESC("frame_errors", "frame", rx_frame_errors), + STATS_DESC("fifo_errors", "fifo", rx_fifo_errors), + STATS_DESC("missed_errors", "missed", rx_missed_errors), + STATS_DESC("nohandler", "nohandler", rx_nohandler), +}; + +static const struct stats_desc _stats_tx[N_STAT_COLUMNS] = { + STATS_DESC("bytes", "bytes", tx_bytes), + STATS_DESC("packets", "packets", tx_packets), + STATS_DESC("errors", "errors", tx_errors), + STATS_DESC("dropped", "dropped", tx_dropped), + STATS_DESC("carrier_errors", "carrier", tx_carrier_errors), + STATS_DESC("collisions", "collsns", collisions), + STATS_DESC("compressed", "compressed", tx_compressed), +}; + +static const struct stats_desc _stats_tx_err[N_STAT_COLUMNS] = { + STATS_EMPTY, + STATS_DESC("aborted_errors", "aborted", tx_aborted_errors), + STATS_DESC("fifo_errors", "fifo", tx_fifo_errors), + STATS_DESC("window_errors", "window", tx_window_errors), + STATS_DESC("heartbeat_errors", "heartbt", tx_heartbeat_errors), + { .json_key = "carrier_changes", .hdr = "transns", .direct = true, }, + { .hide = true, }, +}; + +static void stat_table_print_json(struct rtnl_link_stats64 *s, + const struct stats_desc desc[]) +{ + unsigned int i; + + for (i = 0; i < N_STAT_COLUMNS; i++) { + if (desc[i].hide || desc[i].empty) + continue; + print_u64(PRINT_JSON, desc[i].json_key, NULL, + desc[i].direct ? desc[i].offset : + ((__u64 *)s)[desc[i].offset / sizeof(__u64)]); + } +} + +static void stat_table_size_columns(const char *pfx, + struct rtnl_link_stats64 *s, + unsigned int cols[], + const struct stats_desc desc[]) +{ + unsigned int i, len; + + for (i = 0; i < N_STAT_COLUMNS; i++) { + if (desc[i].hide) + continue; + len = desc[i].empty ? 0 : strlen(desc[i].hdr); + if (i == 0) + len += strlen(pfx) + !desc[i].empty; + if (len > cols[i]) + cols[i] = len; + if (desc[i].empty) + continue; + size_column(&cols[i], desc[i].direct ? desc[i].offset : + ((__u64 *)s)[desc[i].offset / sizeof(__u64)]); + } +} + +static void stat_table_print_fp(FILE *fp, const char *pfx, + struct rtnl_link_stats64 *s, + unsigned int cols[], + const struct stats_desc desc[]) +{ + unsigned int i, col; + + fprintf(fp, " %s", pfx); + for (i = 0; i < N_STAT_COLUMNS; i++) { + if (desc[i].hide) + continue; + col = cols[i]; + if (i == 0) + col -= strlen(pfx) + 1; + fprintf(fp, "%*s", col + 1, desc[i].hdr); + } + fprintf(fp, "%s", _SL_); + + fprintf(fp, " "); + for (i = 0; i < N_STAT_COLUMNS; i++) { + if (desc[i].hide) + continue; + if (desc[i].empty) { + printf("%*s", cols[i] + 1, ""); + continue; + } + + print_num(fp, cols[i], desc[i].direct ? desc[i].offset : + ((__u64 *)s)[desc[i].offset / sizeof(__u64)]); + } +} + static void __print_link_stats(FILE *fp, struct rtattr *tb[]) { + struct stats_desc stats_rx[N_STAT_COLUMNS], stats_tx[N_STAT_COLUMNS]; const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES]; + struct stats_desc stats_rx_err[N_STAT_COLUMNS]; + struct stats_desc stats_tx_err[N_STAT_COLUMNS]; struct rtnl_link_stats64 _s, *s = &_s; - unsigned int cols[] = { - strlen("*X errors:"), - strlen("packets"), - strlen("errors"), - strlen("dropped"), - strlen("heartbt"), - strlen("overrun"), - strlen("compressed"), - }; + unsigned int cols[N_STAT_COLUMNS] = { }; int ret; ret = get_rtnl_link_stats_rta(s, tb); if (ret < 0) return; + memcpy(stats_rx, _stats_rx, sizeof(stats_rx)); + stats_rx[6].hide = !s->rx_compressed; + memcpy(stats_rx_err, _stats_rx_err, sizeof(stats_rx_err)); + stats_rx_err[6].hide = !s->rx_nohandler; + memcpy(stats_tx, _stats_tx, sizeof(stats_tx)); + stats_tx[6].hide = !s->tx_compressed; + memcpy(stats_tx_err, _stats_tx_err, sizeof(stats_tx_err)); + stats_tx_err[5].hide = !carrier_changes; + if (carrier_changes) + stats_tx_err[5].offset = rta_getattr_u32(carrier_changes); + if (is_json_context()) { open_json_object((ret == sizeof(*s)) ? "stats64" : "stats"); - /* RX stats */ open_json_object("rx"); - print_u64(PRINT_JSON, "bytes", NULL, s->rx_bytes); - print_u64(PRINT_JSON, "packets", NULL, s->rx_packets); - print_u64(PRINT_JSON, "errors", NULL, s->rx_errors); - print_u64(PRINT_JSON, "dropped", NULL, s->rx_dropped); - print_u64(PRINT_JSON, "over_errors", NULL, s->rx_over_errors); - print_u64(PRINT_JSON, "multicast", NULL, s->multicast); - if (s->rx_compressed) - print_u64(PRINT_JSON, - "compressed", NULL, s->rx_compressed); - - /* RX error stats */ - if (show_stats > 1) { - print_u64(PRINT_JSON, - "length_errors", - NULL, s->rx_length_errors); - print_u64(PRINT_JSON, - "crc_errors", - NULL, s->rx_crc_errors); - print_u64(PRINT_JSON, - "frame_errors", - NULL, s->rx_frame_errors); - print_u64(PRINT_JSON, - "fifo_errors", - NULL, s->rx_fifo_errors); - print_u64(PRINT_JSON, - "missed_errors", - NULL, s->rx_missed_errors); - if (s->rx_nohandler) - print_u64(PRINT_JSON, - "nohandler", NULL, s->rx_nohandler); - } + stat_table_print_json(s, stats_rx); + if (show_stats > 1) + stat_table_print_json(s, stats_rx_err); close_json_object(); - /* TX stats */ open_json_object("tx"); - print_u64(PRINT_JSON, "bytes", NULL, s->tx_bytes); - print_u64(PRINT_JSON, "packets", NULL, s->tx_packets); - print_u64(PRINT_JSON, "errors", NULL, s->tx_errors); - print_u64(PRINT_JSON, "dropped", NULL, s->tx_dropped); - print_u64(PRINT_JSON, - "carrier_errors", - NULL, s->tx_carrier_errors); - print_u64(PRINT_JSON, "collisions", NULL, s->collisions); - if (s->tx_compressed) - print_u64(PRINT_JSON, - "compressed", NULL, s->tx_compressed); - - /* TX error stats */ - if (show_stats > 1) { - print_u64(PRINT_JSON, - "aborted_errors", - NULL, s->tx_aborted_errors); - print_u64(PRINT_JSON, - "fifo_errors", - NULL, s->tx_fifo_errors); - print_u64(PRINT_JSON, - "window_errors", - NULL, s->tx_window_errors); - print_u64(PRINT_JSON, - "heartbeat_errors", - NULL, s->tx_heartbeat_errors); - if (carrier_changes) - print_u64(PRINT_JSON, "carrier_changes", NULL, - rta_getattr_u32(carrier_changes)); - } - + stat_table_print_json(s, stats_tx); + if (show_stats > 1) + stat_table_print_json(s, stats_tx_err); close_json_object(); + close_json_object(); } else { - size_columns(cols, ARRAY_SIZE(cols), - s->rx_bytes, s->rx_packets, s->rx_errors, - s->rx_dropped, s->rx_missed_errors, - s->multicast, s->rx_compressed); + stat_table_size_columns("RX:", s, cols, stats_rx); if (show_stats > 1) - size_columns(cols, ARRAY_SIZE(cols), 0, - s->rx_length_errors, s->rx_crc_errors, - s->rx_frame_errors, s->rx_fifo_errors, - s->rx_over_errors, s->rx_nohandler); - size_columns(cols, ARRAY_SIZE(cols), - s->tx_bytes, s->tx_packets, s->tx_errors, - s->tx_dropped, s->tx_carrier_errors, - s->collisions, s->tx_compressed); + stat_table_size_columns("RX errors:", s, cols, + stats_rx_err); + stat_table_size_columns("TX:", s, cols, stats_tx); if (show_stats > 1) - size_columns(cols, ARRAY_SIZE(cols), 0, 0, - s->tx_aborted_errors, s->tx_fifo_errors, - s->tx_window_errors, - s->tx_heartbeat_errors, - carrier_changes ? - rta_getattr_u32(carrier_changes) : 0); + stat_table_size_columns("TX errors:", s, cols, + stats_tx_err); /* RX stats */ - fprintf(fp, " RX: %*s %*s %*s %*s %*s %*s %*s%s", - cols[0] - 4, "bytes", cols[1], "packets", - cols[2], "errors", cols[3], "dropped", - cols[4], "missed", cols[5], "mcast", - cols[6], s->rx_compressed ? "compressed" : "", _SL_); - - fprintf(fp, " "); - print_num(fp, cols[0], s->rx_bytes); - print_num(fp, cols[1], s->rx_packets); - print_num(fp, cols[2], s->rx_errors); - print_num(fp, cols[3], s->rx_dropped); - print_num(fp, cols[4], s->rx_missed_errors); - print_num(fp, cols[5], s->multicast); - if (s->rx_compressed) - print_num(fp, cols[6], s->rx_compressed); + stat_table_print_fp(fp, "RX:", s, cols, stats_rx); /* RX error stats */ if (show_stats > 1) { fprintf(fp, "%s", _SL_); - fprintf(fp, " RX errors:%*s %*s %*s %*s %*s %*s %*s%s", - cols[0] - 10, "", cols[1], "length", - cols[2], "crc", cols[3], "frame", - cols[4], "fifo", cols[5], "overrun", - cols[6], s->rx_nohandler ? "nohandler" : "", - _SL_); - fprintf(fp, "%*s", cols[0] + 5, ""); - print_num(fp, cols[1], s->rx_length_errors); - print_num(fp, cols[2], s->rx_crc_errors); - print_num(fp, cols[3], s->rx_frame_errors); - print_num(fp, cols[4], s->rx_fifo_errors); - print_num(fp, cols[5], s->rx_over_errors); - if (s->rx_nohandler) - print_num(fp, cols[6], s->rx_nohandler); + stat_table_print_fp(fp, "RX errors:", s, cols, + stats_rx_err); } fprintf(fp, "%s", _SL_); /* TX stats */ - fprintf(fp, " TX: %*s %*s %*s %*s %*s %*s %*s%s", - cols[0] - 4, "bytes", cols[1], "packets", - cols[2], "errors", cols[3], "dropped", - cols[4], "carrier", cols[5], "collsns", - cols[6], s->tx_compressed ? "compressed" : "", _SL_); - - fprintf(fp, " "); - print_num(fp, cols[0], s->tx_bytes); - print_num(fp, cols[1], s->tx_packets); - print_num(fp, cols[2], s->tx_errors); - print_num(fp, cols[3], s->tx_dropped); - print_num(fp, cols[4], s->tx_carrier_errors); - print_num(fp, cols[5], s->collisions); - if (s->tx_compressed) - print_num(fp, cols[6], s->tx_compressed); + stat_table_print_fp(fp, "TX:", s, cols, stats_tx); /* TX error stats */ if (show_stats > 1) { fprintf(fp, "%s", _SL_); - fprintf(fp, " TX errors:%*s %*s %*s %*s %*s %*s%s", - cols[0] - 10, "", cols[1], "aborted", - cols[2], "fifo", cols[3], "window", - cols[4], "heartbt", - cols[5], carrier_changes ? "transns" : "", - _SL_); - - fprintf(fp, "%*s", cols[0] + 5, ""); - print_num(fp, cols[1], s->tx_aborted_errors); - print_num(fp, cols[2], s->tx_fifo_errors); - print_num(fp, cols[3], s->tx_window_errors); - print_num(fp, cols[4], s->tx_heartbeat_errors); - if (carrier_changes) - print_num(fp, cols[5], - rta_getattr_u32(carrier_changes)); + stat_table_print_fp(fp, "TX errors:", s, cols, + stats_tx_err); } } } -- 2.31.1
On 4/30/21 9:10 PM, Jakub Kicinski wrote: > This change makes ip -s -s output size the columns > automatically. I often find myself using json > output because the normal output is unreadable. > Even on a laptop after 2 days of uptime byte > and packet counters almost overflow their columns, > let alone a busy server. > > For max readability switch to right align. > > Before: > > RX: bytes packets errors dropped missed mcast > 8227918473 8617683 0 0 0 0 > RX errors: length crc frame fifo overrun > 0 0 0 0 0 > TX: bytes packets errors dropped carrier collsns > 691937917 4727223 0 0 0 0 > TX errors: aborted fifo window heartbeat transns > 0 0 0 0 10 > > After: > > RX: bytes packets errors dropped missed mcast > 8228633710 8618408 0 0 0 0 > RX errors: length crc frame fifo overrun > 0 0 0 0 0 > TX: bytes packets errors dropped carrier collsns > 692006303 4727740 0 0 0 0 > TX errors: aborted fifo window heartbt transns > 0 0 0 0 10 > > More importantly, with large values before: > > RX: bytes packets errors dropped overrun mcast > 126570234447969 15016149200 0 0 0 0 > RX errors: length crc frame fifo missed > 0 0 0 0 0 > TX: bytes packets errors dropped carrier collsns > 126570234447969 15016149200 0 0 0 0 > TX errors: aborted fifo window heartbeat transns > 0 0 0 0 10 > > Note that in this case we have full shift by a column, > e.g. the value under "dropped" is actually for "errors" etc. > > After: > > RX: bytes packets errors dropped missed mcast > 126570234447969 15016149200 0 0 0 0 > RX errors: length crc frame fifo overrun > 0 0 0 0 0 > TX: bytes packets errors dropped carrier collsns > 126570234447969 15016149200 0 0 0 0 > TX errors: aborted fifo window heartbt transns > 0 0 0 0 10 > > Signed-off-by: Jakub Kicinski <kuba@kernel.org> > --- > Note - this patch does depend on the trivial nohandler print fix. > > ip/ipaddress.c | 146 +++++++++++++++++++++++++++++++++++-------------- > 1 file changed, 106 insertions(+), 40 deletions(-) > good improvement. applied. Thanks,
diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 8783b70d81e2..06ca7273c531 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -10,6 +10,7 @@ * */ +#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -544,6 +545,29 @@ static void print_vfinfo(FILE *fp, struct ifinfomsg *ifi, struct rtattr *vfinfo) print_vf_stats64(fp, vf[IFLA_VF_STATS]); } +static void size_columns(unsigned int cols[], unsigned int n, ...) +{ + unsigned int i, len; + uint64_t val, powi; + va_list args; + + va_start(args, n); + + for (i = 0; i < n; i++) { + val = va_arg(args, unsigned long long); + + if (human_readable) + continue; + + for (len = 1, powi = 10; powi < val; len++, powi *= 10) + /* nothing */; + if (len > cols[i]) + cols[i] = len; + } + + va_end(args); +} + void print_num(FILE *fp, unsigned int width, uint64_t count) { const char *prefix = "kMGTPE"; @@ -554,7 +578,7 @@ void print_num(FILE *fp, unsigned int width, uint64_t count) char buf[64]; if (!human_readable || count < base) { - fprintf(fp, "%-*"PRIu64" ", width, count); + fprintf(fp, "%*"PRIu64" ", width, count); return; } @@ -581,7 +605,7 @@ void print_num(FILE *fp, unsigned int width, uint64_t count) snprintf(buf, sizeof(buf), "%.*f%c%s", precision, (double) count / powi, *prefix, use_iec ? "i" : ""); - fprintf(fp, "%-*s ", width, buf); + fprintf(fp, "%*s ", width, buf); } static void print_vf_stats64(FILE *fp, struct rtattr *vfstats) @@ -659,6 +683,15 @@ static void __print_link_stats(FILE *fp, struct rtattr *tb[]) { const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES]; struct rtnl_link_stats64 _s, *s = &_s; + unsigned int cols[] = { + strlen("*X errors:"), + strlen("packets"), + strlen("errors"), + strlen("dropped"), + strlen("heartbt"), + strlen("overrun"), + strlen("compressed"), + }; int ret; ret = get_rtnl_link_stats_rta(s, tb); @@ -739,65 +772,98 @@ static void __print_link_stats(FILE *fp, struct rtattr *tb[]) close_json_object(); close_json_object(); } else { + size_columns(cols, ARRAY_SIZE(cols), + s->rx_bytes, s->rx_packets, s->rx_errors, + s->rx_dropped, s->rx_missed_errors, + s->multicast, s->rx_compressed); + if (show_stats > 1) + size_columns(cols, ARRAY_SIZE(cols), 0, + s->rx_length_errors, s->rx_crc_errors, + s->rx_frame_errors, s->rx_fifo_errors, + s->rx_over_errors, s->rx_nohandler); + size_columns(cols, ARRAY_SIZE(cols), + s->tx_bytes, s->tx_packets, s->tx_errors, + s->tx_dropped, s->tx_carrier_errors, + s->collisions, s->tx_compressed); + if (show_stats > 1) + size_columns(cols, ARRAY_SIZE(cols), 0, 0, + s->tx_aborted_errors, s->tx_fifo_errors, + s->tx_window_errors, + s->tx_heartbeat_errors, + carrier_changes ? + rta_getattr_u32(carrier_changes) : 0); + /* RX stats */ - fprintf(fp, " RX: bytes packets errors dropped missed mcast %s%s", - s->rx_compressed ? "compressed" : "", _SL_); + fprintf(fp, " RX: %*s %*s %*s %*s %*s %*s %*s%s", + cols[0] - 4, "bytes", cols[1], "packets", + cols[2], "errors", cols[3], "dropped", + cols[4], "missed", cols[5], "mcast", + cols[6], s->rx_compressed ? "compressed" : "", _SL_); fprintf(fp, " "); - print_num(fp, 10, s->rx_bytes); - print_num(fp, 8, s->rx_packets); - print_num(fp, 7, s->rx_errors); - print_num(fp, 7, s->rx_dropped); - print_num(fp, 7, s->rx_missed_errors); - print_num(fp, 7, s->multicast); + print_num(fp, cols[0], s->rx_bytes); + print_num(fp, cols[1], s->rx_packets); + print_num(fp, cols[2], s->rx_errors); + print_num(fp, cols[3], s->rx_dropped); + print_num(fp, cols[4], s->rx_missed_errors); + print_num(fp, cols[5], s->multicast); if (s->rx_compressed) - print_num(fp, 7, s->rx_compressed); + print_num(fp, cols[6], s->rx_compressed); /* RX error stats */ if (show_stats > 1) { fprintf(fp, "%s", _SL_); - fprintf(fp, " RX errors: length crc frame fifo overrun%s%s", - s->rx_nohandler ? " nohandler" : "", _SL_); - fprintf(fp, " "); - print_num(fp, 8, s->rx_length_errors); - print_num(fp, 7, s->rx_crc_errors); - print_num(fp, 7, s->rx_frame_errors); - print_num(fp, 7, s->rx_fifo_errors); - print_num(fp, 7, s->rx_over_errors); + fprintf(fp, " RX errors:%*s %*s %*s %*s %*s %*s %*s%s", + cols[0] - 10, "", cols[1], "length", + cols[2], "crc", cols[3], "frame", + cols[4], "fifo", cols[5], "overrun", + cols[6], s->rx_nohandler ? "nohandler" : "", + _SL_); + fprintf(fp, "%*s", cols[0] + 5, ""); + print_num(fp, cols[1], s->rx_length_errors); + print_num(fp, cols[2], s->rx_crc_errors); + print_num(fp, cols[3], s->rx_frame_errors); + print_num(fp, cols[4], s->rx_fifo_errors); + print_num(fp, cols[5], s->rx_over_errors); if (s->rx_nohandler) - print_num(fp, 7, s->rx_nohandler); + print_num(fp, cols[6], s->rx_nohandler); } fprintf(fp, "%s", _SL_); /* TX stats */ - fprintf(fp, " TX: bytes packets errors dropped carrier collsns %s%s", - s->tx_compressed ? "compressed" : "", _SL_); + fprintf(fp, " TX: %*s %*s %*s %*s %*s %*s %*s%s", + cols[0] - 4, "bytes", cols[1], "packets", + cols[2], "errors", cols[3], "dropped", + cols[4], "carrier", cols[5], "collsns", + cols[6], s->tx_compressed ? "compressed" : "", _SL_); fprintf(fp, " "); - print_num(fp, 10, s->tx_bytes); - print_num(fp, 8, s->tx_packets); - print_num(fp, 7, s->tx_errors); - print_num(fp, 7, s->tx_dropped); - print_num(fp, 7, s->tx_carrier_errors); - print_num(fp, 7, s->collisions); + print_num(fp, cols[0], s->tx_bytes); + print_num(fp, cols[1], s->tx_packets); + print_num(fp, cols[2], s->tx_errors); + print_num(fp, cols[3], s->tx_dropped); + print_num(fp, cols[4], s->tx_carrier_errors); + print_num(fp, cols[5], s->collisions); if (s->tx_compressed) - print_num(fp, 7, s->tx_compressed); + print_num(fp, cols[6], s->tx_compressed); /* TX error stats */ if (show_stats > 1) { fprintf(fp, "%s", _SL_); - fprintf(fp, " TX errors: aborted fifo window heartbeat"); - if (carrier_changes) - fprintf(fp, " transns"); - fprintf(fp, "%s", _SL_); - - fprintf(fp, " "); - print_num(fp, 8, s->tx_aborted_errors); - print_num(fp, 7, s->tx_fifo_errors); - print_num(fp, 7, s->tx_window_errors); - print_num(fp, 7, s->tx_heartbeat_errors); + fprintf(fp, " TX errors:%*s %*s %*s %*s %*s %*s%s", + cols[0] - 10, "", cols[1], "aborted", + cols[2], "fifo", cols[3], "window", + cols[4], "heartbt", + cols[5], carrier_changes ? "transns" : "", + _SL_); + + fprintf(fp, "%*s", cols[0] + 5, ""); + print_num(fp, cols[1], s->tx_aborted_errors); + print_num(fp, cols[2], s->tx_fifo_errors); + print_num(fp, cols[3], s->tx_window_errors); + print_num(fp, cols[4], s->tx_heartbeat_errors); if (carrier_changes) - print_num(fp, 7, + print_num(fp, cols[5], rta_getattr_u32(carrier_changes)); } }
This change makes ip -s -s output size the columns automatically. I often find myself using json output because the normal output is unreadable. Even on a laptop after 2 days of uptime byte and packet counters almost overflow their columns, let alone a busy server. For max readability switch to right align. Before: RX: bytes packets errors dropped missed mcast 8227918473 8617683 0 0 0 0 RX errors: length crc frame fifo overrun 0 0 0 0 0 TX: bytes packets errors dropped carrier collsns 691937917 4727223 0 0 0 0 TX errors: aborted fifo window heartbeat transns 0 0 0 0 10 After: RX: bytes packets errors dropped missed mcast 8228633710 8618408 0 0 0 0 RX errors: length crc frame fifo overrun 0 0 0 0 0 TX: bytes packets errors dropped carrier collsns 692006303 4727740 0 0 0 0 TX errors: aborted fifo window heartbt transns 0 0 0 0 10 More importantly, with large values before: RX: bytes packets errors dropped overrun mcast 126570234447969 15016149200 0 0 0 0 RX errors: length crc frame fifo missed 0 0 0 0 0 TX: bytes packets errors dropped carrier collsns 126570234447969 15016149200 0 0 0 0 TX errors: aborted fifo window heartbeat transns 0 0 0 0 10 Note that in this case we have full shift by a column, e.g. the value under "dropped" is actually for "errors" etc. After: RX: bytes packets errors dropped missed mcast 126570234447969 15016149200 0 0 0 0 RX errors: length crc frame fifo overrun 0 0 0 0 0 TX: bytes packets errors dropped carrier collsns 126570234447969 15016149200 0 0 0 0 TX errors: aborted fifo window heartbt transns 0 0 0 0 10 Signed-off-by: Jakub Kicinski <kuba@kernel.org> --- Note - this patch does depend on the trivial nohandler print fix. ip/ipaddress.c | 146 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 40 deletions(-)