From ae0dfe14124ab5b3bb999fd909dde5437c1c7335 Mon Sep 17 00:00:00 2001 From: zhenwei pi Date: Mon, 11 Nov 2019 20:48:59 +0800 Subject: [PATCH 01/53] irqtop: implement a new utility to display kernel interrupt currently, there are usually 40/48/64/96 CPUs on a single server, and a lot of interrupts are enabled by multi-queues of a NIC. "/proc/interrupts" is not human readable any more. 'watch -d -n 1 "cat /proc/interrupts"' also can not work well. so implement irqtop to show the interrupts information, we can sort the interrupts by count(default), name and /proc/interrupts. irqtop - IRQ : 49, TOTAL : 2361705032, CPU : 8, ACTIVE CPU : 8 IRQ COUNT DESC CAL 21196 Function call interrupts LOC 13733 Local timer interrupts 154 1430 IR-PCI-MSI 32768-edge i915 127 1322 IR-PCI-MSI 327680-edge xhci_hcd RES 1224 Rescheduling interrupts 146 336 IR-PCI-MSI 520192-edge enp0s31f6 IWI 135 IRQ work interrupts 147 48 IR-PCI-MSI 31981569-edge nvme0q2 151 42 IR-PCI-MSI 31981573-edge nvme0q6 TLB 8 TLB shootdowns 150 7 IR-PCI-MSI 31981572-edge nvme0q5 152 5 IR-PCI-MSI 31981574-edge nvme0q7 156 4 IR-PCI-MSI 1572864-edge iwlwifi 148 3 IR-PCI-MSI 31981570-edge nvme0q3 153 2 IR-PCI-MSI 31981575-edge nvme0q8 NMI 2 Non-maskable interrupts PMI 2 Performance monitoring interrupts 0 0 IR-IO-APIC 2-edge timer 1 0 IR-IO-APIC 1-edge i8042 8 0 IR-IO-APIC 8-edge rtc0 9 0 IR-IO-APIC 9-fasteoi acpi 12 0 IR-IO-APIC 12-edge i8042 test on 4.14 & 4.19, work fine. test on bare metal & kvm virtual machine, work fine. hot-plug/hot-unplug virtual NIC during running irqtop, work fine. Signed-off-by: zhenwei pi --- Makefile | 11 ++ irqtop.c | 479 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 490 insertions(+) create mode 100644 Makefile create mode 100644 irqtop.c diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..f020d4e33 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +IRQTOP=irqtop + +all : + gcc *.c -O2 -g -o $(IRQTOP) -I. -lncurses + +install : all + @cp $(IRQTOP) /usr/bin + @chmod +s /usr/bin/$(IRQTOP) + +clean: + rm -rf $(IRQTOP) diff --git a/irqtop.c b/irqtop.c new file mode 100644 index 000000000..be96b5865 --- /dev/null +++ b/irqtop.c @@ -0,0 +1,479 @@ +/* + * irqtop.c - utility to display kernel interrupt information. + * + * zhenwei pi + * + * Copyright (C) 2019 zhenwei pi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define IRQTOP_VERSION "Version 0.1" +#define IRQTOP_AUTHOR "zhenwei pi" +#define DEF_SORT_FUNC sort_count +#define IRQ_NAME_LEN 4 +#define IRQ_DESC_LEN 64 +#define IRQ_INFO_LEN 64 +#define INTERRUPTS_FILE "/proc/interrupts" +#define MIN(x,y) ((x) > (y) ? (y) : (x)) +#define RESERVE_ROWS (1 + 1 + 1) /* summary + header + last row */ +#define print_line(fmt, ...) if (run_once) printf(fmt, __VA_ARGS__); \ + else printw(fmt, __VA_ARGS__) + +struct irq_info { + char irq[IRQ_NAME_LEN+1]; /* name of this irq */ + char desc[IRQ_DESC_LEN+1]; /* description of this irq */ + unsigned long count; /* count of this irq for all cpu(s) */ +}; + +struct irq_stat { + unsigned int nr_irq; /* number of irq vector */ + unsigned int nr_irq_info; /* number of irq info */ + struct irq_info *irq_info; /* array of irq_info */ + long nr_online_cpu; /* number of online cpu */ + long nr_active_cpu; /* number of active cpu */ + unsigned long total_irq; /* total irqs */ +}; + +static int run_once; +static unsigned short cols, rows; +static struct termios saved_tty; +static long delay = 3; +static int (*sort_func)(const struct irq_info *, const struct irq_info *); +static long smp_num_cpus; +static char *program; + +/* + * irqinfo - parse the system's interrupts + */ +static struct irq_stat *get_irqinfo() +{ + FILE *irqfile; + char *buffer, *tmp; + long bufferlen; + struct irq_stat *stat; + struct irq_info *curr; + int ret = -1; + + /* NAME + ':' + 11 bytes/cpu + IRQ_DESC_LEN */ + bufferlen = IRQ_NAME_LEN + 1 + smp_num_cpus * 11 + IRQ_DESC_LEN; + buffer = malloc(bufferlen); + if (!buffer) + goto out; + + stat = calloc(1, sizeof(*stat)); + if (!stat) + goto free_buf; + + stat->irq_info = malloc(sizeof(*stat->irq_info) * IRQ_INFO_LEN); + if (!stat->irq_info) + goto free_stat; + stat->nr_irq_info = IRQ_INFO_LEN; + + irqfile = fopen(INTERRUPTS_FILE, "r"); + if (!irqfile) { + perror("fopen " INTERRUPTS_FILE); + ret = 1; + goto free_stat; + } + + /* read header firstly */ + if (!fgets(buffer, bufferlen, irqfile)) { + fprintf(stderr, "cannot read from irqinfo\n"); + ret = 1; + goto close_file; + } + + stat->nr_online_cpu = smp_num_cpus; + tmp = buffer; + while ((tmp = strstr(tmp, "CPU")) != NULL) { + tmp += 3; /* skip this "CPU", find next */ + stat->nr_active_cpu++; + } + + /* parse each line of INTERRUPTS_FILE */ + while (fgets(buffer, bufferlen, irqfile)) { + unsigned long count; + int index, length; + char *colon; + + tmp = strchr(buffer, ':'); + if (!tmp) + continue; + + length = strlen(buffer); + if (length < IRQ_NAME_LEN + 1 || tmp - buffer > IRQ_NAME_LEN) + continue; + + curr = stat->irq_info + stat->nr_irq++; + memset(curr, 0x00, sizeof(*curr)); + memcpy(curr->irq, buffer, tmp - buffer); + + tmp += 1; + for (index = 0; (index < stat->nr_active_cpu) && + (tmp - buffer < length); index++) { + sscanf(tmp, " %10lu", &count); + curr->count += count; + stat->total_irq += count; + tmp += 11; + } + + if (tmp - buffer < length) { + /* strip all space before desc */ + while(*tmp == ' ') + tmp++; + strcpy(curr->desc, tmp); + } else { + /* no desc string at all, we have to set '\n' here */ + curr->desc[0] = '\n'; + } + + if (stat->nr_irq == stat->nr_irq_info) { + stat->nr_irq_info *= 2; + stat->irq_info = realloc(stat->irq_info, + sizeof(*stat->irq_info) * stat->nr_irq_info); + } + } + + return stat; + +close_file: + fclose(irqfile); +free_stat: + if (stat) + free(stat->irq_info); + free(stat); +free_buf: + free(buffer); +out: + return NULL; +} + +static void put_irqinfo(struct irq_stat *stat) +{ + if (stat) + free(stat->irq_info); + free(stat); +} + +static int sort_name(const struct irq_info *a, const struct irq_info *b) +{ + return (strcmp(a->irq, b->irq) > 0) ? 1 : 0; +} + +static int sort_count(const struct irq_info *a, const struct irq_info *b) +{ + return a->count < b->count; +} + +static int sort_interrupts(const struct irq_info *a, const struct irq_info *b) +{ + return 0; +} + +static void sort_result(struct irq_info *result, size_t nmemb) +{ + qsort(result, nmemb, sizeof(*result), + (int (*)(const void *, const void *))sort_func); +} + +static void term_size(int unusused __attribute__ ((__unused__))) +{ + struct winsize ws; + + if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) && ws.ws_row > 10) { + cols = ws.ws_col; + rows = ws.ws_row; + } else { + cols = 80; + rows = 24; + } + if (run_once) + rows = USHRT_MAX; +} + +static int uptime(double *uptime_secs, double *idle_secs) +{ + double up, idle; + FILE *f; + char buf[64]; + + f = fopen("/proc/uptime", "r"); + if (!f) + return errno; + + if (!fgets(buf, sizeof(buf), f)) { + fclose(f); + return errno; + } + + if (sscanf(buf, "%lf %lf", &up, &idle) < 2) { + fclose(f); + return errno; + } + + if (uptime_secs) + *uptime_secs = up; + + if (idle_secs) + *idle_secs = idle; + + fclose(f); + return 0; +} + +static void sigint_handler(int unused __attribute__ ((__unused__))) +{ + delay = 0; +} + +static void __attribute__((__noreturn__)) usage(FILE *out, char *msg) +{ + if (!msg) + fputs("msg", out); + fputs("Usage:\n", out); + fprintf(out, " %s [options]\n", program); + fputs("Options:", out); + fputs(" -d, --delay delay updates\n", out); + fputs(" -o, --once only display average irq once, then exit\n", out); + fputs(" -s, --sort specify sort criteria by character (see below)\n", out); + + fputs("\nThe following are valid sort criteria:\n", out); + fputs(" c: sort by increase count of each interrupt\n", out); + fputs(" i: sort by default interrupts from proc interrupt\n", out); + fputs(" n: sort by name\n", out); + fputs("Contact:\n", out); + fprintf(out, " %s\n", IRQTOP_AUTHOR); + + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static void * set_sort_func(char key) +{ + switch (key) { + case 'c': + return (void *) sort_count; + case 'i': + return (void *) sort_interrupts; + case 'n': + return (void *) sort_name; + default: + return (void *) DEF_SORT_FUNC; + } +} + +static void parse_input(char c) +{ + switch(c) { + case 'c': + sort_func = sort_count; + break; + case 'i': + sort_func = sort_interrupts; + break; + case 'n': + sort_func = sort_name; + break; + case 'q': + case 'Q': + delay = 0; + break; + } +} + +int main(int argc, char *argv[]) +{ + int is_tty, o; + unsigned short old_rows; + struct irq_stat *stat, *last_stat = NULL; + double uptime_secs; + int retval = EXIT_SUCCESS; + + static const struct option longopts[] = { + { "delay", required_argument, NULL, 'd' }, + { "sort", required_argument, NULL, 's' }, + { "once", no_argument, NULL, 'o' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + setlocale (LC_ALL, ""); + program = argv[0]; + sort_func = DEF_SORT_FUNC; + + while ((o = getopt_long(argc, argv, "d:os:hV", longopts, NULL)) != -1) { + switch (o) { + case 'd': + errno = 0; + delay = atol(optarg); + if (delay < 1) + usage(stderr, "delay must be positive integer\n"); + break; + case 's': + sort_func = (int (*)(const struct irq_info*, + const struct irq_info *)) set_sort_func(optarg[0]); + break; + case 'o': + run_once = 1; + delay = 0; + break; + case 'V': + printf("%s\n", IRQTOP_VERSION); + return EXIT_SUCCESS; + case 'h': + usage(stdout, NULL); + default: + usage(stderr, NULL); + } + } + + is_tty = isatty(STDIN_FILENO); + if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) + fputs("terminal setting retrieval", stdout); + + old_rows = rows; + term_size(0); + if (!run_once) { + initscr(); + resizeterm(rows, cols); + signal(SIGWINCH, term_size); + } + signal(SIGINT, sigint_handler); + + smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + uptime(&uptime_secs, NULL); + + do { + struct timeval tv; + struct irq_info *result, *curr; + size_t size; + fd_set readfds; + char c; + int i; + + stat = get_irqinfo(); + if (!stat) { + retval = EXIT_FAILURE; + break; + } + + if (!run_once && old_rows != rows) { + resizeterm(rows, cols); + old_rows = rows; + } + + move(0, 0); + + /* summary stat */ + print_line("irqtop - IRQ : %d, TOTAL : %ld, CPU : %ld, " + "ACTIVE CPU : %ld\n", stat->nr_irq, stat->total_irq, + stat->nr_online_cpu, stat->nr_active_cpu); + + /* header */ + attron(A_REVERSE); + print_line("%-80s\n", " IRQ COUNT DESC "); + attroff(A_REVERSE); + + size = sizeof(*stat->irq_info) * stat->nr_irq; + result = malloc(size); + memcpy(result, stat->irq_info, size); + if (!last_stat) { + for (i = 0; i < stat->nr_irq; i++) { + curr = result + i; + curr->count /= uptime_secs; + } + last_stat = stat; + } else { + int i, j; + + for (i = 0; i < stat->nr_irq; i++) { + struct irq_info *found = NULL; + unsigned long diff = 0; + + curr = result + i; + for (j = 0; j < last_stat->nr_irq; j++) { + struct irq_info *prev = last_stat->irq_info + j; + + if (!strcmp(curr->irq, prev->irq)) + found = prev; + } + + if (found && curr->count >= found->count) + diff = curr->count - found->count; + else + diff = curr->count; + + curr->count = diff; + } + put_irqinfo(last_stat); + + last_stat = stat; + } + + /* okay, sort and show the result */ + sort_result(result, stat->nr_irq); + for (i = 0; i < MIN(rows - RESERVE_ROWS, stat->nr_irq); i++) { + curr = result + i; + print_line("%4s %10ld %s", curr->irq, curr->count, + curr->desc); + } + free(result); + + if (run_once) { + break; + } else { + refresh(); + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + tv.tv_sec = delay; + tv.tv_usec = 0; + if (select(STDOUT_FILENO, &readfds, NULL, NULL, &tv) > 0) { + if (read(STDIN_FILENO, &c, 1) != 1) + break; + parse_input(c); + } + } + } while (delay); + + put_irqinfo(last_stat); + + if (is_tty) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); + + if (!run_once) + endwin(); + + return retval; +} From 6ce44862724b01a35998fa12b3695d9291f71b40 Mon Sep 17 00:00:00 2001 From: zhenwei pi Date: Mon, 11 Nov 2019 20:51:12 +0800 Subject: [PATCH 02/53] irqtop: init README add basic infomation in README Signed-off-by: zhenwei pi --- README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..8f7768aea --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +#irqtop +utility to display kernel interrupt information. + +#example +``` +irqtop - IRQ : 930, TOTAL : 27779096095, CPU : 96, ACTIVE CPU : 96 + IRQ COUNT DESC + PIW 849463 Posted-interrupt wakeup event + RES 243989 Rescheduling interrupts + LOC 194051 Local timer interrupts + PIN 76629 Posted-interrupt notification event + 280 902 IR-PCI-MSI 113246208-edge mlx5_ctrl_eq@pci:0000:d8:00.0 + CAL 897 Function call interrupts + 154 143 IR-PCI-MSI 30932996-edge eth0-3 + TLB 135 TLB shootdowns + 150 109 IR-PCI-MSI 30932992-edge mlx5_ctrl_eq@pci:0000:3b:00.0 + 215 108 IR-PCI-MSI 30935040-edge mlx5_ctrl_eq@pci:0000:3b:00.1 + 526 106 IR-PCI-MSI 113256448-edge mlx5_ctrl_eq@pci:0000:d8:00.5 + 846 106 IR-PCI-MSI 113276928-edge mlx5_ctrl_eq@pci:0000:d8:01.7 + 345 104 IR-PCI-MSI 113248256-edge mlx5_ctrl_eq@pci:0000:d8:00.1 + 430 102 IR-PCI-MSI 113250304-edge mlx5_ctrl_eq@pci:0000:d8:00.2 + 462 102 IR-PCI-MSI 113252352-edge mlx5_ctrl_eq@pci:0000:d8:00.3 + 494 102 IR-PCI-MSI 113254400-edge mlx5_ctrl_eq@pci:0000:d8:00.4 + 558 102 IR-PCI-MSI 113258496-edge mlx5_ctrl_eq@pci:0000:d8:00.6 + 590 102 IR-PCI-MSI 113260544-edge mlx5_ctrl_eq@pci:0000:d8:00.7 + 622 102 IR-PCI-MSI 113262592-edge mlx5_ctrl_eq@pci:0000:d8:01.0 + 654 102 IR-PCI-MSI 113264640-edge mlx5_ctrl_eq@pci:0000:d8:01.1 + 686 102 IR-PCI-MSI 113266688-edge mlx5_ctrl_eq@pci:0000:d8:01.2 + 718 102 IR-PCI-MSI 113268736-edge mlx5_ctrl_eq@pci:0000:d8:01.3 + 750 102 IR-PCI-MSI 113270784-edge mlx5_ctrl_eq@pci:0000:d8:01.4 + 782 102 IR-PCI-MSI 113272832-edge mlx5_ctrl_eq@pci:0000:d8:01.5 + 814 102 IR-PCI-MSI 113274880-edge mlx5_ctrl_eq@pci:0000:d8:01.6 + 878 102 IR-PCI-MSI 113278976-edge mlx5_ctrl_eq@pci:0000:d8:02.0 + 153 93 IR-PCI-MSI 30932995-edge eth0-2 + 155 84 IR-PCI-MSI 30932997-edge eth0-4 + 152 83 IR-PCI-MSI 30932994-edge eth0-1 + 157 75 IR-PCI-MSI 30932999-edge eth0-6 + 151 72 IR-PCI-MSI 30932993-edge eth0-0 + 94 54 IR-PCI-MSI 91750449-edge nvme0q49 + 156 48 IR-PCI-MSI 30932998-edge eth0-5 + 158 34 IR-PCI-MSI 30933000-edge eth0-7 + 99 15 IR-PCI-MSI 91750454-edge nvme0q54 + NMI 15 Non-maskable interrupts + PMI 15 Performance monitoring interrupts + 95 12 IR-PCI-MSI 91750450-edge nvme0q50 + 119 11 IR-PCI-MSI 91750466-edge nvme0q66 + 9 10 IR-IO-APIC 9-fasteoi acpi + 128 3 IR-PCI-MSI 91750475-edge nvme0q75 + 123 2 IR-PCI-MSI 91750470-edge nvme0q70 + 141 2 IR-PCI-MSI 91750488-edge nvme0q88 + 281 2 IR-PCI-MSI 113246209-edge eth2_15-0 + 71 1 IR-PCI-MSI 91750426-edge nvme0q26 + 118 1 IR-PCI-MSI 91750465-edge nvme0q65 + 0 0 IR-IO-APIC 2-edge timer + 8 0 IR-IO-APIC 8-edge rtc0 + 16 0 IR-IO-APIC 16-fasteoi i801_smbus + 24 0 IR-PCI-MSI 458752-edge PCIe PME, pciehp + 25 0 IR-PCI-MSI 468992-edge PCIe PME + 27 0 IR-PCI-MSI 12058624-edge PCIe PME, pciehp + 28 0 IR-PCI-MSI 12075008-edge PCIe PME, pciehp + 30 0 IR-PCI-MSI 30408704-edge PCIe PME + 31 0 IR-PCI-MSI 30441472-edge PCIe PME + 34 0 IR-PCI-MSI 91226112-edge PCIe PME, pciehp + 35 0 IR-PCI-MSI 91242496-edge PCIe PME, pciehp + 36 0 IR-PCI-MSI 91258880-edge PCIe PME, pciehp + 37 0 IR-PCI-MSI 91275264-edge PCIe PME, pciehp + 39 0 IR-PCI-MSI 112721920-edge PCIe PME + 40 0 IR-PCI-MSI 288768-edge ahci[0000:00:11.5] + 41 0 IR-PCI-MSI 327680-edge xhci_hcd + 43 0 IR-PCI-MSI 91750400-edge nvme0q0 + 44 0 IR-PCI-MSI 376832-edge ahci[0000:00:17.0] + 46 0 IR-PCI-MSI 91750401-edge nvme0q1 +``` \ No newline at end of file From 2cd409cf71c9e0250e18da30c7f83ac843826593 Mon Sep 17 00:00:00 2001 From: zhenwei pi Date: Wed, 25 Dec 2019 17:56:07 +0800 Subject: [PATCH 03/53] irqtop: fix all warnings use -Wall for compiling flags, and fix all warnings. Signed-off-by: zhenwei pi --- Makefile | 2 +- irqtop.c | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index f020d4e33..65acd44ff 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ IRQTOP=irqtop all : - gcc *.c -O2 -g -o $(IRQTOP) -I. -lncurses + gcc *.c -O2 -g -o $(IRQTOP) -I. -lncurses -Wall install : all @cp $(IRQTOP) /usr/bin diff --git a/irqtop.c b/irqtop.c index be96b5865..debb3138a 100644 --- a/irqtop.c +++ b/irqtop.c @@ -46,10 +46,10 @@ #define IRQ_DESC_LEN 64 #define IRQ_INFO_LEN 64 #define INTERRUPTS_FILE "/proc/interrupts" -#define MIN(x,y) ((x) > (y) ? (y) : (x)) +#define MIN(x,y) ((x) > (y) ? (y) : (x)) #define RESERVE_ROWS (1 + 1 + 1) /* summary + header + last row */ #define print_line(fmt, ...) if (run_once) printf(fmt, __VA_ARGS__); \ - else printw(fmt, __VA_ARGS__) + else printw(fmt, __VA_ARGS__) struct irq_info { char irq[IRQ_NAME_LEN+1]; /* name of this irq */ @@ -59,8 +59,8 @@ struct irq_info { struct irq_stat { unsigned int nr_irq; /* number of irq vector */ - unsigned int nr_irq_info; /* number of irq info */ - struct irq_info *irq_info; /* array of irq_info */ + unsigned int nr_irq_info; /* number of irq info */ + struct irq_info *irq_info; /* array of irq_info */ long nr_online_cpu; /* number of online cpu */ long nr_active_cpu; /* number of active cpu */ unsigned long total_irq; /* total irqs */ @@ -84,7 +84,6 @@ static struct irq_stat *get_irqinfo() long bufferlen; struct irq_stat *stat; struct irq_info *curr; - int ret = -1; /* NAME + ':' + 11 bytes/cpu + IRQ_DESC_LEN */ bufferlen = IRQ_NAME_LEN + 1 + smp_num_cpus * 11 + IRQ_DESC_LEN; @@ -104,14 +103,12 @@ static struct irq_stat *get_irqinfo() irqfile = fopen(INTERRUPTS_FILE, "r"); if (!irqfile) { perror("fopen " INTERRUPTS_FILE); - ret = 1; goto free_stat; } /* read header firstly */ if (!fgets(buffer, bufferlen, irqfile)) { fprintf(stderr, "cannot read from irqinfo\n"); - ret = 1; goto close_file; } @@ -126,7 +123,6 @@ static struct irq_stat *get_irqinfo() while (fgets(buffer, bufferlen, irqfile)) { unsigned long count; int index, length; - char *colon; tmp = strchr(buffer, ':'); if (!tmp) @@ -317,7 +313,7 @@ int main(int argc, char *argv[]) int is_tty, o; unsigned short old_rows; struct irq_stat *stat, *last_stat = NULL; - double uptime_secs; + double uptime_secs = 1; int retval = EXIT_SUCCESS; static const struct option longopts[] = { @@ -373,7 +369,10 @@ int main(int argc, char *argv[]) signal(SIGINT, sigint_handler); smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - uptime(&uptime_secs, NULL); + if (uptime(&uptime_secs, NULL)) { + printf("get uptime fail\n"); + return EXIT_FAILURE; + } do { struct timeval tv; From 952690cfd29e792c462fc178305e7dcdb69a444e Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 8 Feb 2020 17:07:18 +0000 Subject: [PATCH 04/53] irctop: move source code to sys-utils/ directory And remove files that util-linux does not need from the earlier repository. Signed-off-by: Sami Kerola --- Makefile | 11 ----- README.md | 73 ---------------------------------- irqtop.c => sys-utils/irqtop.c | 0 3 files changed, 84 deletions(-) delete mode 100644 Makefile delete mode 100644 README.md rename irqtop.c => sys-utils/irqtop.c (100%) diff --git a/Makefile b/Makefile deleted file mode 100644 index 65acd44ff..000000000 --- a/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -IRQTOP=irqtop - -all : - gcc *.c -O2 -g -o $(IRQTOP) -I. -lncurses -Wall - -install : all - @cp $(IRQTOP) /usr/bin - @chmod +s /usr/bin/$(IRQTOP) - -clean: - rm -rf $(IRQTOP) diff --git a/README.md b/README.md deleted file mode 100644 index 8f7768aea..000000000 --- a/README.md +++ /dev/null @@ -1,73 +0,0 @@ -#irqtop -utility to display kernel interrupt information. - -#example -``` -irqtop - IRQ : 930, TOTAL : 27779096095, CPU : 96, ACTIVE CPU : 96 - IRQ COUNT DESC - PIW 849463 Posted-interrupt wakeup event - RES 243989 Rescheduling interrupts - LOC 194051 Local timer interrupts - PIN 76629 Posted-interrupt notification event - 280 902 IR-PCI-MSI 113246208-edge mlx5_ctrl_eq@pci:0000:d8:00.0 - CAL 897 Function call interrupts - 154 143 IR-PCI-MSI 30932996-edge eth0-3 - TLB 135 TLB shootdowns - 150 109 IR-PCI-MSI 30932992-edge mlx5_ctrl_eq@pci:0000:3b:00.0 - 215 108 IR-PCI-MSI 30935040-edge mlx5_ctrl_eq@pci:0000:3b:00.1 - 526 106 IR-PCI-MSI 113256448-edge mlx5_ctrl_eq@pci:0000:d8:00.5 - 846 106 IR-PCI-MSI 113276928-edge mlx5_ctrl_eq@pci:0000:d8:01.7 - 345 104 IR-PCI-MSI 113248256-edge mlx5_ctrl_eq@pci:0000:d8:00.1 - 430 102 IR-PCI-MSI 113250304-edge mlx5_ctrl_eq@pci:0000:d8:00.2 - 462 102 IR-PCI-MSI 113252352-edge mlx5_ctrl_eq@pci:0000:d8:00.3 - 494 102 IR-PCI-MSI 113254400-edge mlx5_ctrl_eq@pci:0000:d8:00.4 - 558 102 IR-PCI-MSI 113258496-edge mlx5_ctrl_eq@pci:0000:d8:00.6 - 590 102 IR-PCI-MSI 113260544-edge mlx5_ctrl_eq@pci:0000:d8:00.7 - 622 102 IR-PCI-MSI 113262592-edge mlx5_ctrl_eq@pci:0000:d8:01.0 - 654 102 IR-PCI-MSI 113264640-edge mlx5_ctrl_eq@pci:0000:d8:01.1 - 686 102 IR-PCI-MSI 113266688-edge mlx5_ctrl_eq@pci:0000:d8:01.2 - 718 102 IR-PCI-MSI 113268736-edge mlx5_ctrl_eq@pci:0000:d8:01.3 - 750 102 IR-PCI-MSI 113270784-edge mlx5_ctrl_eq@pci:0000:d8:01.4 - 782 102 IR-PCI-MSI 113272832-edge mlx5_ctrl_eq@pci:0000:d8:01.5 - 814 102 IR-PCI-MSI 113274880-edge mlx5_ctrl_eq@pci:0000:d8:01.6 - 878 102 IR-PCI-MSI 113278976-edge mlx5_ctrl_eq@pci:0000:d8:02.0 - 153 93 IR-PCI-MSI 30932995-edge eth0-2 - 155 84 IR-PCI-MSI 30932997-edge eth0-4 - 152 83 IR-PCI-MSI 30932994-edge eth0-1 - 157 75 IR-PCI-MSI 30932999-edge eth0-6 - 151 72 IR-PCI-MSI 30932993-edge eth0-0 - 94 54 IR-PCI-MSI 91750449-edge nvme0q49 - 156 48 IR-PCI-MSI 30932998-edge eth0-5 - 158 34 IR-PCI-MSI 30933000-edge eth0-7 - 99 15 IR-PCI-MSI 91750454-edge nvme0q54 - NMI 15 Non-maskable interrupts - PMI 15 Performance monitoring interrupts - 95 12 IR-PCI-MSI 91750450-edge nvme0q50 - 119 11 IR-PCI-MSI 91750466-edge nvme0q66 - 9 10 IR-IO-APIC 9-fasteoi acpi - 128 3 IR-PCI-MSI 91750475-edge nvme0q75 - 123 2 IR-PCI-MSI 91750470-edge nvme0q70 - 141 2 IR-PCI-MSI 91750488-edge nvme0q88 - 281 2 IR-PCI-MSI 113246209-edge eth2_15-0 - 71 1 IR-PCI-MSI 91750426-edge nvme0q26 - 118 1 IR-PCI-MSI 91750465-edge nvme0q65 - 0 0 IR-IO-APIC 2-edge timer - 8 0 IR-IO-APIC 8-edge rtc0 - 16 0 IR-IO-APIC 16-fasteoi i801_smbus - 24 0 IR-PCI-MSI 458752-edge PCIe PME, pciehp - 25 0 IR-PCI-MSI 468992-edge PCIe PME - 27 0 IR-PCI-MSI 12058624-edge PCIe PME, pciehp - 28 0 IR-PCI-MSI 12075008-edge PCIe PME, pciehp - 30 0 IR-PCI-MSI 30408704-edge PCIe PME - 31 0 IR-PCI-MSI 30441472-edge PCIe PME - 34 0 IR-PCI-MSI 91226112-edge PCIe PME, pciehp - 35 0 IR-PCI-MSI 91242496-edge PCIe PME, pciehp - 36 0 IR-PCI-MSI 91258880-edge PCIe PME, pciehp - 37 0 IR-PCI-MSI 91275264-edge PCIe PME, pciehp - 39 0 IR-PCI-MSI 112721920-edge PCIe PME - 40 0 IR-PCI-MSI 288768-edge ahci[0000:00:11.5] - 41 0 IR-PCI-MSI 327680-edge xhci_hcd - 43 0 IR-PCI-MSI 91750400-edge nvme0q0 - 44 0 IR-PCI-MSI 376832-edge ahci[0000:00:17.0] - 46 0 IR-PCI-MSI 91750401-edge nvme0q1 -``` \ No newline at end of file diff --git a/irqtop.c b/sys-utils/irqtop.c similarity index 100% rename from irqtop.c rename to sys-utils/irqtop.c From 4f6610f5ca542ae6f017f6cc9063d874635f931a Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Fri, 3 Jan 2020 20:38:38 +0000 Subject: [PATCH 05/53] irqtop: make util-linux build-system to build the command Signed-off-by: Sami Kerola --- configure.ac | 7 +++++++ sys-utils/Makemodule.am | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/configure.ac b/configure.ac index 960e2016d..29c603976 100644 --- a/configure.ac +++ b/configure.ac @@ -1768,6 +1768,13 @@ AC_ARG_ENABLE([ipcs], UL_BUILD_INIT([ipcs]) AM_CONDITIONAL([BUILD_IPCS], [test "x$build_ipcs" = xyes]) +AC_ARG_ENABLE([irqtop], + AS_HELP_STRING([--disable-irqtop], [do not build irqtop]), + [], [UL_DEFAULT_ENABLE([irqtop], [yes])] +) +UL_BUILD_INIT([irqtop]) +AM_CONDITIONAL([BUILD_IRQTOP], [test "x$build_irqtop" = xyes]) + UL_BUILD_INIT([choom], [check]) UL_REQUIRES_LINUX([choom]) AM_CONDITIONAL([BUILD_CHOOM], [test "x$build_choom" = xyes]) diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index 07228f75f..0b87fd369 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -50,6 +50,13 @@ ipcs_SOURCES = sys-utils/ipcs.c \ ipcs_LDADD = $(LDADD) libcommon.la endif +if BUILD_IRQTOP +usrbin_exec_PROGRAMS += irqtop +irqtop_SOURCES = sys-utils/irqtop.c +irqtop_CFLAGS = $(AM_CFLAGS) $(NCURSES_CFLAGS) +irqtop_LDADD = $(NCURSES_LIBS) +endif + if BUILD_LSIPC usrbin_exec_PROGRAMS += lsipc dist_man_MANS += sys-utils/lsipc.1 From 57e3dc061bc1d0b946489929321c6251f50a42cf Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Fri, 3 Jan 2020 21:06:43 +0000 Subject: [PATCH 06/53] irqtop: use util-linux libcommon facilities Including NLS support. Signed-off-by: Sami Kerola --- .gitignore | 1 + include/pathnames.h | 4 + sys-utils/Makemodule.am | 2 +- sys-utils/irqtop.c | 201 +++++++++++++++++++++------------------- 4 files changed, 112 insertions(+), 96 deletions(-) diff --git a/.gitignore b/.gitignore index 62624191a..0cd0b9078 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,7 @@ ylwrap /ipcmk /ipcrm /ipcs +/irqtop /isosize /kill /last diff --git a/include/pathnames.h b/include/pathnames.h index 97815e5f3..3845d4c33 100644 --- a/include/pathnames.h +++ b/include/pathnames.h @@ -186,6 +186,10 @@ #define _PATH_PROC_IPC_SHMMAX "/proc/sys/kernel/shmmax" #define _PATH_PROC_IPC_SHMMNI "/proc/sys/kernel/shmmni" +/* irqtop paths */ +#define _PATH_PROC_INTERRUPTS "/proc/interrupts" +#define _PATH_PROC_UPTIME "/proc/uptime" + /* kernel command line */ #define _PATH_PROC_CMDLINE "/proc/cmdline" diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index 0b87fd369..e64d68037 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -53,8 +53,8 @@ endif if BUILD_IRQTOP usrbin_exec_PROGRAMS += irqtop irqtop_SOURCES = sys-utils/irqtop.c +irqtop_LDADD = $(LDADD) libcommon.la $(NCURSES_LIBS) irqtop_CFLAGS = $(AM_CFLAGS) $(NCURSES_CFLAGS) -irqtop_LDADD = $(NCURSES_LIBS) endif if BUILD_LSIPC diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index debb3138a..ad7d5a1ca 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -20,50 +20,66 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include +#include +#include #include #include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include +#include #include - #include #include #include +#include #include -#include -#define IRQTOP_VERSION "Version 0.1" -#define IRQTOP_AUTHOR "zhenwei pi" +#ifdef HAVE_SLCURSES_H +# include +#elif defined(HAVE_SLANG_SLCURSES_H) +# include +#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR) +# include +#elif defined(HAVE_NCURSES_H) +# include +#elif defined(HAVE_NCURSES_NCURSES_H) +# include +#endif + +#ifdef HAVE_WIDECHAR +# include +# include +#endif + +#include "c.h" +#include "closestream.h" +#include "nls.h" +#include "pathnames.h" + #define DEF_SORT_FUNC sort_count #define IRQ_NAME_LEN 4 #define IRQ_DESC_LEN 64 #define IRQ_INFO_LEN 64 -#define INTERRUPTS_FILE "/proc/interrupts" #define MIN(x,y) ((x) > (y) ? (y) : (x)) #define RESERVE_ROWS (1 + 1 + 1) /* summary + header + last row */ #define print_line(fmt, ...) if (run_once) printf(fmt, __VA_ARGS__); \ else printw(fmt, __VA_ARGS__) struct irq_info { - char irq[IRQ_NAME_LEN+1]; /* name of this irq */ - char desc[IRQ_DESC_LEN+1]; /* description of this irq */ - unsigned long count; /* count of this irq for all cpu(s) */ + char irq[IRQ_NAME_LEN + 1]; /* name of this irq */ + char desc[IRQ_DESC_LEN + 1]; /* description of this irq */ + unsigned long count; /* count of this irq for all cpu(s) */ }; struct irq_stat { - unsigned int nr_irq; /* number of irq vector */ + unsigned int nr_irq; /* number of irq vector */ unsigned int nr_irq_info; /* number of irq info */ struct irq_info *irq_info; /* array of irq_info */ - long nr_online_cpu; /* number of online cpu */ - long nr_active_cpu; /* number of active cpu */ - unsigned long total_irq; /* total irqs */ + long nr_online_cpu; /* number of online cpu */ + long nr_active_cpu; /* number of active cpu */ + unsigned long total_irq; /* total irqs */ }; static int run_once; @@ -72,12 +88,11 @@ static struct termios saved_tty; static long delay = 3; static int (*sort_func)(const struct irq_info *, const struct irq_info *); static long smp_num_cpus; -static char *program; /* * irqinfo - parse the system's interrupts */ -static struct irq_stat *get_irqinfo() +static struct irq_stat *get_irqinfo(void) { FILE *irqfile; char *buffer, *tmp; @@ -90,7 +105,7 @@ static struct irq_stat *get_irqinfo() buffer = malloc(bufferlen); if (!buffer) goto out; - + stat = calloc(1, sizeof(*stat)); if (!stat) goto free_buf; @@ -100,15 +115,15 @@ static struct irq_stat *get_irqinfo() goto free_stat; stat->nr_irq_info = IRQ_INFO_LEN; - irqfile = fopen(INTERRUPTS_FILE, "r"); + irqfile = fopen(_PATH_PROC_INTERRUPTS, "r"); if (!irqfile) { - perror("fopen " INTERRUPTS_FILE); + warn(_("cannot open %s"), _PATH_PROC_INTERRUPTS); goto free_stat; } /* read header firstly */ if (!fgets(buffer, bufferlen, irqfile)) { - fprintf(stderr, "cannot read from irqinfo\n"); + warn(_("cannot read %s"), _PATH_PROC_INTERRUPTS); goto close_file; } @@ -119,7 +134,7 @@ static struct irq_stat *get_irqinfo() stat->nr_active_cpu++; } - /* parse each line of INTERRUPTS_FILE */ + /* parse each line of _PATH_PROC_INTERRUPTS */ while (fgets(buffer, bufferlen, irqfile)) { unsigned long count; int index, length; @@ -137,8 +152,7 @@ static struct irq_stat *get_irqinfo() memcpy(curr->irq, buffer, tmp - buffer); tmp += 1; - for (index = 0; (index < stat->nr_active_cpu) && - (tmp - buffer < length); index++) { + for (index = 0; (index < stat->nr_active_cpu) && (tmp - buffer < length); index++) { sscanf(tmp, " %10lu", &count); curr->count += count; stat->total_irq += count; @@ -147,7 +161,7 @@ static struct irq_stat *get_irqinfo() if (tmp - buffer < length) { /* strip all space before desc */ - while(*tmp == ' ') + while (*tmp == ' ') tmp++; strcpy(curr->desc, tmp); } else { @@ -158,21 +172,21 @@ static struct irq_stat *get_irqinfo() if (stat->nr_irq == stat->nr_irq_info) { stat->nr_irq_info *= 2; stat->irq_info = realloc(stat->irq_info, - sizeof(*stat->irq_info) * stat->nr_irq_info); + sizeof(*stat->irq_info) * stat->nr_irq_info); } } return stat; -close_file: + close_file: fclose(irqfile); -free_stat: + free_stat: if (stat) free(stat->irq_info); free(stat); -free_buf: + free_buf: free(buffer); -out: + out: return NULL; } @@ -193,18 +207,18 @@ static int sort_count(const struct irq_info *a, const struct irq_info *b) return a->count < b->count; } -static int sort_interrupts(const struct irq_info *a, const struct irq_info *b) +static int sort_interrupts(const struct irq_info *a __attribute__((__unused__)), + const struct irq_info *b __attribute__((__unused__))) { return 0; } static void sort_result(struct irq_info *result, size_t nmemb) { - qsort(result, nmemb, sizeof(*result), - (int (*)(const void *, const void *))sort_func); + qsort(result, nmemb, sizeof(*result), (int (*)(const void *, const void *))sort_func); } -static void term_size(int unusused __attribute__ ((__unused__))) +static void term_size(int unusused __attribute__((__unused__))) { struct winsize ws; @@ -225,15 +239,15 @@ static int uptime(double *uptime_secs, double *idle_secs) FILE *f; char buf[64]; - f = fopen("/proc/uptime", "r"); + f = fopen(_PATH_PROC_UPTIME, "r"); if (!f) return errno; - + if (!fgets(buf, sizeof(buf), f)) { fclose(f); return errno; } - + if (sscanf(buf, "%lf %lf", &up, &idle) < 2) { fclose(f); return errno; @@ -249,49 +263,50 @@ static int uptime(double *uptime_secs, double *idle_secs) return 0; } -static void sigint_handler(int unused __attribute__ ((__unused__))) +static void sigint_handler(int unused __attribute__((__unused__))) { delay = 0; } -static void __attribute__((__noreturn__)) usage(FILE *out, char *msg) +static void __attribute__((__noreturn__)) usage(void) { - if (!msg) - fputs("msg", out); - fputs("Usage:\n", out); - fprintf(out, " %s [options]\n", program); - fputs("Options:", out); - fputs(" -d, --delay delay updates\n", out); - fputs(" -o, --once only display average irq once, then exit\n", out); - fputs(" -s, --sort specify sort criteria by character (see below)\n", out); + fputs(USAGE_HEADER, stdout); + printf(_(" %s [options]\n"), program_invocation_short_name); - fputs("\nThe following are valid sort criteria:\n", out); - fputs(" c: sort by increase count of each interrupt\n", out); - fputs(" i: sort by default interrupts from proc interrupt\n", out); - fputs(" n: sort by name\n", out); - fputs("Contact:\n", out); - fprintf(out, " %s\n", IRQTOP_AUTHOR); + fputs(USAGE_SEPARATOR, stdout); + puts(_("Utility to display kernel interrupt information.")); - exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); + fputs(USAGE_OPTIONS, stdout); + fputs(_(" -d, --delay delay updates\n"), stdout); + fputs(_(" -o, --once only display average irq once, then exit\n"), stdout); + fputs(_(" -s, --sort specify sort criteria by character (see below)\n"), stdout); + fputs(USAGE_SEPARATOR, stdout); + printf(USAGE_HELP_OPTIONS(21)); + fputs(_("\nThe following are valid sort criteria:\n"), stdout); + fputs(_(" c: sort by increase count of each interrupt\n"), stdout); + fputs(_(" i: sort by default interrupts from proc interrupt\n"), stdout); + fputs(_(" n: sort by name\n"), stdout); + printf(USAGE_MAN_TAIL("irqtop(1)")); + exit(EXIT_SUCCESS); } -static void * set_sort_func(char key) +static void *set_sort_func(char key) { switch (key) { case 'c': - return (void *) sort_count; + return (void *)sort_count; case 'i': - return (void *) sort_interrupts; + return (void *)sort_interrupts; case 'n': - return (void *) sort_name; + return (void *)sort_name; default: - return (void *) DEF_SORT_FUNC; + return (void *)DEF_SORT_FUNC; } } static void parse_input(char c) { - switch(c) { + switch (c) { case 'c': sort_func = sort_count; break; @@ -317,16 +332,15 @@ int main(int argc, char *argv[]) int retval = EXIT_SUCCESS; static const struct option longopts[] = { - { "delay", required_argument, NULL, 'd' }, - { "sort", required_argument, NULL, 's' }, - { "once", no_argument, NULL, 'o' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } + {"delay", required_argument, NULL, 'd'}, + {"sort", required_argument, NULL, 's'}, + {"once", no_argument, NULL, 'o'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} }; - setlocale (LC_ALL, ""); - program = argv[0]; + setlocale(LC_ALL, ""); sort_func = DEF_SORT_FUNC; while ((o = getopt_long(argc, argv, "d:os:hV", longopts, NULL)) != -1) { @@ -335,29 +349,28 @@ int main(int argc, char *argv[]) errno = 0; delay = atol(optarg); if (delay < 1) - usage(stderr, "delay must be positive integer\n"); + errx(EXIT_FAILURE, _("delay must be positive integer")); break; case 's': - sort_func = (int (*)(const struct irq_info*, - const struct irq_info *)) set_sort_func(optarg[0]); + sort_func = (int (*)(const struct irq_info *, const struct irq_info *)) + set_sort_func(optarg[0]); break; case 'o': run_once = 1; delay = 0; break; case 'V': - printf("%s\n", IRQTOP_VERSION); - return EXIT_SUCCESS; + print_version(EXIT_SUCCESS); case 'h': - usage(stdout, NULL); + usage(); default: - usage(stderr, NULL); + usage(); } } is_tty = isatty(STDIN_FILENO); if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) - fputs("terminal setting retrieval", stdout); + fputs(_("terminal setting retrieval"), stdout); old_rows = rows; term_size(0); @@ -369,10 +382,8 @@ int main(int argc, char *argv[]) signal(SIGINT, sigint_handler); smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - if (uptime(&uptime_secs, NULL)) { - printf("get uptime fail\n"); - return EXIT_FAILURE; - } + if (uptime(&uptime_secs, NULL)) + errx(EXIT_FAILURE, _("could not read uptime")); do { struct timeval tv; @@ -380,7 +391,7 @@ int main(int argc, char *argv[]) size_t size; fd_set readfds; char c; - int i; + size_t index; stat = get_irqinfo(); if (!stat) { @@ -397,8 +408,8 @@ int main(int argc, char *argv[]) /* summary stat */ print_line("irqtop - IRQ : %d, TOTAL : %ld, CPU : %ld, " - "ACTIVE CPU : %ld\n", stat->nr_irq, stat->total_irq, - stat->nr_online_cpu, stat->nr_active_cpu); + "ACTIVE CPU : %ld\n", stat->nr_irq, stat->total_irq, + stat->nr_online_cpu, stat->nr_active_cpu); /* header */ attron(A_REVERSE); @@ -409,13 +420,14 @@ int main(int argc, char *argv[]) result = malloc(size); memcpy(result, stat->irq_info, size); if (!last_stat) { - for (i = 0; i < stat->nr_irq; i++) { - curr = result + i; + + for (index = 0; index < stat->nr_irq; index++) { + curr = result + index; curr->count /= uptime_secs; } last_stat = stat; } else { - int i, j; + size_t i, j; for (i = 0; i < stat->nr_irq; i++) { struct irq_info *found = NULL; @@ -443,10 +455,9 @@ int main(int argc, char *argv[]) /* okay, sort and show the result */ sort_result(result, stat->nr_irq); - for (i = 0; i < MIN(rows - RESERVE_ROWS, stat->nr_irq); i++) { - curr = result + i; - print_line("%4s %10ld %s", curr->irq, curr->count, - curr->desc); + for (index = 0; index < MIN(rows - RESERVE_ROWS, stat->nr_irq); index++) { + curr = result + index; + print_line("%4s %10ld %s", curr->irq, curr->count, curr->desc); } free(result); From a11660d155f371affe5964d9d4d90c4aecb66e05 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 4 Jan 2020 07:33:01 +0000 Subject: [PATCH 07/53] irqtop: avoid function like pre-processor definitions And simplify how terminal size determination. Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index ad7d5a1ca..e4d157560 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -57,15 +57,13 @@ #include "closestream.h" #include "nls.h" #include "pathnames.h" +#include "ttyutils.h" #define DEF_SORT_FUNC sort_count #define IRQ_NAME_LEN 4 #define IRQ_DESC_LEN 64 #define IRQ_INFO_LEN 64 -#define MIN(x,y) ((x) > (y) ? (y) : (x)) #define RESERVE_ROWS (1 + 1 + 1) /* summary + header + last row */ -#define print_line(fmt, ...) if (run_once) printf(fmt, __VA_ARGS__); \ - else printw(fmt, __VA_ARGS__) struct irq_info { char irq[IRQ_NAME_LEN + 1]; /* name of this irq */ @@ -82,8 +80,9 @@ struct irq_stat { unsigned long total_irq; /* total irqs */ }; +static WINDOW *win; static int run_once; -static unsigned short cols, rows; +static int cols, rows; static struct termios saved_tty; static long delay = 3; static int (*sort_func)(const struct irq_info *, const struct irq_info *); @@ -220,17 +219,7 @@ static void sort_result(struct irq_info *result, size_t nmemb) static void term_size(int unusused __attribute__((__unused__))) { - struct winsize ws; - - if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) && ws.ws_row > 10) { - cols = ws.ws_col; - rows = ws.ws_row; - } else { - cols = 80; - rows = 24; - } - if (run_once) - rows = USHRT_MAX; + get_terminal_dimension(&cols, &rows); } static int uptime(double *uptime_secs, double *idle_secs) @@ -323,6 +312,26 @@ static void parse_input(char c) } } +static inline size_t choose_smaller(size_t a, size_t b) +{ + if (a < b) + return a; + return b; +} + +static inline void print_line(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (run_once) + vprintf(fmt, args); + else { + vw_printw(win, fmt, args); + } + va_end(args); +} + int main(int argc, char *argv[]) { int is_tty, o; @@ -373,9 +382,10 @@ int main(int argc, char *argv[]) fputs(_("terminal setting retrieval"), stdout); old_rows = rows; - term_size(0); + get_terminal_dimension(&cols, &rows); if (!run_once) { - initscr(); + win = initscr(); + get_terminal_dimension(&cols, &rows); resizeterm(rows, cols); signal(SIGWINCH, term_size); } @@ -455,7 +465,7 @@ int main(int argc, char *argv[]) /* okay, sort and show the result */ sort_result(result, stat->nr_irq); - for (index = 0; index < MIN(rows - RESERVE_ROWS, stat->nr_irq); index++) { + for (index = 0; index < choose_smaller(rows - RESERVE_ROWS, stat->nr_irq); index++) { curr = result + index; print_line("%4s %10ld %s", curr->irq, curr->count, curr->desc); } From 9aea73b3ba94dbf9311aea67ce3715d4e15bad4c Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 4 Jan 2020 07:40:49 +0000 Subject: [PATCH 08/53] irqtop: use memory allocation that check errors Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index e4d157560..79a292d10 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -58,6 +58,7 @@ #include "nls.h" #include "pathnames.h" #include "ttyutils.h" +#include "xalloc.h" #define DEF_SORT_FUNC sort_count #define IRQ_NAME_LEN 4 @@ -101,17 +102,10 @@ static struct irq_stat *get_irqinfo(void) /* NAME + ':' + 11 bytes/cpu + IRQ_DESC_LEN */ bufferlen = IRQ_NAME_LEN + 1 + smp_num_cpus * 11 + IRQ_DESC_LEN; - buffer = malloc(bufferlen); - if (!buffer) - goto out; + buffer = xmalloc(bufferlen); + stat = xcalloc(1, sizeof(*stat)); - stat = calloc(1, sizeof(*stat)); - if (!stat) - goto free_buf; - - stat->irq_info = malloc(sizeof(*stat->irq_info) * IRQ_INFO_LEN); - if (!stat->irq_info) - goto free_stat; + stat->irq_info = xmalloc(sizeof(*stat->irq_info) * IRQ_INFO_LEN); stat->nr_irq_info = IRQ_INFO_LEN; irqfile = fopen(_PATH_PROC_INTERRUPTS, "r"); @@ -147,7 +141,7 @@ static struct irq_stat *get_irqinfo(void) continue; curr = stat->irq_info + stat->nr_irq++; - memset(curr, 0x00, sizeof(*curr)); + memset(curr, 0, sizeof(*curr)); memcpy(curr->irq, buffer, tmp - buffer); tmp += 1; @@ -170,8 +164,8 @@ static struct irq_stat *get_irqinfo(void) if (stat->nr_irq == stat->nr_irq_info) { stat->nr_irq_info *= 2; - stat->irq_info = realloc(stat->irq_info, - sizeof(*stat->irq_info) * stat->nr_irq_info); + stat->irq_info = xrealloc(stat->irq_info, + sizeof(*stat->irq_info) * stat->nr_irq_info); } } @@ -180,12 +174,9 @@ static struct irq_stat *get_irqinfo(void) close_file: fclose(irqfile); free_stat: - if (stat) - free(stat->irq_info); + free(stat->irq_info); free(stat); - free_buf: free(buffer); - out: return NULL; } @@ -427,7 +418,7 @@ int main(int argc, char *argv[]) attroff(A_REVERSE); size = sizeof(*stat->irq_info) * stat->nr_irq; - result = malloc(size); + result = xmalloc(size); memcpy(result, stat->irq_info, size); if (!last_stat) { From 465e279a2654b75ac0054c63f7dacf2c711d409e Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 4 Jan 2020 07:45:43 +0000 Subject: [PATCH 09/53] irqtop: use lib/monotonic.c to determine uptime This should be functionally the same, and has the advantage of avoiding duplicated code. Signed-off-by: Sami Kerola --- sys-utils/Makemodule.am | 5 +++-- sys-utils/irqtop.c | 38 ++++---------------------------------- 2 files changed, 7 insertions(+), 36 deletions(-) diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index e64d68037..8a0cd9c90 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -52,8 +52,9 @@ endif if BUILD_IRQTOP usrbin_exec_PROGRAMS += irqtop -irqtop_SOURCES = sys-utils/irqtop.c -irqtop_LDADD = $(LDADD) libcommon.la $(NCURSES_LIBS) +irqtop_SOURCES = sys-utils/irqtop.c \ + lib/monotonic.c +irqtop_LDADD = $(LDADD) libcommon.la $(NCURSES_LIBS) $(REALTIME_LIBS) irqtop_CFLAGS = $(AM_CFLAGS) $(NCURSES_CFLAGS) endif diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 79a292d10..6932ad5cf 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -55,6 +55,7 @@ #include "c.h" #include "closestream.h" +#include "monotonic.h" #include "nls.h" #include "pathnames.h" #include "ttyutils.h" @@ -213,36 +214,6 @@ static void term_size(int unusused __attribute__((__unused__))) get_terminal_dimension(&cols, &rows); } -static int uptime(double *uptime_secs, double *idle_secs) -{ - double up, idle; - FILE *f; - char buf[64]; - - f = fopen(_PATH_PROC_UPTIME, "r"); - if (!f) - return errno; - - if (!fgets(buf, sizeof(buf), f)) { - fclose(f); - return errno; - } - - if (sscanf(buf, "%lf %lf", &up, &idle) < 2) { - fclose(f); - return errno; - } - - if (uptime_secs) - *uptime_secs = up; - - if (idle_secs) - *idle_secs = idle; - - fclose(f); - return 0; -} - static void sigint_handler(int unused __attribute__((__unused__))) { delay = 0; @@ -328,7 +299,7 @@ int main(int argc, char *argv[]) int is_tty, o; unsigned short old_rows; struct irq_stat *stat, *last_stat = NULL; - double uptime_secs = 1; + struct timeval uptime_tv; int retval = EXIT_SUCCESS; static const struct option longopts[] = { @@ -383,8 +354,7 @@ int main(int argc, char *argv[]) signal(SIGINT, sigint_handler); smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - if (uptime(&uptime_secs, NULL)) - errx(EXIT_FAILURE, _("could not read uptime")); + gettime_monotonic(&uptime_tv); do { struct timeval tv; @@ -424,7 +394,7 @@ int main(int argc, char *argv[]) for (index = 0; index < stat->nr_irq; index++) { curr = result + index; - curr->count /= uptime_secs; + curr->count /= uptime_tv.tv_sec; } last_stat = stat; } else { From a937ed0fd42185ee32d5cfea5474cbdbb6631125 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 4 Jan 2020 08:08:17 +0000 Subject: [PATCH 10/53] irqtop: use runtime control structure This is pretty standard util-linux way to avoid global variables, and pass runtime configuration around. Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 109 ++++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 50 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 6932ad5cf..66cba9d40 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -82,18 +82,22 @@ struct irq_stat { unsigned long total_irq; /* total irqs */ }; -static WINDOW *win; -static int run_once; -static int cols, rows; -static struct termios saved_tty; -static long delay = 3; -static int (*sort_func)(const struct irq_info *, const struct irq_info *); -static long smp_num_cpus; +struct irqtop_ctl { + WINDOW *win; + int cols; + int rows; + long delay; + int (*sort_func)(const struct irq_info *, const struct irq_info *); + long smp_num_cpus; + unsigned int + run_once:1; +}; +static struct irqtop_ctl *gctl; /* * irqinfo - parse the system's interrupts */ -static struct irq_stat *get_irqinfo(void) +static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) { FILE *irqfile; char *buffer, *tmp; @@ -102,7 +106,7 @@ static struct irq_stat *get_irqinfo(void) struct irq_info *curr; /* NAME + ':' + 11 bytes/cpu + IRQ_DESC_LEN */ - bufferlen = IRQ_NAME_LEN + 1 + smp_num_cpus * 11 + IRQ_DESC_LEN; + bufferlen = IRQ_NAME_LEN + 1 + ctl->smp_num_cpus * 11 + IRQ_DESC_LEN; buffer = xmalloc(bufferlen); stat = xcalloc(1, sizeof(*stat)); @@ -121,7 +125,7 @@ static struct irq_stat *get_irqinfo(void) goto close_file; } - stat->nr_online_cpu = smp_num_cpus; + stat->nr_online_cpu = ctl->smp_num_cpus; tmp = buffer; while ((tmp = strstr(tmp, "CPU")) != NULL) { tmp += 3; /* skip this "CPU", find next */ @@ -204,19 +208,19 @@ static int sort_interrupts(const struct irq_info *a __attribute__((__unused__)), return 0; } -static void sort_result(struct irq_info *result, size_t nmemb) +static void sort_result(struct irqtop_ctl *ctl, struct irq_info *result, size_t nmemb) { - qsort(result, nmemb, sizeof(*result), (int (*)(const void *, const void *))sort_func); + qsort(result, nmemb, sizeof(*result), (int (*)(const void *, const void *))ctl->sort_func); } static void term_size(int unusused __attribute__((__unused__))) { - get_terminal_dimension(&cols, &rows); + get_terminal_dimension(&gctl->cols, &gctl->rows); } static void sigint_handler(int unused __attribute__((__unused__))) { - delay = 0; + gctl->delay = 0; } static void __attribute__((__noreturn__)) usage(void) @@ -255,21 +259,21 @@ static void *set_sort_func(char key) } } -static void parse_input(char c) +static void parse_input(struct irqtop_ctl *ctl, char c) { switch (c) { case 'c': - sort_func = sort_count; + ctl->sort_func = sort_count; break; case 'i': - sort_func = sort_interrupts; + ctl->sort_func = sort_interrupts; break; case 'n': - sort_func = sort_name; + ctl->sort_func = sort_name; break; case 'q': case 'Q': - delay = 0; + ctl->delay = 0; break; } } @@ -281,15 +285,15 @@ static inline size_t choose_smaller(size_t a, size_t b) return b; } -static inline void print_line(const char *fmt, ...) +static inline void print_line(struct irqtop_ctl *ctl, const char *fmt, ...) { va_list args; va_start(args, fmt); - if (run_once) + if (ctl->run_once) vprintf(fmt, args); else { - vw_printw(win, fmt, args); + vw_printw(ctl->win, fmt, args); } va_end(args); } @@ -301,6 +305,8 @@ int main(int argc, char *argv[]) struct irq_stat *stat, *last_stat = NULL; struct timeval uptime_tv; int retval = EXIT_SUCCESS; + struct termios saved_tty; + struct irqtop_ctl ctl = { .delay = 3 }; static const struct option longopts[] = { {"delay", required_argument, NULL, 'd'}, @@ -311,24 +317,27 @@ int main(int argc, char *argv[]) {NULL, 0, NULL, 0} }; + /* FIXME; use signalfd */ + gctl = &ctl; + setlocale(LC_ALL, ""); - sort_func = DEF_SORT_FUNC; + ctl.sort_func = DEF_SORT_FUNC; while ((o = getopt_long(argc, argv, "d:os:hV", longopts, NULL)) != -1) { switch (o) { case 'd': errno = 0; - delay = atol(optarg); - if (delay < 1) + ctl.delay = atol(optarg); + if (ctl.delay < 1) errx(EXIT_FAILURE, _("delay must be positive integer")); break; case 's': - sort_func = (int (*)(const struct irq_info *, const struct irq_info *)) - set_sort_func(optarg[0]); + ctl.sort_func = (int (*)(const struct irq_info *, const struct irq_info *)) + set_sort_func(optarg[0]); break; case 'o': - run_once = 1; - delay = 0; + ctl.run_once = 1; + ctl.delay = 0; break; case 'V': print_version(EXIT_SUCCESS); @@ -343,17 +352,17 @@ int main(int argc, char *argv[]) if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) fputs(_("terminal setting retrieval"), stdout); - old_rows = rows; - get_terminal_dimension(&cols, &rows); - if (!run_once) { - win = initscr(); - get_terminal_dimension(&cols, &rows); - resizeterm(rows, cols); + old_rows = ctl.rows; + get_terminal_dimension(&ctl.cols, &ctl.rows); + if (!ctl.run_once) { + ctl.win = initscr(); + get_terminal_dimension(&ctl.cols, &ctl.rows); + resizeterm(ctl.rows, ctl.cols); signal(SIGWINCH, term_size); } signal(SIGINT, sigint_handler); - smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + ctl.smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); gettime_monotonic(&uptime_tv); do { @@ -364,27 +373,27 @@ int main(int argc, char *argv[]) char c; size_t index; - stat = get_irqinfo(); + stat = get_irqinfo(&ctl); if (!stat) { retval = EXIT_FAILURE; break; } - if (!run_once && old_rows != rows) { - resizeterm(rows, cols); - old_rows = rows; + if (!ctl.run_once && old_rows != ctl.rows) { + resizeterm(ctl.rows, ctl.cols); + old_rows = ctl.rows; } move(0, 0); /* summary stat */ - print_line("irqtop - IRQ : %d, TOTAL : %ld, CPU : %ld, " + print_line(&ctl, "irqtop - IRQ : %d, TOTAL : %ld, CPU : %ld, " "ACTIVE CPU : %ld\n", stat->nr_irq, stat->total_irq, stat->nr_online_cpu, stat->nr_active_cpu); /* header */ attron(A_REVERSE); - print_line("%-80s\n", " IRQ COUNT DESC "); + print_line(&ctl, "%-80s\n", " IRQ COUNT DESC "); attroff(A_REVERSE); size = sizeof(*stat->irq_info) * stat->nr_irq; @@ -425,35 +434,35 @@ int main(int argc, char *argv[]) } /* okay, sort and show the result */ - sort_result(result, stat->nr_irq); - for (index = 0; index < choose_smaller(rows - RESERVE_ROWS, stat->nr_irq); index++) { + sort_result(&ctl, result, stat->nr_irq); + for (index = 0; index < choose_smaller(ctl.rows - RESERVE_ROWS, stat->nr_irq); index++) { curr = result + index; - print_line("%4s %10ld %s", curr->irq, curr->count, curr->desc); + print_line(&ctl, "%4s %10ld %s", curr->irq, curr->count, curr->desc); } free(result); - if (run_once) { + if (ctl.run_once) { break; } else { refresh(); FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); - tv.tv_sec = delay; + tv.tv_sec = ctl.delay; tv.tv_usec = 0; if (select(STDOUT_FILENO, &readfds, NULL, NULL, &tv) > 0) { if (read(STDIN_FILENO, &c, 1) != 1) break; - parse_input(c); + parse_input(&ctl, c); } } - } while (delay); + } while (ctl.delay); put_irqinfo(last_stat); if (is_tty) tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); - if (!run_once) + if (!ctl.run_once) endwin(); return retval; From 4e55ffbddece069810a53ad792abda78247bb1d1 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 4 Jan 2020 08:32:42 +0000 Subject: [PATCH 11/53] irqtop: change the update delay to use struct timeval The struct timeval is more expressive, and it allows use of fractional and subsecond intervals. Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 66cba9d40..b4f0cc111 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -58,6 +58,7 @@ #include "monotonic.h" #include "nls.h" #include "pathnames.h" +#include "strutils.h" #include "ttyutils.h" #include "xalloc.h" @@ -86,10 +87,11 @@ struct irqtop_ctl { WINDOW *win; int cols; int rows; - long delay; + struct timeval delay; int (*sort_func)(const struct irq_info *, const struct irq_info *); long smp_num_cpus; unsigned int + request_exit:1, run_once:1; }; static struct irqtop_ctl *gctl; @@ -220,7 +222,7 @@ static void term_size(int unusused __attribute__((__unused__))) static void sigint_handler(int unused __attribute__((__unused__))) { - gctl->delay = 0; + gctl->request_exit = 1; } static void __attribute__((__noreturn__)) usage(void) @@ -273,7 +275,7 @@ static void parse_input(struct irqtop_ctl *ctl, char c) break; case 'q': case 'Q': - ctl->delay = 0; + ctl->request_exit = 1; break; } } @@ -306,7 +308,7 @@ int main(int argc, char *argv[]) struct timeval uptime_tv; int retval = EXIT_SUCCESS; struct termios saved_tty; - struct irqtop_ctl ctl = { .delay = 3 }; + struct irqtop_ctl ctl = { .delay.tv_sec = 3 }; static const struct option longopts[] = { {"delay", required_argument, NULL, 'd'}, @@ -326,10 +328,8 @@ int main(int argc, char *argv[]) while ((o = getopt_long(argc, argv, "d:os:hV", longopts, NULL)) != -1) { switch (o) { case 'd': - errno = 0; - ctl.delay = atol(optarg); - if (ctl.delay < 1) - errx(EXIT_FAILURE, _("delay must be positive integer")); + strtotimeval_or_err(optarg, &ctl.delay, + _("failed to parse delay argument")); break; case 's': ctl.sort_func = (int (*)(const struct irq_info *, const struct irq_info *)) @@ -337,7 +337,7 @@ int main(int argc, char *argv[]) break; case 'o': ctl.run_once = 1; - ctl.delay = 0; + ctl.request_exit = 1; break; case 'V': print_version(EXIT_SUCCESS); @@ -366,7 +366,6 @@ int main(int argc, char *argv[]) gettime_monotonic(&uptime_tv); do { - struct timeval tv; struct irq_info *result, *curr; size_t size; fd_set readfds; @@ -444,18 +443,19 @@ int main(int argc, char *argv[]) if (ctl.run_once) { break; } else { + /* copy timeval, select will overwrite the value */ + struct timeval tv = ctl.delay; + refresh(); FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); - tv.tv_sec = ctl.delay; - tv.tv_usec = 0; if (select(STDOUT_FILENO, &readfds, NULL, NULL, &tv) > 0) { if (read(STDIN_FILENO, &c, 1) != 1) break; parse_input(&ctl, c); } } - } while (ctl.delay); + } while (!ctl.request_exit); put_irqinfo(last_stat); From bc90c16a789b01b71c1bd02aba5da2183fa36d1b Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 4 Jan 2020 09:35:19 +0000 Subject: [PATCH 12/53] irqtop: fix open file descriptor leak With very short delay one can see irqfile file descriptors pile up rather quickly causing program to stop with too many open files error. This commit fixes that, and removes couple other memory leaks. With these the command should hopefully run fine for days if not even months. Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index b4f0cc111..c9f8faf9c 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -175,7 +175,8 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) sizeof(*stat->irq_info) * stat->nr_irq_info); } } - + fclose(irqfile); + free(buffer); return stat; close_file: @@ -462,8 +463,9 @@ int main(int argc, char *argv[]) if (is_tty) tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); - if (!ctl.run_once) + if (ctl.win) { + delwin(ctl.win); endwin(); - + } return retval; } From 29135b232057534903ded03c9ae5d821187bbcaf Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sun, 5 Jan 2020 09:00:28 +0000 Subject: [PATCH 13/53] irqtop: move screen update to a separate function The main() was too long. Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 234 +++++++++++++++++++++++---------------------- 1 file changed, 122 insertions(+), 112 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index c9f8faf9c..bb43c28af 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -87,9 +87,12 @@ struct irqtop_ctl { WINDOW *win; int cols; int rows; + int old_rows; struct timeval delay; + struct timeval uptime_tv; int (*sort_func)(const struct irq_info *, const struct irq_info *); long smp_num_cpus; + struct irq_stat *last_stat; unsigned int request_exit:1, run_once:1; @@ -188,7 +191,7 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) return NULL; } -static void put_irqinfo(struct irq_stat *stat) +static void free_irqinfo(struct irq_stat *stat) { if (stat) free(stat->irq_info); @@ -301,16 +304,85 @@ static inline void print_line(struct irqtop_ctl *ctl, const char *fmt, ...) va_end(args); } -int main(int argc, char *argv[]) +static int update_screen(struct irqtop_ctl *ctl) { - int is_tty, o; - unsigned short old_rows; - struct irq_stat *stat, *last_stat = NULL; - struct timeval uptime_tv; - int retval = EXIT_SUCCESS; - struct termios saved_tty; - struct irqtop_ctl ctl = { .delay.tv_sec = 3 }; + struct irq_info *result, *curr; + struct irq_stat *stat; + size_t size; + size_t index; + stat = get_irqinfo(ctl); + if (!stat) { + ctl->request_exit = 1; + return 1; + } + + if (!ctl->run_once && ctl->old_rows != ctl->rows) { + resizeterm(ctl->rows, ctl->cols); + ctl->old_rows = ctl->rows; + } + + move(0, 0); + + /* summary stat */ + print_line(ctl, "irqtop - IRQ : %d, TOTAL : %ld, CPU : %ld, " + "ACTIVE CPU : %ld\n", stat->nr_irq, stat->total_irq, + stat->nr_online_cpu, stat->nr_active_cpu); + + /* header */ + attron(A_REVERSE); + print_line(ctl, "%-80s\n", " IRQ COUNT DESC "); + attroff(A_REVERSE); + + size = sizeof(*stat->irq_info) * stat->nr_irq; + result = xmalloc(size); + memcpy(result, stat->irq_info, size); + if (!ctl->last_stat) { + for (index = 0; index < stat->nr_irq; index++) { + curr = result + index; + curr->count /= ctl->uptime_tv.tv_sec; + } + ctl->last_stat = stat; + } else { + size_t i, j; + + for (i = 0; i < stat->nr_irq; i++) { + struct irq_info *found = NULL; + unsigned long diff = 0; + + curr = result + i; + for (j = 0; j < ctl->last_stat->nr_irq; j++) { + struct irq_info *prev = ctl->last_stat->irq_info + j; + + if (!strcmp(curr->irq, prev->irq)) + found = prev; + } + + if (found && curr->count >= found->count) + diff = curr->count - found->count; + else + diff = curr->count; + + curr->count = diff; + } + free_irqinfo(ctl->last_stat); + + ctl->last_stat = stat; + } + + /* okay, sort and show the result */ + sort_result(ctl, result, stat->nr_irq); + for (index = 0; index < choose_smaller(ctl->rows - RESERVE_ROWS, stat->nr_irq); index++) { + curr = result + index; + print_line(ctl, "%4s %10ld %s", curr->irq, curr->count, curr->desc); + } + free(result); + + return 0; +} + +static void parse_args(struct irqtop_ctl *ctl, int argc, char **argv) +{ static const struct option longopts[] = { {"delay", required_argument, NULL, 'd'}, {"sort", required_argument, NULL, 's'}, @@ -319,6 +391,38 @@ int main(int argc, char *argv[]) {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} }; + int o; + + while ((o = getopt_long(argc, argv, "d:os:hV", longopts, NULL)) != -1) { + switch (o) { + case 'd': + strtotimeval_or_err(optarg, &ctl->delay, + _("failed to parse delay argument")); + break; + case 's': + ctl->sort_func = (int (*)(const struct irq_info *, const struct irq_info *)) + set_sort_func(optarg[0]); + break; + case 'o': + ctl->run_once = 1; + ctl->request_exit = 1; + break; + case 'V': + print_version(EXIT_SUCCESS); + case 'h': + usage(); + default: + errtryhelp(EXIT_FAILURE); + } + } +} + +int main(int argc, char **argv) +{ + int is_tty; + int retval = EXIT_SUCCESS; + struct termios saved_tty; + struct irqtop_ctl ctl = { .delay.tv_sec = 3 }; /* FIXME; use signalfd */ gctl = &ctl; @@ -326,34 +430,13 @@ int main(int argc, char *argv[]) setlocale(LC_ALL, ""); ctl.sort_func = DEF_SORT_FUNC; - while ((o = getopt_long(argc, argv, "d:os:hV", longopts, NULL)) != -1) { - switch (o) { - case 'd': - strtotimeval_or_err(optarg, &ctl.delay, - _("failed to parse delay argument")); - break; - case 's': - ctl.sort_func = (int (*)(const struct irq_info *, const struct irq_info *)) - set_sort_func(optarg[0]); - break; - case 'o': - ctl.run_once = 1; - ctl.request_exit = 1; - break; - case 'V': - print_version(EXIT_SUCCESS); - case 'h': - usage(); - default: - usage(); - } - } + parse_args(&ctl, argc, argv); is_tty = isatty(STDIN_FILENO); if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) fputs(_("terminal setting retrieval"), stdout); - old_rows = ctl.rows; + ctl.old_rows = ctl.rows; get_terminal_dimension(&ctl.cols, &ctl.rows); if (!ctl.run_once) { ctl.win = initscr(); @@ -364,101 +447,28 @@ int main(int argc, char *argv[]) signal(SIGINT, sigint_handler); ctl.smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - gettime_monotonic(&uptime_tv); + gettime_monotonic(&ctl.uptime_tv); do { - struct irq_info *result, *curr; - size_t size; - fd_set readfds; - char c; - size_t index; - - stat = get_irqinfo(&ctl); - if (!stat) { - retval = EXIT_FAILURE; - break; - } - - if (!ctl.run_once && old_rows != ctl.rows) { - resizeterm(ctl.rows, ctl.cols); - old_rows = ctl.rows; - } - - move(0, 0); - - /* summary stat */ - print_line(&ctl, "irqtop - IRQ : %d, TOTAL : %ld, CPU : %ld, " - "ACTIVE CPU : %ld\n", stat->nr_irq, stat->total_irq, - stat->nr_online_cpu, stat->nr_active_cpu); - - /* header */ - attron(A_REVERSE); - print_line(&ctl, "%-80s\n", " IRQ COUNT DESC "); - attroff(A_REVERSE); - - size = sizeof(*stat->irq_info) * stat->nr_irq; - result = xmalloc(size); - memcpy(result, stat->irq_info, size); - if (!last_stat) { - - for (index = 0; index < stat->nr_irq; index++) { - curr = result + index; - curr->count /= uptime_tv.tv_sec; - } - last_stat = stat; - } else { - size_t i, j; - - for (i = 0; i < stat->nr_irq; i++) { - struct irq_info *found = NULL; - unsigned long diff = 0; - - curr = result + i; - for (j = 0; j < last_stat->nr_irq; j++) { - struct irq_info *prev = last_stat->irq_info + j; - - if (!strcmp(curr->irq, prev->irq)) - found = prev; - } - - if (found && curr->count >= found->count) - diff = curr->count - found->count; - else - diff = curr->count; - - curr->count = diff; - } - put_irqinfo(last_stat); - - last_stat = stat; - } - - /* okay, sort and show the result */ - sort_result(&ctl, result, stat->nr_irq); - for (index = 0; index < choose_smaller(ctl.rows - RESERVE_ROWS, stat->nr_irq); index++) { - curr = result + index; - print_line(&ctl, "%4s %10ld %s", curr->irq, curr->count, curr->desc); - } - free(result); - - if (ctl.run_once) { - break; - } else { + retval |= update_screen(&ctl); + if (!ctl.run_once) { /* copy timeval, select will overwrite the value */ struct timeval tv = ctl.delay; + char c; + fd_set readfds; refresh(); FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); if (select(STDOUT_FILENO, &readfds, NULL, NULL, &tv) > 0) { if (read(STDIN_FILENO, &c, 1) != 1) - break; + return 1; parse_input(&ctl, c); } } } while (!ctl.request_exit); - put_irqinfo(last_stat); + free_irqinfo(ctl.last_stat); if (is_tty) tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); From fa8c5cd50359784b4c21739996b5a3895b9e48c0 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sun, 5 Jan 2020 09:27:19 +0000 Subject: [PATCH 14/53] irqtop: use epoll event loop The event loop takes care of the standard screen updates, signals, and user inputs. Two nice things using even loop like this are; 1) no need for any global variables and 2) effect of user commands is immediate. Signed-off-by: Sami Kerola --- configure.ac | 1 + sys-utils/irqtop.c | 132 ++++++++++++++++++++++++++++++++------------- 2 files changed, 95 insertions(+), 38 deletions(-) diff --git a/configure.ac b/configure.ac index 29c603976..76c23993e 100644 --- a/configure.ac +++ b/configure.ac @@ -1773,6 +1773,7 @@ AC_ARG_ENABLE([irqtop], [], [UL_DEFAULT_ENABLE([irqtop], [yes])] ) UL_BUILD_INIT([irqtop]) +UL_REQUIRES_LINUX([irqtop]) AM_CONDITIONAL([BUILD_IRQTOP], [test "x$build_irqtop" = xyes]) UL_BUILD_INIT([choom], [check]) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index bb43c28af..f3aea22f9 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -29,9 +29,12 @@ #include #include #include +#include #include #include +#include #include +#include #include #include #include @@ -67,6 +70,7 @@ #define IRQ_DESC_LEN 64 #define IRQ_INFO_LEN 64 #define RESERVE_ROWS (1 + 1 + 1) /* summary + header + last row */ +#define MAX_EVENTS 3 struct irq_info { char irq[IRQ_NAME_LEN + 1]; /* name of this irq */ @@ -88,7 +92,7 @@ struct irqtop_ctl { int cols; int rows; int old_rows; - struct timeval delay; + struct itimerspec timer; struct timeval uptime_tv; int (*sort_func)(const struct irq_info *, const struct irq_info *); long smp_num_cpus; @@ -97,7 +101,6 @@ struct irqtop_ctl { request_exit:1, run_once:1; }; -static struct irqtop_ctl *gctl; /* * irqinfo - parse the system's interrupts @@ -219,16 +222,6 @@ static void sort_result(struct irqtop_ctl *ctl, struct irq_info *result, size_t qsort(result, nmemb, sizeof(*result), (int (*)(const void *, const void *))ctl->sort_func); } -static void term_size(int unusused __attribute__((__unused__))) -{ - get_terminal_dimension(&gctl->cols, &gctl->rows); -} - -static void sigint_handler(int unused __attribute__((__unused__))) -{ - gctl->request_exit = 1; -} - static void __attribute__((__noreturn__)) usage(void) { fputs(USAGE_HEADER, stdout); @@ -381,6 +374,79 @@ static int update_screen(struct irqtop_ctl *ctl) return 0; } +static int event_loop(struct irqtop_ctl *ctl) +{ + int efd, sfd, tfd; + sigset_t sigmask; + struct signalfd_siginfo siginfo; + struct epoll_event ev, events[MAX_EVENTS]; + long int nr; + uint64_t unused; + int retval = 0; + + efd = epoll_create1(0); + + if ((tfd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) + err(EXIT_FAILURE, _("cannot not create timerfd")); + if (timerfd_settime(tfd, 0, &ctl->timer, NULL) != 0) + err(EXIT_FAILURE, _("cannot set timerfd")); + ev.events = EPOLLIN; + ev.data.fd = tfd; + if (epoll_ctl(efd, EPOLL_CTL_ADD, tfd, &ev) != 0) + err(EXIT_FAILURE, _("epoll_ctl failed")); + + if (sigfillset(&sigmask) != 0) + err(EXIT_FAILURE, _("sigfillset failed")); + if (sigprocmask(SIG_BLOCK, &sigmask, NULL) != 0) + err(EXIT_FAILURE, _("sigprocmask failed")); + if ((sfd = signalfd(-1, &sigmask, 0)) < 0) + err(EXIT_FAILURE, _("cannot not create signalfd")); + ev.events = EPOLLIN; + ev.data.fd = sfd; + if (epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &ev) != 0) + err(EXIT_FAILURE, _("epoll_ctl failed")); + + ev.events = EPOLLIN; + ev.data.fd = STDIN_FILENO; + if (epoll_ctl(efd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) != 0) + err(EXIT_FAILURE, _("epoll_ctl failed")); + + retval |= update_screen(ctl); + refresh(); + + while (!ctl->request_exit) { + const ssize_t nr_events = epoll_wait(efd, events, MAX_EVENTS, -1); + + for (nr = 0; nr < nr_events; nr++) { + if (events[nr].data.fd == tfd) { + if (read(tfd, &unused, sizeof(unused)) < 0) + warn(_("read failed")); + } else if (events[nr].data.fd == sfd) { + if (read(sfd, &siginfo, sizeof(siginfo)) < 0) { + warn(_("read failed")); + continue; + } + if (siginfo.ssi_signo == SIGWINCH) + get_terminal_dimension(&ctl->cols, &ctl->rows); + else { + ctl->request_exit = 1; + break; + } + } else if (events[nr].data.fd == STDIN_FILENO) { + char c; + + if (read(STDIN_FILENO, &c, 1) != 1) + warn(_("read failed")); + parse_input(ctl, c); + } else + abort(); + retval |= update_screen(ctl); + refresh(); + } + } + return retval; +} + static void parse_args(struct irqtop_ctl *ctl, int argc, char **argv) { static const struct option longopts[] = { @@ -396,8 +462,14 @@ static void parse_args(struct irqtop_ctl *ctl, int argc, char **argv) while ((o = getopt_long(argc, argv, "d:os:hV", longopts, NULL)) != -1) { switch (o) { case 'd': - strtotimeval_or_err(optarg, &ctl->delay, - _("failed to parse delay argument")); + { + struct timeval delay; + + strtotimeval_or_err(optarg, &delay, + _("failed to parse delay argument")); + TIMEVAL_TO_TIMESPEC(&delay, &ctl->timer.it_interval); + ctl->timer.it_value = ctl->timer.it_interval; + } break; case 's': ctl->sort_func = (int (*)(const struct irq_info *, const struct irq_info *)) @@ -422,10 +494,11 @@ int main(int argc, char **argv) int is_tty; int retval = EXIT_SUCCESS; struct termios saved_tty; - struct irqtop_ctl ctl = { .delay.tv_sec = 3 }; - /* FIXME; use signalfd */ - gctl = &ctl; + struct irqtop_ctl ctl = { + .timer.it_interval = {3, 0}, + .timer.it_value = {3, 0} + }; setlocale(LC_ALL, ""); ctl.sort_func = DEF_SORT_FUNC; @@ -442,31 +515,14 @@ int main(int argc, char **argv) ctl.win = initscr(); get_terminal_dimension(&ctl.cols, &ctl.rows); resizeterm(ctl.rows, ctl.cols); - signal(SIGWINCH, term_size); } - signal(SIGINT, sigint_handler); - ctl.smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); gettime_monotonic(&ctl.uptime_tv); - do { - retval |= update_screen(&ctl); - if (!ctl.run_once) { - /* copy timeval, select will overwrite the value */ - struct timeval tv = ctl.delay; - char c; - fd_set readfds; - - refresh(); - FD_ZERO(&readfds); - FD_SET(STDIN_FILENO, &readfds); - if (select(STDOUT_FILENO, &readfds, NULL, NULL, &tv) > 0) { - if (read(STDIN_FILENO, &c, 1) != 1) - return 1; - parse_input(&ctl, c); - } - } - } while (!ctl.request_exit); + if (ctl.run_once) + retval = update_screen(&ctl); + else + event_loop(&ctl); free_irqinfo(ctl.last_stat); From a163d853c6d657f039100d891e2081c008afcb61 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sun, 5 Jan 2020 10:40:12 +0000 Subject: [PATCH 15/53] irqtop: include hostname and timestamp to output header Having a when and from where information is useful in situations when output is examined later and possibly elsewhere. Typical example would be a screenshot or output copy-paste of a terminal to a ticketing system. Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index f3aea22f9..61ff2cd6a 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -62,6 +62,7 @@ #include "nls.h" #include "pathnames.h" #include "strutils.h" +#include "timeutils.h" #include "ttyutils.h" #include "xalloc.h" @@ -69,7 +70,7 @@ #define IRQ_NAME_LEN 4 #define IRQ_DESC_LEN 64 #define IRQ_INFO_LEN 64 -#define RESERVE_ROWS (1 + 1 + 1) /* summary + header + last row */ +#define RESERVE_ROWS (1 + 2 + 1) /* summary + header + last row */ #define MAX_EVENTS 3 struct irq_info { @@ -97,6 +98,7 @@ struct irqtop_ctl { int (*sort_func)(const struct irq_info *, const struct irq_info *); long smp_num_cpus; struct irq_stat *last_stat; + char *hostname; unsigned int request_exit:1, run_once:1; @@ -303,6 +305,8 @@ static int update_screen(struct irqtop_ctl *ctl) struct irq_stat *stat; size_t size; size_t index; + time_t now; + char timestr[64]; stat = get_irqinfo(ctl); if (!stat) { @@ -318,9 +322,13 @@ static int update_screen(struct irqtop_ctl *ctl) move(0, 0); /* summary stat */ - print_line(ctl, "irqtop - IRQ : %d, TOTAL : %ld, CPU : %ld, " - "ACTIVE CPU : %ld\n", stat->nr_irq, stat->total_irq, - stat->nr_online_cpu, stat->nr_active_cpu); + now = time(NULL); + strtime_iso(&now, ISO_TIMESTAMP_T, timestr, sizeof(timestr)); + print_line(ctl, + "irqtop - IRQ: %d TOTAL: %ld CPU: %ld ACTIVE CPU: %ld\n" + "HOST: %s TIME: %s\n", + stat->nr_irq, stat->total_irq, stat->nr_online_cpu, stat->nr_active_cpu, + ctl->hostname, timestr); /* header */ attron(A_REVERSE); @@ -518,6 +526,7 @@ int main(int argc, char **argv) } ctl.smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); gettime_monotonic(&ctl.uptime_tv); + ctl.hostname = xgethostname(); if (ctl.run_once) retval = update_screen(&ctl); @@ -525,6 +534,7 @@ int main(int argc, char **argv) event_loop(&ctl); free_irqinfo(ctl.last_stat); + free(ctl.hostname); if (is_tty) tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); From 8d8cef804595c3108543c04dcf2e908b4291fe36 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 8 Feb 2020 16:00:20 +0000 Subject: [PATCH 16/53] irqtop: use libsmartcols This makes output to be automatically be nicely adjusted to optimal width. In this commit also removes excess spaces from description strings. Signed-off-by: Sami Kerola --- sys-utils/Makemodule.am | 4 +- sys-utils/irqtop.c | 276 +++++++++++++++++++++++++++++++--------- 2 files changed, 221 insertions(+), 59 deletions(-) diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index 8a0cd9c90..03ece85ec 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -54,8 +54,8 @@ if BUILD_IRQTOP usrbin_exec_PROGRAMS += irqtop irqtop_SOURCES = sys-utils/irqtop.c \ lib/monotonic.c -irqtop_LDADD = $(LDADD) libcommon.la $(NCURSES_LIBS) $(REALTIME_LIBS) -irqtop_CFLAGS = $(AM_CFLAGS) $(NCURSES_CFLAGS) +irqtop_LDADD = $(LDADD) libcommon.la $(NCURSES_LIBS) $(REALTIME_LIBS) libsmartcols.la +irqtop_CFLAGS = $(AM_CFLAGS) $(NCURSES_CFLAGS) -I$(ul_libsmartcols_incdir) endif if BUILD_LSIPC diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 61ff2cd6a..ef3b275db 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -56,6 +56,8 @@ # include #endif +#include + #include "c.h" #include "closestream.h" #include "monotonic.h" @@ -73,6 +75,26 @@ #define RESERVE_ROWS (1 + 2 + 1) /* summary + header + last row */ #define MAX_EVENTS 3 +struct colinfo { + const char *name; + double whint; + int flags; + const char *help; + int json_type; +}; + +enum { + COL_IRQ, + COL_COUNT, + COL_DESC +}; + +static struct colinfo infos[] = { + [COL_IRQ] = {"IRQ", 0.20, SCOLS_FL_RIGHT, N_("interrupts"), SCOLS_JSON_STRING}, + [COL_COUNT] = {"COUNT", 0.20, SCOLS_FL_RIGHT, N_("total count"), SCOLS_JSON_NUMBER}, + [COL_DESC] = {"DESC", 0.60, SCOLS_FL_TRUNC, N_("description"), SCOLS_JSON_STRING}, +}; + struct irq_info { char irq[IRQ_NAME_LEN + 1]; /* name of this irq */ char desc[IRQ_DESC_LEN + 1]; /* description of this irq */ @@ -99,11 +121,136 @@ struct irqtop_ctl { long smp_num_cpus; struct irq_stat *last_stat; char *hostname; + struct libscols_table *table; + struct libscols_line *outline; + int columns[ARRAY_SIZE(infos)]; + size_t ncolumns; unsigned int + json:1, + no_headings:1, request_exit:1, run_once:1; }; +static int column_name_to_id(const char *name, size_t namesz) +{ + size_t i; + + assert(name); + + for (i = 0; i < ARRAY_SIZE(infos); i++) { + const char *cn = infos[i].name; + + if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) + return i; + } + warnx(_("unknown column: %s"), name); + return -1; +} + +static inline int get_column_id(const struct irqtop_ctl *ctl, size_t num) +{ + assert(num < ctl->ncolumns); + assert(ctl->columns[num] < (int)ARRAY_SIZE(infos)); + + return ctl->columns[num]; +} + +static inline struct colinfo *get_column_info(const struct irqtop_ctl *ctl, unsigned num) +{ + return &infos[get_column_id(ctl, num)]; +} + +static void add_scols_line(struct irqtop_ctl *ctl, struct irq_info *stat) +{ + size_t i; + struct libscols_line *line; + + line = scols_table_new_line(ctl->table, NULL); + if (!line) { + warn(_("failed to add line to output")); + return; + } + + for (i = 0; i < ctl->ncolumns; i++) { + char *str = NULL; + + switch (get_column_id(ctl, i)) { + case COL_IRQ: + xasprintf(&str, "%s", stat->irq); + break; + case COL_COUNT: + xasprintf(&str, "%ld", stat->count); + break; + case COL_DESC: + xasprintf(&str, "%s", stat->desc); + break; + default: + break; + } + + if (str && scols_line_refer_data(line, i, str) != 0) + err_oom(); + } + ctl->outline = line; +} + +static int init_scols_table(struct irqtop_ctl *ctl) +{ + size_t i; + + ctl->table = scols_new_table(); + if (!ctl->table) { + warn(_("failed to initialize output table")); + return 1; + } + scols_table_enable_json(ctl->table, ctl->json); + scols_table_enable_noheadings(ctl->table, ctl->no_headings); + + if (ctl->json) + scols_table_set_name(ctl->table, "interrupts"); + + for (i = 0; i < ctl->ncolumns; i++) { + const struct colinfo *col = get_column_info(ctl, i); + int flags = col->flags; + struct libscols_column *cl; + + cl = scols_table_new_column(ctl->table, col->name, col->whint, flags); + if (cl == NULL) { + warnx(_("failed to initialize output column")); + goto err; + } + if (ctl->json) + scols_column_set_json_type(cl, col->json_type); + } + + return 0; + err: + scols_unref_table(ctl->table); + return 1; +} + +static char *remove_repeated_spaces(char *str) +{ + char *inp = str, *outp = str; + uint8_t prev_space = 0; + + while (*inp) { + if (isspace(*inp)) { + if (!prev_space) { + *outp++ = ' '; + prev_space = 1; + } + } else { + *outp++ = *inp; + prev_space = 0; + } + ++inp; + } + *outp = '\0'; + return str; +} + /* * irqinfo - parse the system's interrupts */ @@ -158,6 +305,7 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) curr = stat->irq_info + stat->nr_irq++; memset(curr, 0, sizeof(*curr)); memcpy(curr->irq, buffer, tmp - buffer); + ltrim_whitespace((unsigned char *)curr->irq); tmp += 1; for (index = 0; (index < stat->nr_active_cpu) && (tmp - buffer < length); index++) { @@ -169,12 +317,13 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) if (tmp - buffer < length) { /* strip all space before desc */ - while (*tmp == ' ') + while (isspace(*tmp)) tmp++; + tmp = remove_repeated_spaces(tmp); strcpy(curr->desc, tmp); } else { - /* no desc string at all, we have to set '\n' here */ - curr->desc[0] = '\n'; + /* no desc string at all, we have to set '\0' here */ + curr->desc[0] = '\0'; } if (stat->nr_irq == stat->nr_irq_info) { @@ -226,6 +375,8 @@ static void sort_result(struct irqtop_ctl *ctl, struct irq_info *result, size_t static void __attribute__((__noreturn__)) usage(void) { + size_t i; + fputs(USAGE_HEADER, stdout); printf(_(" %s [options]\n"), program_invocation_short_name); @@ -233,15 +384,23 @@ static void __attribute__((__noreturn__)) usage(void) puts(_("Utility to display kernel interrupt information.")); fputs(USAGE_OPTIONS, stdout); - fputs(_(" -d, --delay delay updates\n"), stdout); - fputs(_(" -o, --once only display average irq once, then exit\n"), stdout); - fputs(_(" -s, --sort specify sort criteria by character (see below)\n"), stdout); + fputs(_(" -d, --delay delay updates\n"), stdout); + fputs(_(" -o, --once only display average irq once, then exit\n"), stdout); + fputs(_(" --json output json, implies displaying once\n"), stdout); + fputs(_(" --columns define which output columns to use (see below)\n"), stdout); + fputs(_(" -s, --sort specify sort criteria by character (see below)\n"), stdout); fputs(USAGE_SEPARATOR, stdout); printf(USAGE_HELP_OPTIONS(21)); + fputs(_("\nThe following are valid sort criteria:\n"), stdout); fputs(_(" c: sort by increase count of each interrupt\n"), stdout); fputs(_(" i: sort by default interrupts from proc interrupt\n"), stdout); fputs(_(" n: sort by name\n"), stdout); + + fputs(USAGE_COLUMNS, stdout); + for (i = 0; i < ARRAY_SIZE(infos); i++) + fprintf(stdout, " %-10s %s\n", infos[i].name, _(infos[i].help)); + printf(USAGE_MAN_TAIL("irqtop(1)")); exit(EXIT_SUCCESS); } @@ -314,71 +473,47 @@ static int update_screen(struct irqtop_ctl *ctl) return 1; } + /* summary header */ + if (!ctl->run_once) + move(0, 0); + if (!ctl->json) { + now = time(NULL); + strtime_iso(&now, ISO_TIMESTAMP_T, timestr, sizeof(timestr)); + print_line(ctl, + "irqtop - IRQ: %d TOTAL: %ld CPU: %ld ACTIVE CPU: %ld\n" + "HOST: %s TIME: %s\n", + stat->nr_irq, stat->total_irq, stat->nr_online_cpu, + stat->nr_active_cpu, ctl->hostname, timestr); + } + /* the stats */ if (!ctl->run_once && ctl->old_rows != ctl->rows) { resizeterm(ctl->rows, ctl->cols); ctl->old_rows = ctl->rows; } - - move(0, 0); - - /* summary stat */ - now = time(NULL); - strtime_iso(&now, ISO_TIMESTAMP_T, timestr, sizeof(timestr)); - print_line(ctl, - "irqtop - IRQ: %d TOTAL: %ld CPU: %ld ACTIVE CPU: %ld\n" - "HOST: %s TIME: %s\n", - stat->nr_irq, stat->total_irq, stat->nr_online_cpu, stat->nr_active_cpu, - ctl->hostname, timestr); - - /* header */ - attron(A_REVERSE); - print_line(ctl, "%-80s\n", " IRQ COUNT DESC "); - attroff(A_REVERSE); + if (init_scols_table(ctl)) + return 1; size = sizeof(*stat->irq_info) * stat->nr_irq; result = xmalloc(size); memcpy(result, stat->irq_info, size); - if (!ctl->last_stat) { - for (index = 0; index < stat->nr_irq; index++) { - curr = result + index; - curr->count /= ctl->uptime_tv.tv_sec; - } - ctl->last_stat = stat; - } else { - size_t i, j; - - for (i = 0; i < stat->nr_irq; i++) { - struct irq_info *found = NULL; - unsigned long diff = 0; - - curr = result + i; - for (j = 0; j < ctl->last_stat->nr_irq; j++) { - struct irq_info *prev = ctl->last_stat->irq_info + j; - - if (!strcmp(curr->irq, prev->irq)) - found = prev; - } - - if (found && curr->count >= found->count) - diff = curr->count - found->count; - else - diff = curr->count; - - curr->count = diff; - } - free_irqinfo(ctl->last_stat); - - ctl->last_stat = stat; - } - - /* okay, sort and show the result */ sort_result(ctl, result, stat->nr_irq); for (index = 0; index < choose_smaller(ctl->rows - RESERVE_ROWS, stat->nr_irq); index++) { curr = result + index; - print_line(ctl, "%4s %10ld %s", curr->irq, curr->count, curr->desc); + add_scols_line(ctl, curr); } free(result); + if (ctl->run_once) + scols_print_table(ctl->table); + else { + char *data; + + scols_print_table_to_string(ctl->table, &data); + print_line(ctl, "%s", data); + free(data); + } + scols_unref_table(ctl->table); + return 0; } @@ -457,10 +592,17 @@ static int event_loop(struct irqtop_ctl *ctl) static void parse_args(struct irqtop_ctl *ctl, int argc, char **argv) { + enum { + COLUMNS_OPT, + JSON_OPT, + }; + static const struct option longopts[] = { {"delay", required_argument, NULL, 'd'}, {"sort", required_argument, NULL, 's'}, {"once", no_argument, NULL, 'o'}, + {"json", no_argument, NULL, JSON_OPT}, + {"columns", optional_argument, NULL, COLUMNS_OPT}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} @@ -487,6 +629,22 @@ static void parse_args(struct irqtop_ctl *ctl, int argc, char **argv) ctl->run_once = 1; ctl->request_exit = 1; break; + case JSON_OPT: + ctl->json = 1; + ctl->run_once = 1; + ctl->request_exit = 1; + break; + case COLUMNS_OPT: + if (optarg) { + ssize_t nc = string_to_idarray(optarg, ctl->columns, + ARRAY_SIZE(ctl->columns), + column_name_to_id); + + if (nc < 0) + exit(EXIT_FAILURE); + ctl->ncolumns = nc; + } + break; case 'V': print_version(EXIT_SUCCESS); case 'h': @@ -511,6 +669,10 @@ int main(int argc, char **argv) setlocale(LC_ALL, ""); ctl.sort_func = DEF_SORT_FUNC; + ctl.columns[ctl.ncolumns++] = COL_IRQ; + ctl.columns[ctl.ncolumns++] = COL_COUNT; + ctl.columns[ctl.ncolumns++] = COL_DESC; + parse_args(&ctl, argc, argv); is_tty = isatty(STDIN_FILENO); From 050dc9f9230157022e7f745325b6759faf3b3cab Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 8 Feb 2020 16:03:07 +0000 Subject: [PATCH 17/53] irqtop: add manual page Signed-off-by: Sami Kerola --- sys-utils/Makemodule.am | 1 + sys-utils/irqtop.1 | 62 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 sys-utils/irqtop.1 diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index 03ece85ec..28e01c9ba 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -52,6 +52,7 @@ endif if BUILD_IRQTOP usrbin_exec_PROGRAMS += irqtop +dist_man_MANS += sys-utils/irqtop.1 irqtop_SOURCES = sys-utils/irqtop.c \ lib/monotonic.c irqtop_LDADD = $(LDADD) libcommon.la $(NCURSES_LIBS) $(REALTIME_LIBS) libsmartcols.la diff --git a/sys-utils/irqtop.1 b/sys-utils/irqtop.1 new file mode 100644 index 000000000..4353c23f4 --- /dev/null +++ b/sys-utils/irqtop.1 @@ -0,0 +1,62 @@ +.TH IRQTOP "1" "February 2020" "util-linux" "User Commands" +.SH NAME +irqtop \- utility to display kernel interrupt information +.SH SYNOPSIS +.B irqtop +[options] +.SH DESCRIPTION +Display kernel interrupt counter information in +.BR top (1) +style view. +.SH OPTIONS +.TP +.BR \-d ,\ \-\-delay\ \c +.I seconds +Update interrupt output every +.I seconds +intervals. +.TP +.BR \-o ,\ \-\-once +Collect interrupt information and output it. This mode does not use +ncurses, so the output can easily be redirected to a pipe, file, or such. +.TP +.BR \-s ,\ \-\-sort\ \c +.I char +Specify sort criteria by character. Pressing these characters in +interactive mode will make the ncurses output to be ordered accordingly. +.PP +.RS +.PD 0 +.TP +.B c +sort by increase count of each interrupt (the default) +.TP +.B i +sort by default interrupts from proc interrupt +.TP +.B n +sort by name +.TP +.B q Q +stop updates and exit program +.PD +.RE +.TP +.BR \-V ", " \-\-version +Display version information and exit. +.TP +.BR \-h ,\ \-\-help +Display help text and exit. +.SH AUTHORS +.MT pizhenwei@\:bytedance.com +Zhenwei Pi +.ME +.br +.MT kerolasa@\:iki.fi +Sami Kerola +.ME +.SH AVAILABILITY +The example command is part of the util-linux package and is available from +.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ +Linux Kernel Archive +.UE . From e11f242769153602f2c6c54e1a1a858ab7c95974 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 8 Feb 2020 16:28:52 +0000 Subject: [PATCH 18/53] irqtop: add bash-completion Signed-off-by: Sami Kerola --- bash-completion/Makemodule.am | 3 +++ bash-completion/irqtop | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 bash-completion/irqtop diff --git a/bash-completion/Makemodule.am b/bash-completion/Makemodule.am index a1f8bd16f..0a813f8d5 100644 --- a/bash-completion/Makemodule.am +++ b/bash-completion/Makemodule.am @@ -45,6 +45,9 @@ endif if BUILD_IPCS dist_bashcompletion_DATA += bash-completion/ipcs endif +if BUILD_IRQTOP +dist_bashcompletion_DATA += bash-completion/irqtop +endif if BUILD_ISOSIZE dist_bashcompletion_DATA += bash-completion/isosize endif diff --git a/bash-completion/irqtop b/bash-completion/irqtop new file mode 100644 index 000000000..4a914b9c5 --- /dev/null +++ b/bash-completion/irqtop @@ -0,0 +1,28 @@ +_irqtop_module() +{ + local cur prev OPTS + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-d'|'--delay') + COMPREPLY=( $(compgen -W "secs" -- $cur) ) + return 0 + ;; + '-s'|'--sort') + COMPREPLY=( $(compgen -W "c i n" -- $cur) ) + return 0 + ;; + '-h'|'--help'|'-V'|'--version') + return 0 + ;; + esac + OPTS=" --delay + --once + --sort + --help + --version" + COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) + return 0 +} +complete -F _irqtop_module irqtop From 11b20fb29d96b2a93ccbda77d470634d10352e93 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Fri, 21 Feb 2020 22:28:43 +0000 Subject: [PATCH 19/53] irqtop: display number of new interupts in-between updates The rirst output is total count followed by number of interupts since previous screen update. This is how the irqtop worked before libsmartcols was added to the command. Signed-off-by: Sami Kerola --- sys-utils/irqtop.1 | 4 ++++ sys-utils/irqtop.c | 22 +++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/sys-utils/irqtop.1 b/sys-utils/irqtop.1 index 4353c23f4..97e24f6bd 100644 --- a/sys-utils/irqtop.1 +++ b/sys-utils/irqtop.1 @@ -27,6 +27,7 @@ interactive mode will make the ncurses output to be ordered accordingly. .PP .RS .PD 0 +.\" Key commands are in alphabetical order .TP .B c sort by increase count of each interrupt (the default) @@ -39,6 +40,9 @@ sort by name .TP .B q Q stop updates and exit program +.TP +.B t +alterate displaying delta and totals count .PD .RE .TP diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index ef3b275db..bdb8c3b87 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -119,7 +119,7 @@ struct irqtop_ctl { struct timeval uptime_tv; int (*sort_func)(const struct irq_info *, const struct irq_info *); long smp_num_cpus; - struct irq_stat *last_stat; + struct irq_stat *prev_stat; char *hostname; struct libscols_table *table; struct libscols_line *outline; @@ -129,6 +129,7 @@ struct irqtop_ctl { json:1, no_headings:1, request_exit:1, + display_total:1, run_once:1; }; @@ -396,6 +397,8 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" c: sort by increase count of each interrupt\n"), stdout); fputs(_(" i: sort by default interrupts from proc interrupt\n"), stdout); fputs(_(" n: sort by name\n"), stdout); + fputs(_(" q Q: stop updates and exit program\n"), stdout); + fputs(_(" t: alterate displaying delta and totals count\n"), stdout); fputs(USAGE_COLUMNS, stdout); for (i = 0; i < ARRAY_SIZE(infos); i++) @@ -431,6 +434,9 @@ static void parse_input(struct irqtop_ctl *ctl, char c) case 'n': ctl->sort_func = sort_name; break; + case 't': + ctl->display_total = !ctl->display_total; + break; case 'q': case 'Q': ctl->request_exit = 1; @@ -496,8 +502,13 @@ static int update_screen(struct irqtop_ctl *ctl) size = sizeof(*stat->irq_info) * stat->nr_irq; result = xmalloc(size); memcpy(result, stat->irq_info, size); + if (!ctl->display_total) { + for (index = 0; ctl->prev_stat && index < stat->nr_irq; index++) + result[index].count -= ctl->prev_stat->irq_info[index].count; + } sort_result(ctl, result, stat->nr_irq); - for (index = 0; index < choose_smaller(ctl->rows - RESERVE_ROWS, stat->nr_irq); index++) { + size = choose_smaller(ctl->rows - RESERVE_ROWS, stat->nr_irq); + for (index = 0; index < size; index++) { curr = result + index; add_scols_line(ctl, curr); } @@ -514,6 +525,11 @@ static int update_screen(struct irqtop_ctl *ctl) } scols_unref_table(ctl->table); + if (!ctl->display_total) { + if (ctl->prev_stat) + free_irqinfo(ctl->prev_stat); + ctl->prev_stat = stat; + } return 0; } @@ -695,7 +711,7 @@ int main(int argc, char **argv) else event_loop(&ctl); - free_irqinfo(ctl.last_stat); + free_irqinfo(ctl.prev_stat); free(ctl.hostname); if (is_tty) From bf611f4034c26a8b34b220ead5a044dd9d379c99 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 22 Feb 2020 08:12:59 +0000 Subject: [PATCH 20/53] irqtop: hide cursor when in interactive mode Visible cursor is distraction. Secondly with visible cursor key commands can be visible adding unwanted characters to end of output. Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index bdb8c3b87..05596c41b 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -701,6 +701,7 @@ int main(int argc, char **argv) ctl.win = initscr(); get_terminal_dimension(&ctl.cols, &ctl.rows); resizeterm(ctl.rows, ctl.cols); + curs_set(0); } ctl.smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); gettime_monotonic(&ctl.uptime_tv); From daee272009bccf45ad6cc60e3092ca009a07b1c4 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 22 Feb 2020 14:48:55 +0000 Subject: [PATCH 21/53] irqtop: improve header Change total to display delta or count depending on run mode. Add space in between header and irqtop body. Print only number of active CPU in header. Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 47 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 05596c41b..6b7fb0ff1 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -72,7 +72,6 @@ #define IRQ_NAME_LEN 4 #define IRQ_DESC_LEN 64 #define IRQ_INFO_LEN 64 -#define RESERVE_ROWS (1 + 2 + 1) /* summary + header + last row */ #define MAX_EVENTS 3 struct colinfo { @@ -105,7 +104,6 @@ struct irq_stat { unsigned int nr_irq; /* number of irq vector */ unsigned int nr_irq_info; /* number of irq info */ struct irq_info *irq_info; /* array of irq_info */ - long nr_online_cpu; /* number of online cpu */ long nr_active_cpu; /* number of active cpu */ unsigned long total_irq; /* total irqs */ }; @@ -283,7 +281,6 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) goto close_file; } - stat->nr_online_cpu = ctl->smp_num_cpus; tmp = buffer; while ((tmp = strstr(tmp, "CPU")) != NULL) { tmp += 3; /* skip this "CPU", find next */ @@ -444,13 +441,6 @@ static void parse_input(struct irqtop_ctl *ctl, char c) } } -static inline size_t choose_smaller(size_t a, size_t b) -{ - if (a < b) - return a; - return b; -} - static inline void print_line(struct irqtop_ctl *ctl, const char *fmt, ...) { va_list args; @@ -473,25 +463,13 @@ static int update_screen(struct irqtop_ctl *ctl) time_t now; char timestr[64]; + /* the stats */ stat = get_irqinfo(ctl); if (!stat) { ctl->request_exit = 1; return 1; } - /* summary header */ - if (!ctl->run_once) - move(0, 0); - if (!ctl->json) { - now = time(NULL); - strtime_iso(&now, ISO_TIMESTAMP_T, timestr, sizeof(timestr)); - print_line(ctl, - "irqtop - IRQ: %d TOTAL: %ld CPU: %ld ACTIVE CPU: %ld\n" - "HOST: %s TIME: %s\n", - stat->nr_irq, stat->total_irq, stat->nr_online_cpu, - stat->nr_active_cpu, ctl->hostname, timestr); - } - /* the stats */ if (!ctl->run_once && ctl->old_rows != ctl->rows) { resizeterm(ctl->rows, ctl->cols); ctl->old_rows = ctl->rows; @@ -502,18 +480,31 @@ static int update_screen(struct irqtop_ctl *ctl) size = sizeof(*stat->irq_info) * stat->nr_irq; result = xmalloc(size); memcpy(result, stat->irq_info, size); - if (!ctl->display_total) { - for (index = 0; ctl->prev_stat && index < stat->nr_irq; index++) + if (!ctl->display_total && ctl->prev_stat) { + stat->total_irq = 0; + for (index = 0; index < stat->nr_irq; index++) { result[index].count -= ctl->prev_stat->irq_info[index].count; + stat->total_irq += result[index].count; + } } sort_result(ctl, result, stat->nr_irq); - size = choose_smaller(ctl->rows - RESERVE_ROWS, stat->nr_irq); - for (index = 0; index < size; index++) { + for (index = 0; index < stat->nr_irq; index++) { curr = result + index; add_scols_line(ctl, curr); } free(result); + /* header */ + if (!ctl->run_once) + move(0, 0); + if (!ctl->json) { + now = time(NULL); + strtime_iso(&now, ISO_TIMESTAMP_T, timestr, sizeof(timestr)); + print_line(ctl, + "irqtop - total: %ld host: %s time: %s\n\n", + stat->total_irq, ctl->hostname, timestr); + } + /* body */ if (ctl->run_once) scols_print_table(ctl->table); else { @@ -523,8 +514,8 @@ static int update_screen(struct irqtop_ctl *ctl) print_line(ctl, "%s", data); free(data); } + /* clean up */ scols_unref_table(ctl->table); - if (!ctl->display_total) { if (ctl->prev_stat) free_irqinfo(ctl->prev_stat); From 28ddd62bed4759539014a97ef01649f19d79f3a2 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 22 Feb 2020 15:19:09 +0000 Subject: [PATCH 22/53] irqtop: use name instead of desc as irq name field referal This matches with sort order n for name, and allows later usage of d for delta without needing to go around d for description. Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 6b7fb0ff1..110b3adad 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -68,11 +68,11 @@ #include "ttyutils.h" #include "xalloc.h" -#define DEF_SORT_FUNC sort_count -#define IRQ_NAME_LEN 4 -#define IRQ_DESC_LEN 64 -#define IRQ_INFO_LEN 64 -#define MAX_EVENTS 3 +#define DEF_SORT_FUNC sort_count +#define IRQ_FIELD_LEN 4 +#define IRQ_NAME_LEN 64 +#define IRQ_INFO_LEN 64 +#define MAX_EVENTS 3 struct colinfo { const char *name; @@ -85,18 +85,18 @@ struct colinfo { enum { COL_IRQ, COL_COUNT, - COL_DESC + COL_NAME }; static struct colinfo infos[] = { [COL_IRQ] = {"IRQ", 0.20, SCOLS_FL_RIGHT, N_("interrupts"), SCOLS_JSON_STRING}, [COL_COUNT] = {"COUNT", 0.20, SCOLS_FL_RIGHT, N_("total count"), SCOLS_JSON_NUMBER}, - [COL_DESC] = {"DESC", 0.60, SCOLS_FL_TRUNC, N_("description"), SCOLS_JSON_STRING}, + [COL_NAME] = {"NAME", 0.60, SCOLS_FL_TRUNC, N_("name"), SCOLS_JSON_STRING}, }; struct irq_info { - char irq[IRQ_NAME_LEN + 1]; /* name of this irq */ - char desc[IRQ_DESC_LEN + 1]; /* description of this irq */ + char irq[IRQ_FIELD_LEN + 1]; /* name of this irq */ + char name[IRQ_NAME_LEN + 1]; /* description of this irq */ unsigned long count; /* count of this irq for all cpu(s) */ }; @@ -181,8 +181,8 @@ static void add_scols_line(struct irqtop_ctl *ctl, struct irq_info *stat) case COL_COUNT: xasprintf(&str, "%ld", stat->count); break; - case COL_DESC: - xasprintf(&str, "%s", stat->desc); + case COL_NAME: + xasprintf(&str, "%s", stat->name); break; default: break; @@ -261,8 +261,8 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) struct irq_stat *stat; struct irq_info *curr; - /* NAME + ':' + 11 bytes/cpu + IRQ_DESC_LEN */ - bufferlen = IRQ_NAME_LEN + 1 + ctl->smp_num_cpus * 11 + IRQ_DESC_LEN; + /* NAME + ':' + 11 bytes/cpu + IRQ_NAME_LEN */ + bufferlen = IRQ_FIELD_LEN + 1 + ctl->smp_num_cpus * 11 + IRQ_NAME_LEN; buffer = xmalloc(bufferlen); stat = xcalloc(1, sizeof(*stat)); @@ -297,7 +297,7 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) continue; length = strlen(buffer); - if (length < IRQ_NAME_LEN + 1 || tmp - buffer > IRQ_NAME_LEN) + if (length < IRQ_FIELD_LEN + 1 || tmp - buffer > IRQ_FIELD_LEN) continue; curr = stat->irq_info + stat->nr_irq++; @@ -318,10 +318,10 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) while (isspace(*tmp)) tmp++; tmp = remove_repeated_spaces(tmp); - strcpy(curr->desc, tmp); + strcpy(curr->name, tmp); } else { - /* no desc string at all, we have to set '\0' here */ - curr->desc[0] = '\0'; + /* no irq name string, we have to set '\0' here */ + curr->name[0] = '\0'; } if (stat->nr_irq == stat->nr_irq_info) { @@ -678,7 +678,7 @@ int main(int argc, char **argv) ctl.columns[ctl.ncolumns++] = COL_IRQ; ctl.columns[ctl.ncolumns++] = COL_COUNT; - ctl.columns[ctl.ncolumns++] = COL_DESC; + ctl.columns[ctl.ncolumns++] = COL_NAME; parse_args(&ctl, argc, argv); From 68afc56bcd7eab9a91de9cea6b7bc988efe56f83 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 22 Feb 2020 16:42:56 +0000 Subject: [PATCH 23/53] irqtop: add total and delta as own columns This commit add total and delta as separate columns rather than a toggle, making output generally more informative. When using command I found that sort order key bindings should match with first letter of column headers so that they are as easy as possible to remember, and therefore enhancing user experience. Requested-by: Karel Zak Signed-off-by: Sami Kerola --- bash-completion/irqtop | 17 +++++- sys-utils/irqtop.1 | 18 +++--- sys-utils/irqtop.c | 122 +++++++++++++++++++++++------------------ 3 files changed, 95 insertions(+), 62 deletions(-) diff --git a/bash-completion/irqtop b/bash-completion/irqtop index 4a914b9c5..b6a71454d 100644 --- a/bash-completion/irqtop +++ b/bash-completion/irqtop @@ -10,7 +10,20 @@ _irqtop_module() return 0 ;; '-s'|'--sort') - COMPREPLY=( $(compgen -W "c i n" -- $cur) ) + COMPREPLY=( $(compgen -W "i t d n" -- $cur) ) + return 0 + ;; + '--columns') + local prefix realcur OUTPUT + realcur="${cur##*,}" + prefix="${cur%$realcur}" + for WORD in "IRQ TOTAL DELTA NAME"; do + if ! [[ $prefix == *"$WORD"* ]]; then + OUTPUT="$WORD ${OUTPUT:-""}" + fi + done + compopt -o nospace + COMPREPLY=( $(compgen -P "$prefix" -W "$OUTPUT" -S ',' -- $realcur) ) return 0 ;; '-h'|'--help'|'-V'|'--version') @@ -19,7 +32,9 @@ _irqtop_module() esac OPTS=" --delay --once + --json --sort + --columns --help --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) diff --git a/sys-utils/irqtop.1 b/sys-utils/irqtop.1 index 97e24f6bd..ebc67549c 100644 --- a/sys-utils/irqtop.1 +++ b/sys-utils/irqtop.1 @@ -27,22 +27,22 @@ interactive mode will make the ncurses output to be ordered accordingly. .PP .RS .PD 0 -.\" Key commands are in alphabetical order -.TP -.B c -sort by increase count of each interrupt (the default) +.\" Sort key commands are in same order as default output fields .TP .B i -sort by default interrupts from proc interrupt +sort by short irq name or number field +.TP +.B t +sort by total count of interrupts (the default) +.TP +.B d +sort by delta count of interrupts .TP .B n -sort by name +sort by long descriptive name field .TP .B q Q stop updates and exit program -.TP -.B t -alterate displaying delta and totals count .PD .RE .TP diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 110b3adad..0d9d26a51 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -68,7 +68,7 @@ #include "ttyutils.h" #include "xalloc.h" -#define DEF_SORT_FUNC sort_count +#define DEF_SORT_FUNC sort_total #define IRQ_FIELD_LEN 4 #define IRQ_NAME_LEN 64 #define IRQ_INFO_LEN 64 @@ -84,20 +84,23 @@ struct colinfo { enum { COL_IRQ, - COL_COUNT, + COL_TOTAL, + COL_DELTA, COL_NAME }; static struct colinfo infos[] = { - [COL_IRQ] = {"IRQ", 0.20, SCOLS_FL_RIGHT, N_("interrupts"), SCOLS_JSON_STRING}, - [COL_COUNT] = {"COUNT", 0.20, SCOLS_FL_RIGHT, N_("total count"), SCOLS_JSON_NUMBER}, - [COL_NAME] = {"NAME", 0.60, SCOLS_FL_TRUNC, N_("name"), SCOLS_JSON_STRING}, + [COL_IRQ] = {"IRQ", 0.10, SCOLS_FL_RIGHT, N_("interrupts"), SCOLS_JSON_STRING}, + [COL_TOTAL] = {"TOTAL", 0.10, SCOLS_FL_RIGHT, N_("total count"), SCOLS_JSON_NUMBER}, + [COL_DELTA] = {"DELTA", 0.10, SCOLS_FL_RIGHT, N_("delta count"), SCOLS_JSON_NUMBER}, + [COL_NAME] = {"NAME", 0.70, SCOLS_FL_TRUNC, N_("name"), SCOLS_JSON_STRING}, }; struct irq_info { - char irq[IRQ_FIELD_LEN + 1]; /* name of this irq */ - char name[IRQ_NAME_LEN + 1]; /* description of this irq */ - unsigned long count; /* count of this irq for all cpu(s) */ + char irq[IRQ_FIELD_LEN + 1]; /* short name of this irq */ + char name[IRQ_NAME_LEN + 1]; /* descriptive name of this irq */ + unsigned long total; /* total count since system start up */ + unsigned long delta; /* delta count since previous update */ }; struct irq_stat { @@ -106,6 +109,7 @@ struct irq_stat { struct irq_info *irq_info; /* array of irq_info */ long nr_active_cpu; /* number of active cpu */ unsigned long total_irq; /* total irqs */ + unsigned long delta_irq; /* delta irqs */ }; struct irqtop_ctl { @@ -124,10 +128,10 @@ struct irqtop_ctl { int columns[ARRAY_SIZE(infos)]; size_t ncolumns; unsigned int + specific_columns:1, json:1, no_headings:1, request_exit:1, - display_total:1, run_once:1; }; @@ -178,8 +182,11 @@ static void add_scols_line(struct irqtop_ctl *ctl, struct irq_info *stat) case COL_IRQ: xasprintf(&str, "%s", stat->irq); break; - case COL_COUNT: - xasprintf(&str, "%ld", stat->count); + case COL_TOTAL: + xasprintf(&str, "%ld", stat->total); + break; + case COL_DELTA: + xasprintf(&str, "%ld", stat->delta); break; case COL_NAME: xasprintf(&str, "%s", stat->name); @@ -308,7 +315,7 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) tmp += 1; for (index = 0; (index < stat->nr_active_cpu) && (tmp - buffer < length); index++) { sscanf(tmp, " %10lu", &count); - curr->count += count; + curr->total += count; stat->total_irq += count; tmp += 11; } @@ -351,21 +358,25 @@ static void free_irqinfo(struct irq_stat *stat) } static int sort_name(const struct irq_info *a, const struct irq_info *b) +{ + return (strcmp(a->name, b->name) > 0) ? 1 : 0; +} + +static int sort_total(const struct irq_info *a, const struct irq_info *b) +{ + return a->total < b->total; +} + +static int sort_delta(const struct irq_info *a, const struct irq_info *b) +{ + return a->delta < b->delta; +} + +static int sort_interrupts(const struct irq_info *a, const struct irq_info *b) { return (strcmp(a->irq, b->irq) > 0) ? 1 : 0; } -static int sort_count(const struct irq_info *a, const struct irq_info *b) -{ - return a->count < b->count; -} - -static int sort_interrupts(const struct irq_info *a __attribute__((__unused__)), - const struct irq_info *b __attribute__((__unused__))) -{ - return 0; -} - static void sort_result(struct irqtop_ctl *ctl, struct irq_info *result, size_t nmemb) { qsort(result, nmemb, sizeof(*result), (int (*)(const void *, const void *))ctl->sort_func); @@ -391,11 +402,11 @@ static void __attribute__((__noreturn__)) usage(void) printf(USAGE_HELP_OPTIONS(21)); fputs(_("\nThe following are valid sort criteria:\n"), stdout); - fputs(_(" c: sort by increase count of each interrupt\n"), stdout); - fputs(_(" i: sort by default interrupts from proc interrupt\n"), stdout); - fputs(_(" n: sort by name\n"), stdout); + fputs(_(" i: sort by IRQ field\n"), stdout); + fputs(_(" t: sort by TOTAL field\n"), stdout); + fputs(_(" d: sort by DELTA field\n"), stdout); + fputs(_(" n: sort by NAME field\n"), stdout); fputs(_(" q Q: stop updates and exit program\n"), stdout); - fputs(_(" t: alterate displaying delta and totals count\n"), stdout); fputs(USAGE_COLUMNS, stdout); for (i = 0; i < ARRAY_SIZE(infos); i++) @@ -408,10 +419,12 @@ static void __attribute__((__noreturn__)) usage(void) static void *set_sort_func(char key) { switch (key) { - case 'c': - return (void *)sort_count; case 'i': return (void *)sort_interrupts; + case 't': + return (void *)sort_total; + case 'd': + return (void *)sort_delta; case 'n': return (void *)sort_name; default: @@ -422,18 +435,18 @@ static void *set_sort_func(char key) static void parse_input(struct irqtop_ctl *ctl, char c) { switch (c) { - case 'c': - ctl->sort_func = sort_count; - break; case 'i': ctl->sort_func = sort_interrupts; break; + case 't': + ctl->sort_func = sort_total; + break; + case 'd': + ctl->sort_func = sort_delta; + break; case 'n': ctl->sort_func = sort_name; break; - case 't': - ctl->display_total = !ctl->display_total; - break; case 'q': case 'Q': ctl->request_exit = 1; @@ -480,11 +493,11 @@ static int update_screen(struct irqtop_ctl *ctl) size = sizeof(*stat->irq_info) * stat->nr_irq; result = xmalloc(size); memcpy(result, stat->irq_info, size); - if (!ctl->display_total && ctl->prev_stat) { - stat->total_irq = 0; + if (ctl->prev_stat) { + stat->delta_irq = 0; for (index = 0; index < stat->nr_irq; index++) { - result[index].count -= ctl->prev_stat->irq_info[index].count; - stat->total_irq += result[index].count; + result[index].delta = result[index].total - ctl->prev_stat->irq_info[index].total; + stat->delta_irq += result[index].delta; } } sort_result(ctl, result, stat->nr_irq); @@ -501,8 +514,8 @@ static int update_screen(struct irqtop_ctl *ctl) now = time(NULL); strtime_iso(&now, ISO_TIMESTAMP_T, timestr, sizeof(timestr)); print_line(ctl, - "irqtop - total: %ld host: %s time: %s\n\n", - stat->total_irq, ctl->hostname, timestr); + "irqtop - total: %ld delta: %ld host: %s time: %s\n\n", + stat->total_irq, stat->delta_irq, ctl->hostname, timestr); } /* body */ if (ctl->run_once) @@ -516,11 +529,9 @@ static int update_screen(struct irqtop_ctl *ctl) } /* clean up */ scols_unref_table(ctl->table); - if (!ctl->display_total) { - if (ctl->prev_stat) - free_irqinfo(ctl->prev_stat); - ctl->prev_stat = stat; - } + if (ctl->prev_stat) + free_irqinfo(ctl->prev_stat); + ctl->prev_stat = stat; return 0; } @@ -609,7 +620,7 @@ static void parse_args(struct irqtop_ctl *ctl, int argc, char **argv) {"sort", required_argument, NULL, 's'}, {"once", no_argument, NULL, 'o'}, {"json", no_argument, NULL, JSON_OPT}, - {"columns", optional_argument, NULL, COLUMNS_OPT}, + {"columns", required_argument, NULL, COLUMNS_OPT}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} @@ -642,13 +653,16 @@ static void parse_args(struct irqtop_ctl *ctl, int argc, char **argv) ctl->request_exit = 1; break; case COLUMNS_OPT: + ctl->specific_columns = 1; if (optarg) { ssize_t nc = string_to_idarray(optarg, ctl->columns, ARRAY_SIZE(ctl->columns), column_name_to_id); if (nc < 0) - exit(EXIT_FAILURE); + errx(EXIT_FAILURE, + _("too many or unknown --columns argument: %s"), + optarg); ctl->ncolumns = nc; } break; @@ -676,12 +690,16 @@ int main(int argc, char **argv) setlocale(LC_ALL, ""); ctl.sort_func = DEF_SORT_FUNC; - ctl.columns[ctl.ncolumns++] = COL_IRQ; - ctl.columns[ctl.ncolumns++] = COL_COUNT; - ctl.columns[ctl.ncolumns++] = COL_NAME; - parse_args(&ctl, argc, argv); + if (!ctl.specific_columns) { + ctl.columns[ctl.ncolumns++] = COL_IRQ; + ctl.columns[ctl.ncolumns++] = COL_TOTAL; + if (!ctl.run_once) + ctl.columns[ctl.ncolumns++] = COL_DELTA; + ctl.columns[ctl.ncolumns++] = COL_NAME; + } + is_tty = isatty(STDIN_FILENO); if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) fputs(_("terminal setting retrieval"), stdout); From d77e9e13ddb52b5a4bb34df5e02e1852b5786d88 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sun, 23 Feb 2020 07:39:11 +0000 Subject: [PATCH 24/53] irqtop: do not use fixed size /proc/interrupts line buffer Allocating and freeing all the time is a little bit heavier, but it is also more robust. It is impossible to know if field lenghts stay fixed. Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 0d9d26a51..d8b34e666 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -69,8 +69,6 @@ #include "xalloc.h" #define DEF_SORT_FUNC sort_total -#define IRQ_FIELD_LEN 4 -#define IRQ_NAME_LEN 64 #define IRQ_INFO_LEN 64 #define MAX_EVENTS 3 @@ -97,8 +95,8 @@ static struct colinfo infos[] = { }; struct irq_info { - char irq[IRQ_FIELD_LEN + 1]; /* short name of this irq */ - char name[IRQ_NAME_LEN + 1]; /* descriptive name of this irq */ + char *irq; /* short name of this irq */ + char *name; /* descriptive name of this irq */ unsigned long total; /* total count since system start up */ unsigned long delta; /* delta count since previous update */ }; @@ -199,6 +197,8 @@ static void add_scols_line(struct irqtop_ctl *ctl, struct irq_info *stat) err_oom(); } ctl->outline = line; + free(stat->irq); + free(stat->name); } static int init_scols_table(struct irqtop_ctl *ctl) @@ -260,17 +260,15 @@ static char *remove_repeated_spaces(char *str) /* * irqinfo - parse the system's interrupts */ -static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) +static struct irq_stat *get_irqinfo(void) { FILE *irqfile; - char *buffer, *tmp; - long bufferlen; + char *line = NULL, *tmp; + size_t len = 0; struct irq_stat *stat; struct irq_info *curr; /* NAME + ':' + 11 bytes/cpu + IRQ_NAME_LEN */ - bufferlen = IRQ_FIELD_LEN + 1 + ctl->smp_num_cpus * 11 + IRQ_NAME_LEN; - buffer = xmalloc(bufferlen); stat = xcalloc(1, sizeof(*stat)); stat->irq_info = xmalloc(sizeof(*stat->irq_info) * IRQ_INFO_LEN); @@ -283,52 +281,51 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) } /* read header firstly */ - if (!fgets(buffer, bufferlen, irqfile)) { + if (getline(&line, &len, irqfile) < 0) { warn(_("cannot read %s"), _PATH_PROC_INTERRUPTS); goto close_file; } - tmp = buffer; + tmp = line; while ((tmp = strstr(tmp, "CPU")) != NULL) { tmp += 3; /* skip this "CPU", find next */ stat->nr_active_cpu++; } /* parse each line of _PATH_PROC_INTERRUPTS */ - while (fgets(buffer, bufferlen, irqfile)) { + while (getline(&line, &len, irqfile) >= 0) { unsigned long count; int index, length; - tmp = strchr(buffer, ':'); + tmp = strchr(line, ':'); if (!tmp) continue; - length = strlen(buffer); - if (length < IRQ_FIELD_LEN + 1 || tmp - buffer > IRQ_FIELD_LEN) - continue; + length = strlen(line); curr = stat->irq_info + stat->nr_irq++; memset(curr, 0, sizeof(*curr)); - memcpy(curr->irq, buffer, tmp - buffer); + *tmp = '\0'; + curr->irq = xstrdup(line); ltrim_whitespace((unsigned char *)curr->irq); tmp += 1; - for (index = 0; (index < stat->nr_active_cpu) && (tmp - buffer < length); index++) { + for (index = 0; (index < stat->nr_active_cpu) && (tmp - line < length); index++) { sscanf(tmp, " %10lu", &count); curr->total += count; stat->total_irq += count; tmp += 11; } - if (tmp - buffer < length) { + if (tmp - line < length) { /* strip all space before desc */ while (isspace(*tmp)) tmp++; tmp = remove_repeated_spaces(tmp); - strcpy(curr->name, tmp); + curr->name = xstrdup(tmp); } else { /* no irq name string, we have to set '\0' here */ - curr->name[0] = '\0'; + curr->name = xstrdup(""); } if (stat->nr_irq == stat->nr_irq_info) { @@ -338,7 +335,7 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) } } fclose(irqfile); - free(buffer); + free(line); return stat; close_file: @@ -346,7 +343,7 @@ static struct irq_stat *get_irqinfo(struct irqtop_ctl *ctl) free_stat: free(stat->irq_info); free(stat); - free(buffer); + free(line); return NULL; } @@ -477,7 +474,7 @@ static int update_screen(struct irqtop_ctl *ctl) char timestr[64]; /* the stats */ - stat = get_irqinfo(ctl); + stat = get_irqinfo(); if (!stat) { ctl->request_exit = 1; return 1; From d7f9cdf5356bb930fe2016eb53d64b7807367209 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Mon, 24 Feb 2020 18:26:38 +0000 Subject: [PATCH 25/53] irqtop: tidy coding style and update usage() text Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 110 ++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index d8b34e666..c009049cc 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -87,11 +87,11 @@ enum { COL_NAME }; -static struct colinfo infos[] = { - [COL_IRQ] = {"IRQ", 0.10, SCOLS_FL_RIGHT, N_("interrupts"), SCOLS_JSON_STRING}, +static const struct colinfo infos[] = { + [COL_IRQ] = {"IRQ", 0.10, SCOLS_FL_RIGHT, N_("interrupts"), SCOLS_JSON_STRING}, [COL_TOTAL] = {"TOTAL", 0.10, SCOLS_FL_RIGHT, N_("total count"), SCOLS_JSON_NUMBER}, [COL_DELTA] = {"DELTA", 0.10, SCOLS_FL_RIGHT, N_("delta count"), SCOLS_JSON_NUMBER}, - [COL_NAME] = {"NAME", 0.70, SCOLS_FL_TRUNC, N_("name"), SCOLS_JSON_STRING}, + [COL_NAME] = {"NAME", 0.70, SCOLS_FL_TRUNC, N_("name"), SCOLS_JSON_STRING}, }; struct irq_info { @@ -110,6 +110,8 @@ struct irq_stat { unsigned long delta_irq; /* delta irqs */ }; +typedef int (sort_fp)(const struct irq_info *, const struct irq_info *); + struct irqtop_ctl { WINDOW *win; int cols; @@ -117,7 +119,7 @@ struct irqtop_ctl { int old_rows; struct itimerspec timer; struct timeval uptime_tv; - int (*sort_func)(const struct irq_info *, const struct irq_info *); + sort_fp *sort_func; long smp_num_cpus; struct irq_stat *prev_stat; char *hostname; @@ -133,12 +135,11 @@ struct irqtop_ctl { run_once:1; }; -static int column_name_to_id(const char *name, size_t namesz) +static int column_name_to_id(char const *const name, size_t const namesz) { size_t i; assert(name); - for (i = 0; i < ARRAY_SIZE(infos); i++) { const char *cn = infos[i].name; @@ -149,7 +150,7 @@ static int column_name_to_id(const char *name, size_t namesz) return -1; } -static inline int get_column_id(const struct irqtop_ctl *ctl, size_t num) +static inline int get_column_id(struct irqtop_ctl const *const ctl, size_t const num) { assert(num < ctl->ncolumns); assert(ctl->columns[num] < (int)ARRAY_SIZE(infos)); @@ -157,12 +158,14 @@ static inline int get_column_id(const struct irqtop_ctl *ctl, size_t num) return ctl->columns[num]; } -static inline struct colinfo *get_column_info(const struct irqtop_ctl *ctl, unsigned num) +static inline struct colinfo const *get_column_info(struct irqtop_ctl const + *const ctl, + unsigned const num) { return &infos[get_column_id(ctl, num)]; } -static void add_scols_line(struct irqtop_ctl *ctl, struct irq_info *stat) +static void add_scols_line(struct irqtop_ctl *const ctl, struct irq_info const *const stat) { size_t i; struct libscols_line *line; @@ -201,7 +204,7 @@ static void add_scols_line(struct irqtop_ctl *ctl, struct irq_info *stat) free(stat->name); } -static int init_scols_table(struct irqtop_ctl *ctl) +static int init_scols_table(struct irqtop_ctl *const ctl) { size_t i; @@ -214,10 +217,10 @@ static int init_scols_table(struct irqtop_ctl *ctl) scols_table_enable_noheadings(ctl->table, ctl->no_headings); if (ctl->json) - scols_table_set_name(ctl->table, "interrupts"); + scols_table_set_name(ctl->table, _("interrupts")); for (i = 0; i < ctl->ncolumns; i++) { - const struct colinfo *col = get_column_info(ctl, i); + struct colinfo const *const col = get_column_info(ctl, i); int flags = col->flags; struct libscols_column *cl; @@ -236,7 +239,7 @@ static int init_scols_table(struct irqtop_ctl *ctl) return 1; } -static char *remove_repeated_spaces(char *str) +static char *remove_repeated_spaces(char *const str) { char *inp = str, *outp = str; uint8_t prev_space = 0; @@ -323,10 +326,8 @@ static struct irq_stat *get_irqinfo(void) tmp++; tmp = remove_repeated_spaces(tmp); curr->name = xstrdup(tmp); - } else { - /* no irq name string, we have to set '\0' here */ + } else /* no irq name string, we have to set '\0' here */ curr->name = xstrdup(""); - } if (stat->nr_irq == stat->nr_irq_info) { stat->nr_irq_info *= 2; @@ -347,34 +348,40 @@ static struct irq_stat *get_irqinfo(void) return NULL; } -static void free_irqinfo(struct irq_stat *stat) +static void free_irqinfo(struct irq_stat *const stat) { if (stat) free(stat->irq_info); free(stat); } -static int sort_name(const struct irq_info *a, const struct irq_info *b) +static int sort_name(struct irq_info const *const a, + struct irq_info const *const b) { return (strcmp(a->name, b->name) > 0) ? 1 : 0; } -static int sort_total(const struct irq_info *a, const struct irq_info *b) +static int sort_total(struct irq_info const *const a, + struct irq_info const *const b) { return a->total < b->total; } -static int sort_delta(const struct irq_info *a, const struct irq_info *b) +static int sort_delta(struct irq_info const *const a, + struct irq_info const *const b) { return a->delta < b->delta; } -static int sort_interrupts(const struct irq_info *a, const struct irq_info *b) +static int sort_interrupts(struct irq_info const *const a, + struct irq_info const *const b) { return (strcmp(a->irq, b->irq) > 0) ? 1 : 0; } -static void sort_result(struct irqtop_ctl *ctl, struct irq_info *result, size_t nmemb) +static void sort_result(struct irqtop_ctl *const ctl, + struct irq_info *const result, + size_t const nmemb) { qsort(result, nmemb, sizeof(*result), (int (*)(const void *, const void *))ctl->sort_func); } @@ -391,45 +398,46 @@ static void __attribute__((__noreturn__)) usage(void) fputs(USAGE_OPTIONS, stdout); fputs(_(" -d, --delay delay updates\n"), stdout); - fputs(_(" -o, --once only display average irq once, then exit\n"), stdout); + fputs(_(" -o, --once only display interrupts once, then exit\n"), stdout); fputs(_(" --json output json, implies displaying once\n"), stdout); fputs(_(" --columns define which output columns to use (see below)\n"), stdout); fputs(_(" -s, --sort specify sort criteria by character (see below)\n"), stdout); fputs(USAGE_SEPARATOR, stdout); - printf(USAGE_HELP_OPTIONS(21)); + printf(USAGE_HELP_OPTIONS(22)); - fputs(_("\nThe following are valid sort criteria:\n"), stdout); - fputs(_(" i: sort by IRQ field\n"), stdout); - fputs(_(" t: sort by TOTAL field\n"), stdout); - fputs(_(" d: sort by DELTA field\n"), stdout); - fputs(_(" n: sort by NAME field\n"), stdout); - fputs(_(" q Q: stop updates and exit program\n"), stdout); + fputs(_("\nThe following interactive key commands are valid:\n"), stdout); + fputs(_(" i sort by IRQ\n"), stdout); + fputs(_(" t sort by TOTAL\n"), stdout); + fputs(_(" d sort by DELTA\n"), stdout); + fputs(_(" n sort by NAME\n"), stdout); + fputs(_(" q Q quit program\n"), stdout); fputs(USAGE_COLUMNS, stdout); for (i = 0; i < ARRAY_SIZE(infos); i++) - fprintf(stdout, " %-10s %s\n", infos[i].name, _(infos[i].help)); + fprintf(stdout, " %-5s %s\n", infos[i].name, _(infos[i].help)); printf(USAGE_MAN_TAIL("irqtop(1)")); exit(EXIT_SUCCESS); } -static void *set_sort_func(char key) + +static sort_fp *set_sort_func(char const key) { switch (key) { case 'i': - return (void *)sort_interrupts; + return sort_interrupts; case 't': - return (void *)sort_total; + return sort_total; case 'd': - return (void *)sort_delta; + return sort_delta; case 'n': - return (void *)sort_name; + return sort_name; default: - return (void *)DEF_SORT_FUNC; + return DEF_SORT_FUNC; } } -static void parse_input(struct irqtop_ctl *ctl, char c) +static void parse_input(struct irqtop_ctl *const ctl, char const c) { switch (c) { case 'i': @@ -451,20 +459,20 @@ static void parse_input(struct irqtop_ctl *ctl, char c) } } -static inline void print_line(struct irqtop_ctl *ctl, const char *fmt, ...) +static inline void print_line(struct irqtop_ctl const *const ctl, + char const *const fmt, ...) { va_list args; va_start(args, fmt); if (ctl->run_once) vprintf(fmt, args); - else { + else vw_printw(ctl->win, fmt, args); - } va_end(args); } -static int update_screen(struct irqtop_ctl *ctl) +static int update_screen(struct irqtop_ctl *const ctl) { struct irq_info *result, *curr; struct irq_stat *stat; @@ -493,7 +501,8 @@ static int update_screen(struct irqtop_ctl *ctl) if (ctl->prev_stat) { stat->delta_irq = 0; for (index = 0; index < stat->nr_irq; index++) { - result[index].delta = result[index].total - ctl->prev_stat->irq_info[index].total; + result[index].delta = result[index].total - + ctl->prev_stat->irq_info[index].total; stat->delta_irq += result[index].delta; } } @@ -510,8 +519,7 @@ static int update_screen(struct irqtop_ctl *ctl) if (!ctl->json) { now = time(NULL); strtime_iso(&now, ISO_TIMESTAMP_T, timestr, sizeof(timestr)); - print_line(ctl, - "irqtop - total: %ld delta: %ld host: %s time: %s\n\n", + print_line(ctl, _("irqtop - total: %ld delta: %ld host: %s time: %s\n\n"), stat->total_irq, stat->delta_irq, ctl->hostname, timestr); } /* body */ @@ -532,7 +540,7 @@ static int update_screen(struct irqtop_ctl *ctl) return 0; } -static int event_loop(struct irqtop_ctl *ctl) +static int event_loop(struct irqtop_ctl *const ctl) { int efd, sfd, tfd; sigset_t sigmask; @@ -605,13 +613,13 @@ static int event_loop(struct irqtop_ctl *ctl) return retval; } -static void parse_args(struct irqtop_ctl *ctl, int argc, char **argv) +static void parse_args(struct irqtop_ctl *const ctl, int const argc, + char *const *const argv) { enum { - COLUMNS_OPT, + COLUMNS_OPT = CHAR_MAX + 1, JSON_OPT, }; - static const struct option longopts[] = { {"delay", required_argument, NULL, 'd'}, {"sort", required_argument, NULL, 's'}, @@ -637,8 +645,7 @@ static void parse_args(struct irqtop_ctl *ctl, int argc, char **argv) } break; case 's': - ctl->sort_func = (int (*)(const struct irq_info *, const struct irq_info *)) - set_sort_func(optarg[0]); + ctl->sort_func = set_sort_func(optarg[0]); break; case 'o': ctl->run_once = 1; @@ -678,7 +685,6 @@ int main(int argc, char **argv) int is_tty; int retval = EXIT_SUCCESS; struct termios saved_tty; - struct irqtop_ctl ctl = { .timer.it_interval = {3, 0}, .timer.it_value = {3, 0} From ebdb0c350af204129b66f5465d9291ffaa89f513 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Mon, 24 Feb 2020 18:27:16 +0000 Subject: [PATCH 26/53] docs: remove irqtop TODO item Signed-off-by: Sami Kerola --- Documentation/TODO | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Documentation/TODO b/Documentation/TODO index 66746a649..a81eab6c5 100644 --- a/Documentation/TODO +++ b/Documentation/TODO @@ -5,12 +5,6 @@ script (lib/pty-session.c) -------------------------- - (!) add #ifdefs and optional code for non-signalfd() systems -irqtop or lsirq ---------------- - - add tool to list and monitor IRQs from /proc/interrupts - - libsmartcols is expected - - initial work is done by zhenwei pi, see https://github.com/pacepi/irqtop - col --- - use unsigned sizes for columns and lines From 6f99d9d61367ca5856073b5e5db0f191c0de7b1e Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 4 Mar 2020 14:17:27 +0100 Subject: [PATCH 27/53] iqrtop: cleanup header * add '|' to make it more readable * use time string without 'T' Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index c009049cc..a9cff2c1a 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -518,8 +518,8 @@ static int update_screen(struct irqtop_ctl *const ctl) move(0, 0); if (!ctl->json) { now = time(NULL); - strtime_iso(&now, ISO_TIMESTAMP_T, timestr, sizeof(timestr)); - print_line(ctl, _("irqtop - total: %ld delta: %ld host: %s time: %s\n\n"), + strtime_iso(&now, ISO_TIMESTAMP, timestr, sizeof(timestr)); + print_line(ctl, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), stat->total_irq, stat->delta_irq, ctl->hostname, timestr); } /* body */ From 642f6cb0cb2bc3f4f56c55673c77edddbcee1f54 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 4 Mar 2020 14:47:20 +0100 Subject: [PATCH 28/53] irqtop: simplify terminal resizing * don't care about term size and attrs when runs only once * explicitly define signals * resize immediately after SIGWINCH (to keep it nice for users) Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index a9cff2c1a..e678f9066 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -116,7 +116,6 @@ struct irqtop_ctl { WINDOW *win; int cols; int rows; - int old_rows; struct itimerspec timer; struct timeval uptime_tv; sort_fp *sort_func; @@ -488,10 +487,6 @@ static int update_screen(struct irqtop_ctl *const ctl) return 1; } - if (!ctl->run_once && ctl->old_rows != ctl->rows) { - resizeterm(ctl->rows, ctl->cols); - ctl->old_rows = ctl->rows; - } if (init_scols_table(ctl)) return 1; @@ -556,6 +551,7 @@ static int event_loop(struct irqtop_ctl *const ctl) err(EXIT_FAILURE, _("cannot not create timerfd")); if (timerfd_settime(tfd, 0, &ctl->timer, NULL) != 0) err(EXIT_FAILURE, _("cannot set timerfd")); + ev.events = EPOLLIN; ev.data.fd = tfd; if (epoll_ctl(efd, EPOLL_CTL_ADD, tfd, &ev) != 0) @@ -565,8 +561,15 @@ static int event_loop(struct irqtop_ctl *const ctl) err(EXIT_FAILURE, _("sigfillset failed")); if (sigprocmask(SIG_BLOCK, &sigmask, NULL) != 0) err(EXIT_FAILURE, _("sigprocmask failed")); - if ((sfd = signalfd(-1, &sigmask, 0)) < 0) + + sigaddset(&sigmask, SIGWINCH); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGQUIT); + + if ((sfd = signalfd(-1, &sigmask, SFD_CLOEXEC)) < 0) err(EXIT_FAILURE, _("cannot not create signalfd")); + ev.events = EPOLLIN; ev.data.fd = sfd; if (epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &ev) != 0) @@ -592,8 +595,10 @@ static int event_loop(struct irqtop_ctl *const ctl) warn(_("read failed")); continue; } - if (siginfo.ssi_signo == SIGWINCH) + if (siginfo.ssi_signo == SIGWINCH) { get_terminal_dimension(&ctl->cols, &ctl->rows); + resizeterm(ctl->rows, ctl->cols); + } else { ctl->request_exit = 1; break; @@ -682,7 +687,7 @@ static void parse_args(struct irqtop_ctl *const ctl, int const argc, int main(int argc, char **argv) { - int is_tty; + int is_tty = 0; int retval = EXIT_SUCCESS; struct termios saved_tty; struct irqtop_ctl ctl = { @@ -703,18 +708,17 @@ int main(int argc, char **argv) ctl.columns[ctl.ncolumns++] = COL_NAME; } - is_tty = isatty(STDIN_FILENO); - if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) - fputs(_("terminal setting retrieval"), stdout); - - ctl.old_rows = ctl.rows; - get_terminal_dimension(&ctl.cols, &ctl.rows); if (!ctl.run_once) { + is_tty = isatty(STDIN_FILENO); + if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) + fputs(_("terminal setting retrieval"), stdout); + ctl.win = initscr(); get_terminal_dimension(&ctl.cols, &ctl.rows); resizeterm(ctl.rows, ctl.cols); curs_set(0); } + ctl.smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); gettime_monotonic(&ctl.uptime_tv); ctl.hostname = xgethostname(); @@ -727,10 +731,9 @@ int main(int argc, char **argv) free_irqinfo(ctl.prev_stat); free(ctl.hostname); - if (is_tty) - tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); - if (ctl.win) { + if (is_tty) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); delwin(ctl.win); endwin(); } From 0d8ead9327f5a7b7983c3cc87f316f0fe1b24e48 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 4 Mar 2020 14:51:12 +0100 Subject: [PATCH 29/53] irqtop: don't print header for --once We don't use any extra header for lsblk, lsmem, ... so don't make it different. Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index e678f9066..5bdac92f6 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -508,10 +508,9 @@ static int update_screen(struct irqtop_ctl *const ctl) } free(result); - /* header */ - if (!ctl->run_once) + /* header in interactive mode */ + if (!ctl->run_once) { move(0, 0); - if (!ctl->json) { now = time(NULL); strtime_iso(&now, ISO_TIMESTAMP, timestr, sizeof(timestr)); print_line(ctl, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), From f5377ca454ebd6e7de5f4fbef41921a15069cdc3 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 4 Mar 2020 14:58:18 +0100 Subject: [PATCH 30/53] irqtop: use -J for JSON * add -J to the man page * add note about default output * be compatible with another util-linux tools and use -J Signed-off-by: Karel Zak --- sys-utils/irqtop.1 | 8 ++++++++ sys-utils/irqtop.c | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/sys-utils/irqtop.1 b/sys-utils/irqtop.1 index ebc67549c..42ca91495 100644 --- a/sys-utils/irqtop.1 +++ b/sys-utils/irqtop.1 @@ -8,6 +8,10 @@ irqtop \- utility to display kernel interrupt information Display kernel interrupt counter information in .BR top (1) style view. +.PP +The default output is subject to change. So whenever +possible, you should avoid using default outputs in your scripts. Always explicitly define expected columns +by using \fB\-\-columns\fR. .SH OPTIONS .TP .BR \-d ,\ \-\-delay\ \c @@ -16,6 +20,10 @@ Update interrupt output every .I seconds intervals. .TP +.BR \-J ,\ \-\-json +Use JSON output format. It's strongly recommended to use --columns to keep +the output stable. +.TP .BR \-o ,\ \-\-once Collect interrupt information and output it. This mode does not use ncurses, so the output can easily be redirected to a pipe, file, or such. diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 5bdac92f6..3c1253005 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -628,7 +628,7 @@ static void parse_args(struct irqtop_ctl *const ctl, int const argc, {"delay", required_argument, NULL, 'd'}, {"sort", required_argument, NULL, 's'}, {"once", no_argument, NULL, 'o'}, - {"json", no_argument, NULL, JSON_OPT}, + {"json", no_argument, NULL, 'J'}, {"columns", required_argument, NULL, COLUMNS_OPT}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, @@ -636,7 +636,7 @@ static void parse_args(struct irqtop_ctl *const ctl, int const argc, }; int o; - while ((o = getopt_long(argc, argv, "d:os:hV", longopts, NULL)) != -1) { + while ((o = getopt_long(argc, argv, "d:os:hJV", longopts, NULL)) != -1) { switch (o) { case 'd': { @@ -655,7 +655,7 @@ static void parse_args(struct irqtop_ctl *const ctl, int const argc, ctl->run_once = 1; ctl->request_exit = 1; break; - case JSON_OPT: + case 'J': ctl->json = 1; ctl->run_once = 1; ctl->request_exit = 1; From b5886f298e6f5d35565b75afc8e77f8e6f9e8b1d Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Wed, 4 Mar 2020 21:39:07 +0000 Subject: [PATCH 31/53] irqtop: trim white spaces from end of name field Excess white spaces were easiest to see in json output. $ irqtop --once --json { "interrupts": [ {"irq":"LOC", "total":7425148, "name":"Local timer interrupts "}, {"irq":"51", "total":1848384, "name":"IR-PCI-MSI 32768-edge i915 "}, {"irq":"RES", "total":1176665, "name":"Rescheduling interrupts "}, ... Signed-off-by: Sami Kerola --- sys-utils/irqtop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 3c1253005..15fa2ff85 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -324,6 +324,7 @@ static struct irq_stat *get_irqinfo(void) while (isspace(*tmp)) tmp++; tmp = remove_repeated_spaces(tmp); + rtrim_whitespace((unsigned char *)tmp); curr->name = xstrdup(tmp); } else /* no irq name string, we have to set '\0' here */ curr->name = xstrdup(""); From 487c7466388c29bc29aa4f001f8060f2db0ac33f Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 11:40:49 +0100 Subject: [PATCH 32/53] irqtop: cleanup command line options * use -o for output columns (like we have in another tools) * support -o +COLUMN Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 66 ++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 15fa2ff85..8ae1744e6 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -124,10 +124,12 @@ struct irqtop_ctl { char *hostname; struct libscols_table *table; struct libscols_line *outline; - int columns[ARRAY_SIZE(infos)]; + + /* make a space for repeating columns like we support in another tools (-o +FOO) */ + int columns[ARRAY_SIZE(infos) * 2]; size_t ncolumns; + unsigned int - specific_columns:1, json:1, no_headings:1, request_exit:1, @@ -392,15 +394,15 @@ static void __attribute__((__noreturn__)) usage(void) fputs(USAGE_HEADER, stdout); printf(_(" %s [options]\n"), program_invocation_short_name); - fputs(USAGE_SEPARATOR, stdout); - puts(_("Utility to display kernel interrupt information.")); + + puts(_("Interactive utility to display kernel interrupt information.")); fputs(USAGE_OPTIONS, stdout); fputs(_(" -d, --delay delay updates\n"), stdout); - fputs(_(" -o, --once only display interrupts once, then exit\n"), stdout); - fputs(_(" --json output json, implies displaying once\n"), stdout); - fputs(_(" --columns define which output columns to use (see below)\n"), stdout); + fputs(_(" --once only display interrupts once, then exit\n"), stdout); + fputs(_(" -J --json output json, implies displaying once\n"), stdout); + fputs(_(" -o --output define which output columns to use (see below)\n"), stdout); fputs(_(" -s, --sort specify sort criteria by character (see below)\n"), stdout); fputs(USAGE_SEPARATOR, stdout); printf(USAGE_HELP_OPTIONS(22)); @@ -621,23 +623,23 @@ static int event_loop(struct irqtop_ctl *const ctl) static void parse_args(struct irqtop_ctl *const ctl, int const argc, char *const *const argv) { + const char *outarg = NULL; enum { - COLUMNS_OPT = CHAR_MAX + 1, - JSON_OPT, + ONCE_OPT = CHAR_MAX + 1, }; static const struct option longopts[] = { {"delay", required_argument, NULL, 'd'}, {"sort", required_argument, NULL, 's'}, - {"once", no_argument, NULL, 'o'}, + {"once", no_argument, NULL, ONCE_OPT }, {"json", no_argument, NULL, 'J'}, - {"columns", required_argument, NULL, COLUMNS_OPT}, + {"output", required_argument, NULL, 'o'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} }; int o; - while ((o = getopt_long(argc, argv, "d:os:hJV", longopts, NULL)) != -1) { + while ((o = getopt_long(argc, argv, "d:o:s:hJV", longopts, NULL)) != -1) { switch (o) { case 'd': { @@ -652,7 +654,7 @@ static void parse_args(struct irqtop_ctl *const ctl, int const argc, case 's': ctl->sort_func = set_sort_func(optarg[0]); break; - case 'o': + case ONCE_OPT: ctl->run_once = 1; ctl->request_exit = 1; break; @@ -661,19 +663,8 @@ static void parse_args(struct irqtop_ctl *const ctl, int const argc, ctl->run_once = 1; ctl->request_exit = 1; break; - case COLUMNS_OPT: - ctl->specific_columns = 1; - if (optarg) { - ssize_t nc = string_to_idarray(optarg, ctl->columns, - ARRAY_SIZE(ctl->columns), - column_name_to_id); - - if (nc < 0) - errx(EXIT_FAILURE, - _("too many or unknown --columns argument: %s"), - optarg); - ctl->ncolumns = nc; - } + case 'o': + outarg = optarg; break; case 'V': print_version(EXIT_SUCCESS); @@ -683,6 +674,21 @@ static void parse_args(struct irqtop_ctl *const ctl, int const argc, errtryhelp(EXIT_FAILURE); } } + + /* default */ + if (!ctl->ncolumns) { + ctl->columns[ctl->ncolumns++] = COL_IRQ; + ctl->columns[ctl->ncolumns++] = COL_TOTAL; + if (!ctl->run_once) + ctl->columns[ctl->ncolumns++] = COL_DELTA; + ctl->columns[ctl->ncolumns++] = COL_NAME; + } + + /* add -o [+] to putput */ + if (outarg && string_add_to_idarray(outarg, ctl->columns, + ARRAY_SIZE(ctl->columns), + &ctl->ncolumns, column_name_to_id) < 0) + exit(EXIT_FAILURE); } int main(int argc, char **argv) @@ -700,14 +706,6 @@ int main(int argc, char **argv) parse_args(&ctl, argc, argv); - if (!ctl.specific_columns) { - ctl.columns[ctl.ncolumns++] = COL_IRQ; - ctl.columns[ctl.ncolumns++] = COL_TOTAL; - if (!ctl.run_once) - ctl.columns[ctl.ncolumns++] = COL_DELTA; - ctl.columns[ctl.ncolumns++] = COL_NAME; - } - if (!ctl.run_once) { is_tty = isatty(STDIN_FILENO); if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) From bd83424b8018e093f9832448df60c60098b76e6e Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 12:12:12 +0100 Subject: [PATCH 33/53] irqtop: reorder function Move functions to make next changes more readable. Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 146 ++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 8ae1744e6..8562adea3 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -166,6 +166,41 @@ static inline struct colinfo const *get_column_info(struct irqtop_ctl const return &infos[get_column_id(ctl, num)]; } +static int init_scols_table(struct irqtop_ctl *const ctl) +{ + size_t i; + + ctl->table = scols_new_table(); + if (!ctl->table) { + warn(_("failed to initialize output table")); + return 1; + } + scols_table_enable_json(ctl->table, ctl->json); + scols_table_enable_noheadings(ctl->table, ctl->no_headings); + + if (ctl->json) + scols_table_set_name(ctl->table, _("interrupts")); + + for (i = 0; i < ctl->ncolumns; i++) { + struct colinfo const *const col = get_column_info(ctl, i); + int flags = col->flags; + struct libscols_column *cl; + + cl = scols_table_new_column(ctl->table, col->name, col->whint, flags); + if (cl == NULL) { + warnx(_("failed to initialize output column")); + goto err; + } + if (ctl->json) + scols_column_set_json_type(cl, col->json_type); + } + + return 0; + err: + scols_unref_table(ctl->table); + return 1; +} + static void add_scols_line(struct irqtop_ctl *const ctl, struct irq_info const *const stat) { size_t i; @@ -205,41 +240,6 @@ static void add_scols_line(struct irqtop_ctl *const ctl, struct irq_info const * free(stat->name); } -static int init_scols_table(struct irqtop_ctl *const ctl) -{ - size_t i; - - ctl->table = scols_new_table(); - if (!ctl->table) { - warn(_("failed to initialize output table")); - return 1; - } - scols_table_enable_json(ctl->table, ctl->json); - scols_table_enable_noheadings(ctl->table, ctl->no_headings); - - if (ctl->json) - scols_table_set_name(ctl->table, _("interrupts")); - - for (i = 0; i < ctl->ncolumns; i++) { - struct colinfo const *const col = get_column_info(ctl, i); - int flags = col->flags; - struct libscols_column *cl; - - cl = scols_table_new_column(ctl->table, col->name, col->whint, flags); - if (cl == NULL) { - warnx(_("failed to initialize output column")); - goto err; - } - if (ctl->json) - scols_column_set_json_type(cl, col->json_type); - } - - return 0; - err: - scols_unref_table(ctl->table); - return 1; -} - static char *remove_repeated_spaces(char *const str) { char *inp = str, *outp = str; @@ -388,41 +388,6 @@ static void sort_result(struct irqtop_ctl *const ctl, qsort(result, nmemb, sizeof(*result), (int (*)(const void *, const void *))ctl->sort_func); } -static void __attribute__((__noreturn__)) usage(void) -{ - size_t i; - - fputs(USAGE_HEADER, stdout); - printf(_(" %s [options]\n"), program_invocation_short_name); - fputs(USAGE_SEPARATOR, stdout); - - puts(_("Interactive utility to display kernel interrupt information.")); - - fputs(USAGE_OPTIONS, stdout); - fputs(_(" -d, --delay delay updates\n"), stdout); - fputs(_(" --once only display interrupts once, then exit\n"), stdout); - fputs(_(" -J --json output json, implies displaying once\n"), stdout); - fputs(_(" -o --output define which output columns to use (see below)\n"), stdout); - fputs(_(" -s, --sort specify sort criteria by character (see below)\n"), stdout); - fputs(USAGE_SEPARATOR, stdout); - printf(USAGE_HELP_OPTIONS(22)); - - fputs(_("\nThe following interactive key commands are valid:\n"), stdout); - fputs(_(" i sort by IRQ\n"), stdout); - fputs(_(" t sort by TOTAL\n"), stdout); - fputs(_(" d sort by DELTA\n"), stdout); - fputs(_(" n sort by NAME\n"), stdout); - fputs(_(" q Q quit program\n"), stdout); - - fputs(USAGE_COLUMNS, stdout); - for (i = 0; i < ARRAY_SIZE(infos); i++) - fprintf(stdout, " %-5s %s\n", infos[i].name, _(infos[i].help)); - - printf(USAGE_MAN_TAIL("irqtop(1)")); - exit(EXIT_SUCCESS); -} - - static sort_fp *set_sort_func(char const key) { switch (key) { @@ -490,9 +455,6 @@ static int update_screen(struct irqtop_ctl *const ctl) return 1; } - if (init_scols_table(ctl)) - return 1; - size = sizeof(*stat->irq_info) * stat->nr_irq; result = xmalloc(size); memcpy(result, stat->irq_info, size); @@ -505,6 +467,10 @@ static int update_screen(struct irqtop_ctl *const ctl) } } sort_result(ctl, result, stat->nr_irq); + + if (init_scols_table(ctl)) + return 1; + for (index = 0; index < stat->nr_irq; index++) { curr = result + index; add_scols_line(ctl, curr); @@ -620,6 +586,40 @@ static int event_loop(struct irqtop_ctl *const ctl) return retval; } +static void __attribute__((__noreturn__)) usage(void) +{ + size_t i; + + fputs(USAGE_HEADER, stdout); + printf(_(" %s [options]\n"), program_invocation_short_name); + fputs(USAGE_SEPARATOR, stdout); + + puts(_("Interactive utility to display kernel interrupt information.")); + + fputs(USAGE_OPTIONS, stdout); + fputs(_(" -d, --delay delay updates\n"), stdout); + fputs(_(" --once only display interrupts once, then exit\n"), stdout); + fputs(_(" -J --json output json, implies displaying once\n"), stdout); + fputs(_(" -o --output define which output columns to use (see below)\n"), stdout); + fputs(_(" -s, --sort specify sort criteria by character (see below)\n"), stdout); + fputs(USAGE_SEPARATOR, stdout); + printf(USAGE_HELP_OPTIONS(22)); + + fputs(_("\nThe following interactive key commands are valid:\n"), stdout); + fputs(_(" i sort by IRQ\n"), stdout); + fputs(_(" t sort by TOTAL\n"), stdout); + fputs(_(" d sort by DELTA\n"), stdout); + fputs(_(" n sort by NAME\n"), stdout); + fputs(_(" q Q quit program\n"), stdout); + + fputs(USAGE_COLUMNS, stdout); + for (i = 0; i < ARRAY_SIZE(infos); i++) + fprintf(stdout, " %-5s %s\n", infos[i].name, _(infos[i].help)); + + printf(USAGE_MAN_TAIL("irqtop(1)")); + exit(EXIT_SUCCESS); +} + static void parse_args(struct irqtop_ctl *const ctl, int const argc, char *const *const argv) { From f675b0080308ea30f953433d37bd9096cdac1c07 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 12:25:40 +0100 Subject: [PATCH 34/53] irqtop: separate screen and scols code Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 74 +++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 8562adea3..196c67b11 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -404,6 +404,45 @@ static sort_fp *set_sort_func(char const key) } } +static int fill_scols_table(struct irqtop_ctl *const ctl, struct irq_stat **xstat) +{ + struct irq_info *result, *curr; + struct irq_stat *stat; + size_t size; + size_t index; + + /* the stats */ + stat = get_irqinfo(); + if (!stat) + return 1; + + size = sizeof(*stat->irq_info) * stat->nr_irq; + result = xmalloc(size); + memcpy(result, stat->irq_info, size); + + if (ctl->prev_stat) { + stat->delta_irq = 0; + for (index = 0; index < stat->nr_irq; index++) { + result[index].delta = result[index].total - + ctl->prev_stat->irq_info[index].total; + stat->delta_irq += result[index].delta; + } + } + sort_result(ctl, result, stat->nr_irq); + + if (init_scols_table(ctl)) + return 1; + + for (index = 0; index < stat->nr_irq; index++) { + curr = result + index; + add_scols_line(ctl, curr); + } + + free(result); + *xstat = stat; + return 0; +} + static void parse_input(struct irqtop_ctl *const ctl, char const c) { switch (c) { @@ -441,46 +480,19 @@ static inline void print_line(struct irqtop_ctl const *const ctl, static int update_screen(struct irqtop_ctl *const ctl) { - struct irq_info *result, *curr; struct irq_stat *stat; - size_t size; - size_t index; - time_t now; - char timestr[64]; - /* the stats */ - stat = get_irqinfo(); - if (!stat) { + if (fill_scols_table(ctl, &stat) != 0) { ctl->request_exit = 1; return 1; } - size = sizeof(*stat->irq_info) * stat->nr_irq; - result = xmalloc(size); - memcpy(result, stat->irq_info, size); - if (ctl->prev_stat) { - stat->delta_irq = 0; - for (index = 0; index < stat->nr_irq; index++) { - result[index].delta = result[index].total - - ctl->prev_stat->irq_info[index].total; - stat->delta_irq += result[index].delta; - } - } - sort_result(ctl, result, stat->nr_irq); - - if (init_scols_table(ctl)) - return 1; - - for (index = 0; index < stat->nr_irq; index++) { - curr = result + index; - add_scols_line(ctl, curr); - } - free(result); - /* header in interactive mode */ if (!ctl->run_once) { + time_t now = time(NULL); + char timestr[64]; + move(0, 0); - now = time(NULL); strtime_iso(&now, ISO_TIMESTAMP, timestr, sizeof(timestr)); print_line(ctl, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), stat->total_irq, stat->delta_irq, ctl->hostname, timestr); From 60794bd2c645c2f1fd1ffdcca0a37cea8d447b85 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 12:32:39 +0100 Subject: [PATCH 35/53] irqtop: keep WINDOW pointer in functions only Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 196c67b11..a5ed512b7 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -113,7 +113,6 @@ struct irq_stat { typedef int (sort_fp)(const struct irq_info *, const struct irq_info *); struct irqtop_ctl { - WINDOW *win; int cols; int rows; struct itimerspec timer; @@ -465,20 +464,7 @@ static void parse_input(struct irqtop_ctl *const ctl, char const c) } } -static inline void print_line(struct irqtop_ctl const *const ctl, - char const *const fmt, ...) -{ - va_list args; - - va_start(args, fmt); - if (ctl->run_once) - vprintf(fmt, args); - else - vw_printw(ctl->win, fmt, args); - va_end(args); -} - -static int update_screen(struct irqtop_ctl *const ctl) +static int update_screen(struct irqtop_ctl *const ctl, WINDOW *win) { struct irq_stat *stat; @@ -494,7 +480,7 @@ static int update_screen(struct irqtop_ctl *const ctl) move(0, 0); strtime_iso(&now, ISO_TIMESTAMP, timestr, sizeof(timestr)); - print_line(ctl, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), + wprintw(win, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), stat->total_irq, stat->delta_irq, ctl->hostname, timestr); } /* body */ @@ -504,7 +490,7 @@ static int update_screen(struct irqtop_ctl *const ctl) char *data; scols_print_table_to_string(ctl->table, &data); - print_line(ctl, "%s", data); + wprintw(win, "%s", data); free(data); } /* clean up */ @@ -515,7 +501,7 @@ static int update_screen(struct irqtop_ctl *const ctl) return 0; } -static int event_loop(struct irqtop_ctl *const ctl) +static int event_loop(struct irqtop_ctl *const ctl, WINDOW *win) { int efd, sfd, tfd; sigset_t sigmask; @@ -560,7 +546,7 @@ static int event_loop(struct irqtop_ctl *const ctl) if (epoll_ctl(efd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) != 0) err(EXIT_FAILURE, _("epoll_ctl failed")); - retval |= update_screen(ctl); + retval |= update_screen(ctl, win); refresh(); while (!ctl->request_exit) { @@ -591,7 +577,7 @@ static int event_loop(struct irqtop_ctl *const ctl) parse_input(ctl, c); } else abort(); - retval |= update_screen(ctl); + retval |= update_screen(ctl, win); refresh(); } } @@ -705,6 +691,7 @@ static void parse_args(struct irqtop_ctl *const ctl, int const argc, int main(int argc, char **argv) { + WINDOW *win = NULL; int is_tty = 0; int retval = EXIT_SUCCESS; struct termios saved_tty; @@ -723,7 +710,7 @@ int main(int argc, char **argv) if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) fputs(_("terminal setting retrieval"), stdout); - ctl.win = initscr(); + win = initscr(); get_terminal_dimension(&ctl.cols, &ctl.rows); resizeterm(ctl.rows, ctl.cols); curs_set(0); @@ -734,17 +721,17 @@ int main(int argc, char **argv) ctl.hostname = xgethostname(); if (ctl.run_once) - retval = update_screen(&ctl); + retval = update_screen(&ctl, NULL); else - event_loop(&ctl); + event_loop(&ctl, win); free_irqinfo(ctl.prev_stat); free(ctl.hostname); - if (ctl.win) { + if (win) { if (is_tty) tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); - delwin(ctl.win); + delwin(win); endwin(); } return retval; From d5280921becf98b7941f45166871f5b55350690c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 12:36:41 +0100 Subject: [PATCH 36/53] irqtop: remove unnecessary code * remove unused code smp_num_cpus * care about hostname and timer only real top-like output Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index a5ed512b7..f667a892f 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -118,7 +118,6 @@ struct irqtop_ctl { struct itimerspec timer; struct timeval uptime_tv; sort_fp *sort_func; - long smp_num_cpus; struct irq_stat *prev_stat; char *hostname; struct libscols_table *table; @@ -705,7 +704,9 @@ int main(int argc, char **argv) parse_args(&ctl, argc, argv); - if (!ctl.run_once) { + if (ctl.run_once) + retval = update_screen(&ctl, NULL); + else { is_tty = isatty(STDIN_FILENO); if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) fputs(_("terminal setting retrieval"), stdout); @@ -714,16 +715,12 @@ int main(int argc, char **argv) get_terminal_dimension(&ctl.cols, &ctl.rows); resizeterm(ctl.rows, ctl.cols); curs_set(0); - } - ctl.smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - gettime_monotonic(&ctl.uptime_tv); - ctl.hostname = xgethostname(); + gettime_monotonic(&ctl.uptime_tv); + ctl.hostname = xgethostname(); - if (ctl.run_once) - retval = update_screen(&ctl, NULL); - else event_loop(&ctl, win); + } free_irqinfo(ctl.prev_stat); free(ctl.hostname); From ec95b4365f073cf27c9a435a743ea3b6de4d2914 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 12:44:16 +0100 Subject: [PATCH 37/53] irqtop: separate normal and ncurses way Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 50 +++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index f667a892f..5ad8a0009 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -437,7 +437,11 @@ static int fill_scols_table(struct irqtop_ctl *const ctl, struct irq_stat **xsta } free(result); - *xstat = stat; + + if (xstat) + *xstat = stat; + else + free_irqinfo(stat); return 0; } @@ -463,9 +467,21 @@ static void parse_input(struct irqtop_ctl *const ctl, char const c) } } +static int print_irq_data(struct irqtop_ctl *const ctl) +{ + if (fill_scols_table(ctl, NULL) != 0) + return 1; + + scols_print_table(ctl->table); + scols_unref_table(ctl->table); + return 0; +} + static int update_screen(struct irqtop_ctl *const ctl, WINDOW *win) { struct irq_stat *stat; + time_t now = time(NULL); + char timestr[64], *data; if (fill_scols_table(ctl, &stat) != 0) { ctl->request_exit = 1; @@ -473,25 +489,15 @@ static int update_screen(struct irqtop_ctl *const ctl, WINDOW *win) } /* header in interactive mode */ - if (!ctl->run_once) { - time_t now = time(NULL); - char timestr[64]; - - move(0, 0); - strtime_iso(&now, ISO_TIMESTAMP, timestr, sizeof(timestr)); - wprintw(win, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), + move(0, 0); + strtime_iso(&now, ISO_TIMESTAMP, timestr, sizeof(timestr)); + wprintw(win, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), stat->total_irq, stat->delta_irq, ctl->hostname, timestr); - } - /* body */ - if (ctl->run_once) - scols_print_table(ctl->table); - else { - char *data; - scols_print_table_to_string(ctl->table, &data); - wprintw(win, "%s", data); - free(data); - } + scols_print_table_to_string(ctl->table, &data); + wprintw(win, "%s", data); + free(data); + /* clean up */ scols_unref_table(ctl->table); if (ctl->prev_stat) @@ -705,7 +711,7 @@ int main(int argc, char **argv) parse_args(&ctl, argc, argv); if (ctl.run_once) - retval = update_screen(&ctl, NULL); + retval = print_irq_data(&ctl); else { is_tty = isatty(STDIN_FILENO); if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) @@ -720,12 +726,10 @@ int main(int argc, char **argv) ctl.hostname = xgethostname(); event_loop(&ctl, win); - } - free_irqinfo(ctl.prev_stat); - free(ctl.hostname); + free_irqinfo(ctl.prev_stat); + free(ctl.hostname); - if (win) { if (is_tty) tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); delwin(win); From a6fa6aad4b68f442513331a385e398f270209ea8 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 12:46:45 +0100 Subject: [PATCH 38/53] irqtop: remove dead code Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 5ad8a0009..ec7394cb3 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -116,7 +116,6 @@ struct irqtop_ctl { int cols; int rows; struct itimerspec timer; - struct timeval uptime_tv; sort_fp *sort_func; struct irq_stat *prev_stat; char *hostname; @@ -722,9 +721,7 @@ int main(int argc, char **argv) resizeterm(ctl.rows, ctl.cols); curs_set(0); - gettime_monotonic(&ctl.uptime_tv); ctl.hostname = xgethostname(); - event_loop(&ctl, win); free_irqinfo(ctl.prev_stat); From 212ca752c1b835f530646e0ae2db594ba560586f Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 12:56:56 +0100 Subject: [PATCH 39/53] irqtop: keep table in functions only Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 64 ++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index ec7394cb3..0cef262fc 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -119,8 +119,6 @@ struct irqtop_ctl { sort_fp *sort_func; struct irq_stat *prev_stat; char *hostname; - struct libscols_table *table; - struct libscols_line *outline; /* make a space for repeating columns like we support in another tools (-o +FOO) */ int columns[ARRAY_SIZE(infos) * 2]; @@ -163,27 +161,28 @@ static inline struct colinfo const *get_column_info(struct irqtop_ctl const return &infos[get_column_id(ctl, num)]; } -static int init_scols_table(struct irqtop_ctl *const ctl) +static struct libscols_table *new_scols_table(struct irqtop_ctl *const ctl) { size_t i; + struct libscols_table *table; - ctl->table = scols_new_table(); - if (!ctl->table) { + table = scols_new_table(); + if (!table) { warn(_("failed to initialize output table")); - return 1; + return NULL; } - scols_table_enable_json(ctl->table, ctl->json); - scols_table_enable_noheadings(ctl->table, ctl->no_headings); + scols_table_enable_json(table, ctl->json); + scols_table_enable_noheadings(table, ctl->no_headings); if (ctl->json) - scols_table_set_name(ctl->table, _("interrupts")); + scols_table_set_name(table, _("interrupts")); for (i = 0; i < ctl->ncolumns; i++) { struct colinfo const *const col = get_column_info(ctl, i); int flags = col->flags; struct libscols_column *cl; - cl = scols_table_new_column(ctl->table, col->name, col->whint, flags); + cl = scols_table_new_column(table, col->name, col->whint, flags); if (cl == NULL) { warnx(_("failed to initialize output column")); goto err; @@ -192,18 +191,20 @@ static int init_scols_table(struct irqtop_ctl *const ctl) scols_column_set_json_type(cl, col->json_type); } - return 0; + return table; err: - scols_unref_table(ctl->table); - return 1; + scols_unref_table(table); + return NULL; } -static void add_scols_line(struct irqtop_ctl *const ctl, struct irq_info const *const stat) +static void add_scols_line(struct irqtop_ctl *const ctl, + struct irq_info const *const stat, + struct libscols_table *table) { size_t i; struct libscols_line *line; - line = scols_table_new_line(ctl->table, NULL); + line = scols_table_new_line(table, NULL); if (!line) { warn(_("failed to add line to output")); return; @@ -232,7 +233,6 @@ static void add_scols_line(struct irqtop_ctl *const ctl, struct irq_info const * if (str && scols_line_refer_data(line, i, str) != 0) err_oom(); } - ctl->outline = line; free(stat->irq); free(stat->name); } @@ -401,8 +401,9 @@ static sort_fp *set_sort_func(char const key) } } -static int fill_scols_table(struct irqtop_ctl *const ctl, struct irq_stat **xstat) +static struct libscols_table *get_scols_table(struct irqtop_ctl *const ctl, struct irq_stat **xstat) { + struct libscols_table *table; struct irq_info *result, *curr; struct irq_stat *stat; size_t size; @@ -411,7 +412,7 @@ static int fill_scols_table(struct irqtop_ctl *const ctl, struct irq_stat **xsta /* the stats */ stat = get_irqinfo(); if (!stat) - return 1; + return NULL; size = sizeof(*stat->irq_info) * stat->nr_irq; result = xmalloc(size); @@ -427,12 +428,13 @@ static int fill_scols_table(struct irqtop_ctl *const ctl, struct irq_stat **xsta } sort_result(ctl, result, stat->nr_irq); - if (init_scols_table(ctl)) - return 1; + table = new_scols_table(ctl); + if (!table) + return NULL; for (index = 0; index < stat->nr_irq; index++) { curr = result + index; - add_scols_line(ctl, curr); + add_scols_line(ctl, curr, table); } free(result); @@ -441,7 +443,8 @@ static int fill_scols_table(struct irqtop_ctl *const ctl, struct irq_stat **xsta *xstat = stat; else free_irqinfo(stat); - return 0; + + return table;; } static void parse_input(struct irqtop_ctl *const ctl, char const c) @@ -468,21 +471,26 @@ static void parse_input(struct irqtop_ctl *const ctl, char const c) static int print_irq_data(struct irqtop_ctl *const ctl) { - if (fill_scols_table(ctl, NULL) != 0) + struct libscols_table *table; + + table = get_scols_table(ctl, NULL); + if (!table) return 1; - scols_print_table(ctl->table); - scols_unref_table(ctl->table); + scols_print_table(table); + scols_unref_table(table); return 0; } static int update_screen(struct irqtop_ctl *const ctl, WINDOW *win) { + struct libscols_table *table; struct irq_stat *stat; time_t now = time(NULL); char timestr[64], *data; - if (fill_scols_table(ctl, &stat) != 0) { + table = get_scols_table(ctl, &stat); + if (!table) { ctl->request_exit = 1; return 1; } @@ -493,12 +501,12 @@ static int update_screen(struct irqtop_ctl *const ctl, WINDOW *win) wprintw(win, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), stat->total_irq, stat->delta_irq, ctl->hostname, timestr); - scols_print_table_to_string(ctl->table, &data); + scols_print_table_to_string(table, &data); wprintw(win, "%s", data); free(data); /* clean up */ - scols_unref_table(ctl->table); + scols_unref_table(table); if (ctl->prev_stat) free_irqinfo(ctl->prev_stat); ctl->prev_stat = stat; From c6ccf2ec963857b13d64d03434572d9052b38d19 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 13:38:02 +0100 Subject: [PATCH 40/53] irqtop: add struct irq_output * add irq_output to keep top-like stufff independent on data management * remove horrible "const" Signed-off-by: Karel Zak --- sys-utils/irqtop.c | 171 ++++++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 79 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 0cef262fc..c1ea19dd5 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -68,7 +68,6 @@ #include "ttyutils.h" #include "xalloc.h" -#define DEF_SORT_FUNC sort_total #define IRQ_INFO_LEN 64 #define MAX_EVENTS 3 @@ -112,23 +111,30 @@ struct irq_stat { typedef int (sort_fp)(const struct irq_info *, const struct irq_info *); +#define DEF_SORT_FUNC ((sort_fp *)sort_total) + +struct irq_output { + int columns[ARRAY_SIZE(infos) * 2]; + size_t ncolumns; + + sort_fp *sort_func; + + unsigned int + json:1, + no_headings:1; +}; + struct irqtop_ctl { int cols; int rows; struct itimerspec timer; - sort_fp *sort_func; struct irq_stat *prev_stat; char *hostname; - /* make a space for repeating columns like we support in another tools (-o +FOO) */ - int columns[ARRAY_SIZE(infos) * 2]; - size_t ncolumns; - unsigned int - json:1, - no_headings:1, request_exit:1, run_once:1; + }; static int column_name_to_id(char const *const name, size_t const namesz) @@ -146,22 +152,21 @@ static int column_name_to_id(char const *const name, size_t const namesz) return -1; } -static inline int get_column_id(struct irqtop_ctl const *const ctl, size_t const num) +static inline int get_column_id(struct irq_output *out, size_t const num) { - assert(num < ctl->ncolumns); - assert(ctl->columns[num] < (int)ARRAY_SIZE(infos)); + assert(num < out->ncolumns); + assert(out->columns[num] < (int)ARRAY_SIZE(infos)); - return ctl->columns[num]; + return out->columns[num]; } -static inline struct colinfo const *get_column_info(struct irqtop_ctl const - *const ctl, - unsigned const num) +static inline const struct colinfo *get_column_info( + struct irq_output *out, unsigned num) { - return &infos[get_column_id(ctl, num)]; + return &infos[get_column_id(out, num)]; } -static struct libscols_table *new_scols_table(struct irqtop_ctl *const ctl) +static struct libscols_table *new_scols_table(struct irq_output *out) { size_t i; struct libscols_table *table; @@ -171,14 +176,14 @@ static struct libscols_table *new_scols_table(struct irqtop_ctl *const ctl) warn(_("failed to initialize output table")); return NULL; } - scols_table_enable_json(table, ctl->json); - scols_table_enable_noheadings(table, ctl->no_headings); + scols_table_enable_json(table, out->json); + scols_table_enable_noheadings(table, out->no_headings); - if (ctl->json) + if (out->json) scols_table_set_name(table, _("interrupts")); - for (i = 0; i < ctl->ncolumns; i++) { - struct colinfo const *const col = get_column_info(ctl, i); + for (i = 0; i < out->ncolumns; i++) { + const struct colinfo *col = get_column_info(out, i); int flags = col->flags; struct libscols_column *cl; @@ -187,7 +192,7 @@ static struct libscols_table *new_scols_table(struct irqtop_ctl *const ctl) warnx(_("failed to initialize output column")); goto err; } - if (ctl->json) + if (out->json) scols_column_set_json_type(cl, col->json_type); } @@ -197,8 +202,8 @@ static struct libscols_table *new_scols_table(struct irqtop_ctl *const ctl) return NULL; } -static void add_scols_line(struct irqtop_ctl *const ctl, - struct irq_info const *const stat, +static void add_scols_line(struct irq_output *out, + struct irq_info *stat, struct libscols_table *table) { size_t i; @@ -210,10 +215,10 @@ static void add_scols_line(struct irqtop_ctl *const ctl, return; } - for (i = 0; i < ctl->ncolumns; i++) { + for (i = 0; i < out->ncolumns; i++) { char *str = NULL; - switch (get_column_id(ctl, i)) { + switch (get_column_id(out, i)) { case COL_IRQ: xasprintf(&str, "%s", stat->irq); break; @@ -233,11 +238,13 @@ static void add_scols_line(struct irqtop_ctl *const ctl, if (str && scols_line_refer_data(line, i, str) != 0) err_oom(); } + + /* FIXME */ free(stat->irq); free(stat->name); } -static char *remove_repeated_spaces(char *const str) +static char *remove_repeated_spaces(char *str) { char *inp = str, *outp = str; uint8_t prev_space = 0; @@ -347,45 +354,47 @@ static struct irq_stat *get_irqinfo(void) return NULL; } -static void free_irqinfo(struct irq_stat *const stat) +static void free_irqinfo(struct irq_stat *stat) { if (stat) free(stat->irq_info); free(stat); } -static int sort_name(struct irq_info const *const a, - struct irq_info const *const b) +static int sort_name(const struct irq_info *a, + const struct irq_info *b) { return (strcmp(a->name, b->name) > 0) ? 1 : 0; } -static int sort_total(struct irq_info const *const a, - struct irq_info const *const b) +static int sort_total(const struct irq_info *a, + const struct irq_info *b) { return a->total < b->total; } -static int sort_delta(struct irq_info const *const a, - struct irq_info const *const b) +static int sort_delta(const struct irq_info *a, + const struct irq_info *b) { return a->delta < b->delta; } -static int sort_interrupts(struct irq_info const *const a, - struct irq_info const *const b) +static int sort_interrupts(const struct irq_info *a, + const struct irq_info *b) { return (strcmp(a->irq, b->irq) > 0) ? 1 : 0; } -static void sort_result(struct irqtop_ctl *const ctl, - struct irq_info *const result, - size_t const nmemb) +static void sort_result(struct irq_output *out, + struct irq_info *result, + size_t nmemb) { - qsort(result, nmemb, sizeof(*result), (int (*)(const void *, const void *))ctl->sort_func); + qsort(result, nmemb, sizeof(*result), + (int (*)(const void *, + const void *))out->sort_func); } -static sort_fp *set_sort_func(char const key) +static sort_fp *set_sort_func(char key) { switch (key) { case 'i': @@ -401,7 +410,9 @@ static sort_fp *set_sort_func(char const key) } } -static struct libscols_table *get_scols_table(struct irqtop_ctl *const ctl, struct irq_stat **xstat) +static struct libscols_table *get_scols_table(struct irq_output *out, + struct irq_stat *prev, + struct irq_stat **xstat) { struct libscols_table *table; struct irq_info *result, *curr; @@ -418,23 +429,23 @@ static struct libscols_table *get_scols_table(struct irqtop_ctl *const ctl, stru result = xmalloc(size); memcpy(result, stat->irq_info, size); - if (ctl->prev_stat) { + if (prev) { stat->delta_irq = 0; for (index = 0; index < stat->nr_irq; index++) { - result[index].delta = result[index].total - - ctl->prev_stat->irq_info[index].total; + result[index].delta = result[index].total + - prev->irq_info[index].total; stat->delta_irq += result[index].delta; } } - sort_result(ctl, result, stat->nr_irq); + sort_result(out, result, stat->nr_irq); - table = new_scols_table(ctl); + table = new_scols_table(out); if (!table) return NULL; for (index = 0; index < stat->nr_irq; index++) { curr = result + index; - add_scols_line(ctl, curr, table); + add_scols_line(out, curr, table); } free(result); @@ -444,23 +455,23 @@ static struct libscols_table *get_scols_table(struct irqtop_ctl *const ctl, stru else free_irqinfo(stat); - return table;; + return table; } -static void parse_input(struct irqtop_ctl *const ctl, char const c) +static void parse_input(struct irqtop_ctl *ctl, struct irq_output *out, char c) { switch (c) { case 'i': - ctl->sort_func = sort_interrupts; + out->sort_func = sort_interrupts; break; case 't': - ctl->sort_func = sort_total; + out->sort_func = sort_total; break; case 'd': - ctl->sort_func = sort_delta; + out->sort_func = sort_delta; break; case 'n': - ctl->sort_func = sort_name; + out->sort_func = sort_name; break; case 'q': case 'Q': @@ -469,11 +480,11 @@ static void parse_input(struct irqtop_ctl *const ctl, char const c) } } -static int print_irq_data(struct irqtop_ctl *const ctl) +static int print_irq_data(struct irq_output *out) { struct libscols_table *table; - table = get_scols_table(ctl, NULL); + table = get_scols_table(out, NULL, NULL); if (!table) return 1; @@ -482,14 +493,14 @@ static int print_irq_data(struct irqtop_ctl *const ctl) return 0; } -static int update_screen(struct irqtop_ctl *const ctl, WINDOW *win) +static int update_screen(struct irqtop_ctl *ctl, struct irq_output *out, WINDOW *win) { struct libscols_table *table; struct irq_stat *stat; time_t now = time(NULL); char timestr[64], *data; - table = get_scols_table(ctl, &stat); + table = get_scols_table(out, ctl->prev_stat, &stat); if (!table) { ctl->request_exit = 1; return 1; @@ -513,7 +524,7 @@ static int update_screen(struct irqtop_ctl *const ctl, WINDOW *win) return 0; } -static int event_loop(struct irqtop_ctl *const ctl, WINDOW *win) +static int event_loop(struct irqtop_ctl *ctl, struct irq_output *out, WINDOW *win) { int efd, sfd, tfd; sigset_t sigmask; @@ -558,7 +569,7 @@ static int event_loop(struct irqtop_ctl *const ctl, WINDOW *win) if (epoll_ctl(efd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) != 0) err(EXIT_FAILURE, _("epoll_ctl failed")); - retval |= update_screen(ctl, win); + retval |= update_screen(ctl, out, win); refresh(); while (!ctl->request_exit) { @@ -586,10 +597,10 @@ static int event_loop(struct irqtop_ctl *const ctl, WINDOW *win) if (read(STDIN_FILENO, &c, 1) != 1) warn(_("read failed")); - parse_input(ctl, c); + parse_input(ctl, out, c); } else abort(); - retval |= update_screen(ctl, win); + retval |= update_screen(ctl, out, win); refresh(); } } @@ -630,8 +641,10 @@ static void __attribute__((__noreturn__)) usage(void) exit(EXIT_SUCCESS); } -static void parse_args(struct irqtop_ctl *const ctl, int const argc, - char *const *const argv) +static void parse_args( struct irqtop_ctl *ctl, + struct irq_output *out, + int argc, + char **argv) { const char *outarg = NULL; enum { @@ -662,14 +675,14 @@ static void parse_args(struct irqtop_ctl *const ctl, int const argc, } break; case 's': - ctl->sort_func = set_sort_func(optarg[0]); + out->sort_func = set_sort_func(optarg[0]); break; case ONCE_OPT: ctl->run_once = 1; ctl->request_exit = 1; break; case 'J': - ctl->json = 1; + out->json = 1; ctl->run_once = 1; ctl->request_exit = 1; break; @@ -686,18 +699,18 @@ static void parse_args(struct irqtop_ctl *const ctl, int const argc, } /* default */ - if (!ctl->ncolumns) { - ctl->columns[ctl->ncolumns++] = COL_IRQ; - ctl->columns[ctl->ncolumns++] = COL_TOTAL; + if (!out->ncolumns) { + out->columns[out->ncolumns++] = COL_IRQ; + out->columns[out->ncolumns++] = COL_TOTAL; if (!ctl->run_once) - ctl->columns[ctl->ncolumns++] = COL_DELTA; - ctl->columns[ctl->ncolumns++] = COL_NAME; + out->columns[out->ncolumns++] = COL_DELTA; + out->columns[out->ncolumns++] = COL_NAME; } /* add -o [+] to putput */ - if (outarg && string_add_to_idarray(outarg, ctl->columns, - ARRAY_SIZE(ctl->columns), - &ctl->ncolumns, column_name_to_id) < 0) + if (outarg && string_add_to_idarray(outarg, out->columns, + ARRAY_SIZE(out->columns), + &out->ncolumns, column_name_to_id) < 0) exit(EXIT_FAILURE); } @@ -707,18 +720,18 @@ int main(int argc, char **argv) int is_tty = 0; int retval = EXIT_SUCCESS; struct termios saved_tty; + struct irq_output out = { .ncolumns = 0, .sort_func = DEF_SORT_FUNC }; struct irqtop_ctl ctl = { .timer.it_interval = {3, 0}, .timer.it_value = {3, 0} }; setlocale(LC_ALL, ""); - ctl.sort_func = DEF_SORT_FUNC; - parse_args(&ctl, argc, argv); + parse_args(&ctl, &out, argc, argv); if (ctl.run_once) - retval = print_irq_data(&ctl); + retval = print_irq_data(&out); else { is_tty = isatty(STDIN_FILENO); if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) @@ -730,7 +743,7 @@ int main(int argc, char **argv) curs_set(0); ctl.hostname = xgethostname(); - event_loop(&ctl, win); + event_loop(&ctl, &out, win); free_irqinfo(ctl.prev_stat); free(ctl.hostname); From 94e7e258a3bdc7e703132b83bd46aec4ed6eb6b6 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 14:04:33 +0100 Subject: [PATCH 41/53] irqtop: move independent code to irq-common.c Signed-off-by: Karel Zak --- sys-utils/Makemodule.am | 2 + sys-utils/irq-common.c | 362 +++++++++++++++++++++++++++++++ sys-utils/irq-common.h | 75 +++++++ sys-utils/irqtop.c | 460 +++------------------------------------- 4 files changed, 464 insertions(+), 435 deletions(-) create mode 100644 sys-utils/irq-common.c create mode 100644 sys-utils/irq-common.h diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index 28e01c9ba..5a9e8c692 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -54,6 +54,8 @@ if BUILD_IRQTOP usrbin_exec_PROGRAMS += irqtop dist_man_MANS += sys-utils/irqtop.1 irqtop_SOURCES = sys-utils/irqtop.c \ + sys-utils/irq-common.c \ + sys-utils/irq-common.h \ lib/monotonic.c irqtop_LDADD = $(LDADD) libcommon.la $(NCURSES_LIBS) $(REALTIME_LIBS) libsmartcols.la irqtop_CFLAGS = $(AM_CFLAGS) $(NCURSES_CFLAGS) -I$(ul_libsmartcols_incdir) diff --git a/sys-utils/irq-common.c b/sys-utils/irq-common.c new file mode 100644 index 000000000..33faba43c --- /dev/null +++ b/sys-utils/irq-common.c @@ -0,0 +1,362 @@ +/* + * irq-common.c - functions to display kernel interrupt information. + * + * Copyright (C) 2019 zhenwei pi + * Copyright (C) 2020 Karel Zak + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "c.h" +#include "nls.h" +#include "pathnames.h" +#include "strutils.h" +#include "xalloc.h" + +#include "irq-common.h" + +#define IRQ_INFO_LEN 64 + +struct colinfo { + const char *name; + double whint; + int flags; + const char *help; + int json_type; +}; + +static const struct colinfo infos[] = { + [COL_IRQ] = {"IRQ", 0.10, SCOLS_FL_RIGHT, N_("interrupts"), SCOLS_JSON_STRING}, + [COL_TOTAL] = {"TOTAL", 0.10, SCOLS_FL_RIGHT, N_("total count"), SCOLS_JSON_NUMBER}, + [COL_DELTA] = {"DELTA", 0.10, SCOLS_FL_RIGHT, N_("delta count"), SCOLS_JSON_NUMBER}, + [COL_NAME] = {"NAME", 0.70, SCOLS_FL_TRUNC, N_("name"), SCOLS_JSON_STRING}, +}; + +int irq_column_name_to_id(char const *const name, size_t const namesz) +{ + size_t i; + + assert(name); + for (i = 0; i < ARRAY_SIZE(infos); i++) { + const char *cn = infos[i].name; + + if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) + return i; + } + warnx(_("unknown column: %s"), name); + return -1; +} + +static inline int get_column_id(struct irq_output *out, size_t const num) +{ + assert(num < out->ncolumns); + assert(out->columns[num] < (int)ARRAY_SIZE(infos)); + + return out->columns[num]; +} + +static inline const struct colinfo *get_column_info( + struct irq_output *out, unsigned num) +{ + return &infos[get_column_id(out, num)]; +} + +void irq_print_columns(FILE *f) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(infos); i++) + fprintf(f, " %-5s %s\n", infos[i].name, _(infos[i].help)); +} + +static struct libscols_table *new_scols_table(struct irq_output *out) +{ + size_t i; + struct libscols_table *table; + + table = scols_new_table(); + if (!table) { + warn(_("failed to initialize output table")); + return NULL; + } + scols_table_enable_json(table, out->json); + scols_table_enable_noheadings(table, out->no_headings); + + if (out->json) + scols_table_set_name(table, _("interrupts")); + + for (i = 0; i < out->ncolumns; i++) { + const struct colinfo *col = get_column_info(out, i); + int flags = col->flags; + struct libscols_column *cl; + + cl = scols_table_new_column(table, col->name, col->whint, flags); + if (cl == NULL) { + warnx(_("failed to initialize output column")); + goto err; + } + if (out->json) + scols_column_set_json_type(cl, col->json_type); + } + + return table; + err: + scols_unref_table(table); + return NULL; +} + +static void add_scols_line(struct irq_output *out, + struct irq_info *stat, + struct libscols_table *table) +{ + size_t i; + struct libscols_line *line; + + line = scols_table_new_line(table, NULL); + if (!line) { + warn(_("failed to add line to output")); + return; + } + + for (i = 0; i < out->ncolumns; i++) { + char *str = NULL; + + switch (get_column_id(out, i)) { + case COL_IRQ: + xasprintf(&str, "%s", stat->irq); + break; + case COL_TOTAL: + xasprintf(&str, "%ld", stat->total); + break; + case COL_DELTA: + xasprintf(&str, "%ld", stat->delta); + break; + case COL_NAME: + xasprintf(&str, "%s", stat->name); + break; + default: + break; + } + + if (str && scols_line_refer_data(line, i, str) != 0) + err_oom(); + } + + /* FIXME */ + free(stat->irq); + free(stat->name); +} + +static char *remove_repeated_spaces(char *str) +{ + char *inp = str, *outp = str; + uint8_t prev_space = 0; + + while (*inp) { + if (isspace(*inp)) { + if (!prev_space) { + *outp++ = ' '; + prev_space = 1; + } + } else { + *outp++ = *inp; + prev_space = 0; + } + ++inp; + } + *outp = '\0'; + return str; +} + +/* + * irqinfo - parse the system's interrupts + */ +static struct irq_stat *get_irqinfo(void) +{ + FILE *irqfile; + char *line = NULL, *tmp; + size_t len = 0; + struct irq_stat *stat; + struct irq_info *curr; + + /* NAME + ':' + 11 bytes/cpu + IRQ_NAME_LEN */ + stat = xcalloc(1, sizeof(*stat)); + + stat->irq_info = xmalloc(sizeof(*stat->irq_info) * IRQ_INFO_LEN); + stat->nr_irq_info = IRQ_INFO_LEN; + + irqfile = fopen(_PATH_PROC_INTERRUPTS, "r"); + if (!irqfile) { + warn(_("cannot open %s"), _PATH_PROC_INTERRUPTS); + goto free_stat; + } + + /* read header firstly */ + if (getline(&line, &len, irqfile) < 0) { + warn(_("cannot read %s"), _PATH_PROC_INTERRUPTS); + goto close_file; + } + + tmp = line; + while ((tmp = strstr(tmp, "CPU")) != NULL) { + tmp += 3; /* skip this "CPU", find next */ + stat->nr_active_cpu++; + } + + /* parse each line of _PATH_PROC_INTERRUPTS */ + while (getline(&line, &len, irqfile) >= 0) { + unsigned long count; + int index, length; + + tmp = strchr(line, ':'); + if (!tmp) + continue; + + length = strlen(line); + + curr = stat->irq_info + stat->nr_irq++; + memset(curr, 0, sizeof(*curr)); + *tmp = '\0'; + curr->irq = xstrdup(line); + ltrim_whitespace((unsigned char *)curr->irq); + + tmp += 1; + for (index = 0; (index < stat->nr_active_cpu) && (tmp - line < length); index++) { + sscanf(tmp, " %10lu", &count); + curr->total += count; + stat->total_irq += count; + tmp += 11; + } + + if (tmp - line < length) { + /* strip all space before desc */ + while (isspace(*tmp)) + tmp++; + tmp = remove_repeated_spaces(tmp); + rtrim_whitespace((unsigned char *)tmp); + curr->name = xstrdup(tmp); + } else /* no irq name string, we have to set '\0' here */ + curr->name = xstrdup(""); + + if (stat->nr_irq == stat->nr_irq_info) { + stat->nr_irq_info *= 2; + stat->irq_info = xrealloc(stat->irq_info, + sizeof(*stat->irq_info) * stat->nr_irq_info); + } + } + fclose(irqfile); + free(line); + return stat; + + close_file: + fclose(irqfile); + free_stat: + free(stat->irq_info); + free(stat); + free(line); + return NULL; +} + +void free_irqinfo(struct irq_stat *stat) +{ + if (stat) + free(stat->irq_info); + free(stat); +} + +static void sort_result(struct irq_output *out, + struct irq_info *result, + size_t nmemb) +{ + qsort(result, nmemb, sizeof(*result), + (int (*)(const void *, + const void *))out->sort_func); +} + +sort_fp *set_sort_func(char key) +{ + switch (key) { + case 'i': + return sort_interrupts; + case 't': + return sort_total; + case 'd': + return sort_delta; + case 'n': + return sort_name; + default: + return DEF_SORT_FUNC; + } +} + +struct libscols_table *get_scols_table(struct irq_output *out, + struct irq_stat *prev, + struct irq_stat **xstat) +{ + struct libscols_table *table; + struct irq_info *result, *curr; + struct irq_stat *stat; + size_t size; + size_t index; + + /* the stats */ + stat = get_irqinfo(); + if (!stat) + return NULL; + + size = sizeof(*stat->irq_info) * stat->nr_irq; + result = xmalloc(size); + memcpy(result, stat->irq_info, size); + + if (prev) { + stat->delta_irq = 0; + for (index = 0; index < stat->nr_irq; index++) { + result[index].delta = result[index].total + - prev->irq_info[index].total; + stat->delta_irq += result[index].delta; + } + } + sort_result(out, result, stat->nr_irq); + + table = new_scols_table(out); + if (!table) + return NULL; + + for (index = 0; index < stat->nr_irq; index++) { + curr = result + index; + add_scols_line(out, curr, table); + } + + free(result); + + if (xstat) + *xstat = stat; + else + free_irqinfo(stat); + + return table; +} diff --git a/sys-utils/irq-common.h b/sys-utils/irq-common.h new file mode 100644 index 000000000..3a4cd6a03 --- /dev/null +++ b/sys-utils/irq-common.h @@ -0,0 +1,75 @@ + +enum { + COL_IRQ = 1, + COL_TOTAL, + COL_DELTA, + COL_NAME, + + __COL_COUNT +}; + +struct irq_info { + char *irq; /* short name of this irq */ + char *name; /* descriptive name of this irq */ + unsigned long total; /* total count since system start up */ + unsigned long delta; /* delta count since previous update */ +}; + +struct irq_stat { + unsigned int nr_irq; /* number of irq vector */ + unsigned int nr_irq_info; /* number of irq info */ + struct irq_info *irq_info; /* array of irq_info */ + long nr_active_cpu; /* number of active cpu */ + unsigned long total_irq; /* total irqs */ + unsigned long delta_irq; /* delta irqs */ +}; + +typedef int (sort_fp)(const struct irq_info *, const struct irq_info *); + +#define DEF_SORT_FUNC ((sort_fp *)sort_total) + +struct irq_output { + int columns[__COL_COUNT * 2]; + size_t ncolumns; + + sort_fp *sort_func; + + unsigned int + json:1, + no_headings:1; +}; + +int irq_column_name_to_id(char const *const name, size_t const namesz); +void free_irqinfo(struct irq_stat *stat); + +void irq_print_columns(FILE *f); + +static inline int sort_name(const struct irq_info *a, + const struct irq_info *b) +{ + return (strcmp(a->name, b->name) > 0) ? 1 : 0; +} + +static inline int sort_total(const struct irq_info *a, + const struct irq_info *b) +{ + return a->total < b->total; +} + +static inline int sort_delta(const struct irq_info *a, + const struct irq_info *b) +{ + return a->delta < b->delta; +} + +static inline int sort_interrupts(const struct irq_info *a, + const struct irq_info *b) +{ + return (strcmp(a->irq, b->irq) > 0) ? 1 : 0; +} + +sort_fp *set_sort_func(char key); + +struct libscols_table *get_scols_table(struct irq_output *out, + struct irq_stat *prev, + struct irq_stat **xstat); diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index c1ea19dd5..3e8b24bb5 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -1,9 +1,8 @@ /* * irqtop.c - utility to display kernel interrupt information. * - * zhenwei pi - * - * Copyright (C) 2019 zhenwei pi + * Copyright (C) 2019 zhenwei pi + * Copyright (C) 2020 Karel Zak * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,7 +18,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - #include #include #include @@ -68,62 +66,10 @@ #include "ttyutils.h" #include "xalloc.h" -#define IRQ_INFO_LEN 64 +#include "irq-common.h" + #define MAX_EVENTS 3 -struct colinfo { - const char *name; - double whint; - int flags; - const char *help; - int json_type; -}; - -enum { - COL_IRQ, - COL_TOTAL, - COL_DELTA, - COL_NAME -}; - -static const struct colinfo infos[] = { - [COL_IRQ] = {"IRQ", 0.10, SCOLS_FL_RIGHT, N_("interrupts"), SCOLS_JSON_STRING}, - [COL_TOTAL] = {"TOTAL", 0.10, SCOLS_FL_RIGHT, N_("total count"), SCOLS_JSON_NUMBER}, - [COL_DELTA] = {"DELTA", 0.10, SCOLS_FL_RIGHT, N_("delta count"), SCOLS_JSON_NUMBER}, - [COL_NAME] = {"NAME", 0.70, SCOLS_FL_TRUNC, N_("name"), SCOLS_JSON_STRING}, -}; - -struct irq_info { - char *irq; /* short name of this irq */ - char *name; /* descriptive name of this irq */ - unsigned long total; /* total count since system start up */ - unsigned long delta; /* delta count since previous update */ -}; - -struct irq_stat { - unsigned int nr_irq; /* number of irq vector */ - unsigned int nr_irq_info; /* number of irq info */ - struct irq_info *irq_info; /* array of irq_info */ - long nr_active_cpu; /* number of active cpu */ - unsigned long total_irq; /* total irqs */ - unsigned long delta_irq; /* delta irqs */ -}; - -typedef int (sort_fp)(const struct irq_info *, const struct irq_info *); - -#define DEF_SORT_FUNC ((sort_fp *)sort_total) - -struct irq_output { - int columns[ARRAY_SIZE(infos) * 2]; - size_t ncolumns; - - sort_fp *sort_func; - - unsigned int - json:1, - no_headings:1; -}; - struct irqtop_ctl { int cols; int rows; @@ -131,333 +77,10 @@ struct irqtop_ctl { struct irq_stat *prev_stat; char *hostname; - unsigned int - request_exit:1, - run_once:1; + unsigned int request_exit:1; }; -static int column_name_to_id(char const *const name, size_t const namesz) -{ - size_t i; - - assert(name); - for (i = 0; i < ARRAY_SIZE(infos); i++) { - const char *cn = infos[i].name; - - if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) - return i; - } - warnx(_("unknown column: %s"), name); - return -1; -} - -static inline int get_column_id(struct irq_output *out, size_t const num) -{ - assert(num < out->ncolumns); - assert(out->columns[num] < (int)ARRAY_SIZE(infos)); - - return out->columns[num]; -} - -static inline const struct colinfo *get_column_info( - struct irq_output *out, unsigned num) -{ - return &infos[get_column_id(out, num)]; -} - -static struct libscols_table *new_scols_table(struct irq_output *out) -{ - size_t i; - struct libscols_table *table; - - table = scols_new_table(); - if (!table) { - warn(_("failed to initialize output table")); - return NULL; - } - scols_table_enable_json(table, out->json); - scols_table_enable_noheadings(table, out->no_headings); - - if (out->json) - scols_table_set_name(table, _("interrupts")); - - for (i = 0; i < out->ncolumns; i++) { - const struct colinfo *col = get_column_info(out, i); - int flags = col->flags; - struct libscols_column *cl; - - cl = scols_table_new_column(table, col->name, col->whint, flags); - if (cl == NULL) { - warnx(_("failed to initialize output column")); - goto err; - } - if (out->json) - scols_column_set_json_type(cl, col->json_type); - } - - return table; - err: - scols_unref_table(table); - return NULL; -} - -static void add_scols_line(struct irq_output *out, - struct irq_info *stat, - struct libscols_table *table) -{ - size_t i; - struct libscols_line *line; - - line = scols_table_new_line(table, NULL); - if (!line) { - warn(_("failed to add line to output")); - return; - } - - for (i = 0; i < out->ncolumns; i++) { - char *str = NULL; - - switch (get_column_id(out, i)) { - case COL_IRQ: - xasprintf(&str, "%s", stat->irq); - break; - case COL_TOTAL: - xasprintf(&str, "%ld", stat->total); - break; - case COL_DELTA: - xasprintf(&str, "%ld", stat->delta); - break; - case COL_NAME: - xasprintf(&str, "%s", stat->name); - break; - default: - break; - } - - if (str && scols_line_refer_data(line, i, str) != 0) - err_oom(); - } - - /* FIXME */ - free(stat->irq); - free(stat->name); -} - -static char *remove_repeated_spaces(char *str) -{ - char *inp = str, *outp = str; - uint8_t prev_space = 0; - - while (*inp) { - if (isspace(*inp)) { - if (!prev_space) { - *outp++ = ' '; - prev_space = 1; - } - } else { - *outp++ = *inp; - prev_space = 0; - } - ++inp; - } - *outp = '\0'; - return str; -} - -/* - * irqinfo - parse the system's interrupts - */ -static struct irq_stat *get_irqinfo(void) -{ - FILE *irqfile; - char *line = NULL, *tmp; - size_t len = 0; - struct irq_stat *stat; - struct irq_info *curr; - - /* NAME + ':' + 11 bytes/cpu + IRQ_NAME_LEN */ - stat = xcalloc(1, sizeof(*stat)); - - stat->irq_info = xmalloc(sizeof(*stat->irq_info) * IRQ_INFO_LEN); - stat->nr_irq_info = IRQ_INFO_LEN; - - irqfile = fopen(_PATH_PROC_INTERRUPTS, "r"); - if (!irqfile) { - warn(_("cannot open %s"), _PATH_PROC_INTERRUPTS); - goto free_stat; - } - - /* read header firstly */ - if (getline(&line, &len, irqfile) < 0) { - warn(_("cannot read %s"), _PATH_PROC_INTERRUPTS); - goto close_file; - } - - tmp = line; - while ((tmp = strstr(tmp, "CPU")) != NULL) { - tmp += 3; /* skip this "CPU", find next */ - stat->nr_active_cpu++; - } - - /* parse each line of _PATH_PROC_INTERRUPTS */ - while (getline(&line, &len, irqfile) >= 0) { - unsigned long count; - int index, length; - - tmp = strchr(line, ':'); - if (!tmp) - continue; - - length = strlen(line); - - curr = stat->irq_info + stat->nr_irq++; - memset(curr, 0, sizeof(*curr)); - *tmp = '\0'; - curr->irq = xstrdup(line); - ltrim_whitespace((unsigned char *)curr->irq); - - tmp += 1; - for (index = 0; (index < stat->nr_active_cpu) && (tmp - line < length); index++) { - sscanf(tmp, " %10lu", &count); - curr->total += count; - stat->total_irq += count; - tmp += 11; - } - - if (tmp - line < length) { - /* strip all space before desc */ - while (isspace(*tmp)) - tmp++; - tmp = remove_repeated_spaces(tmp); - rtrim_whitespace((unsigned char *)tmp); - curr->name = xstrdup(tmp); - } else /* no irq name string, we have to set '\0' here */ - curr->name = xstrdup(""); - - if (stat->nr_irq == stat->nr_irq_info) { - stat->nr_irq_info *= 2; - stat->irq_info = xrealloc(stat->irq_info, - sizeof(*stat->irq_info) * stat->nr_irq_info); - } - } - fclose(irqfile); - free(line); - return stat; - - close_file: - fclose(irqfile); - free_stat: - free(stat->irq_info); - free(stat); - free(line); - return NULL; -} - -static void free_irqinfo(struct irq_stat *stat) -{ - if (stat) - free(stat->irq_info); - free(stat); -} - -static int sort_name(const struct irq_info *a, - const struct irq_info *b) -{ - return (strcmp(a->name, b->name) > 0) ? 1 : 0; -} - -static int sort_total(const struct irq_info *a, - const struct irq_info *b) -{ - return a->total < b->total; -} - -static int sort_delta(const struct irq_info *a, - const struct irq_info *b) -{ - return a->delta < b->delta; -} - -static int sort_interrupts(const struct irq_info *a, - const struct irq_info *b) -{ - return (strcmp(a->irq, b->irq) > 0) ? 1 : 0; -} - -static void sort_result(struct irq_output *out, - struct irq_info *result, - size_t nmemb) -{ - qsort(result, nmemb, sizeof(*result), - (int (*)(const void *, - const void *))out->sort_func); -} - -static sort_fp *set_sort_func(char key) -{ - switch (key) { - case 'i': - return sort_interrupts; - case 't': - return sort_total; - case 'd': - return sort_delta; - case 'n': - return sort_name; - default: - return DEF_SORT_FUNC; - } -} - -static struct libscols_table *get_scols_table(struct irq_output *out, - struct irq_stat *prev, - struct irq_stat **xstat) -{ - struct libscols_table *table; - struct irq_info *result, *curr; - struct irq_stat *stat; - size_t size; - size_t index; - - /* the stats */ - stat = get_irqinfo(); - if (!stat) - return NULL; - - size = sizeof(*stat->irq_info) * stat->nr_irq; - result = xmalloc(size); - memcpy(result, stat->irq_info, size); - - if (prev) { - stat->delta_irq = 0; - for (index = 0; index < stat->nr_irq; index++) { - result[index].delta = result[index].total - - prev->irq_info[index].total; - stat->delta_irq += result[index].delta; - } - } - sort_result(out, result, stat->nr_irq); - - table = new_scols_table(out); - if (!table) - return NULL; - - for (index = 0; index < stat->nr_irq; index++) { - curr = result + index; - add_scols_line(out, curr, table); - } - - free(result); - - if (xstat) - *xstat = stat; - else - free_irqinfo(stat); - - return table; -} - static void parse_input(struct irqtop_ctl *ctl, struct irq_output *out, char c) { switch (c) { @@ -480,19 +103,6 @@ static void parse_input(struct irqtop_ctl *ctl, struct irq_output *out, char c) } } -static int print_irq_data(struct irq_output *out) -{ - struct libscols_table *table; - - table = get_scols_table(out, NULL, NULL); - if (!table) - return 1; - - scols_print_table(table); - scols_unref_table(table); - return 0; -} - static int update_screen(struct irqtop_ctl *ctl, struct irq_output *out, WINDOW *win) { struct libscols_table *table; @@ -609,8 +219,6 @@ static int event_loop(struct irqtop_ctl *ctl, struct irq_output *out, WINDOW *wi static void __attribute__((__noreturn__)) usage(void) { - size_t i; - fputs(USAGE_HEADER, stdout); printf(_(" %s [options]\n"), program_invocation_short_name); fputs(USAGE_SEPARATOR, stdout); @@ -634,8 +242,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" q Q quit program\n"), stdout); fputs(USAGE_COLUMNS, stdout); - for (i = 0; i < ARRAY_SIZE(infos); i++) - fprintf(stdout, " %-5s %s\n", infos[i].name, _(infos[i].help)); + irq_print_columns(stdout); printf(USAGE_MAN_TAIL("irqtop(1)")); exit(EXIT_SUCCESS); @@ -647,14 +254,9 @@ static void parse_args( struct irqtop_ctl *ctl, char **argv) { const char *outarg = NULL; - enum { - ONCE_OPT = CHAR_MAX + 1, - }; static const struct option longopts[] = { {"delay", required_argument, NULL, 'd'}, {"sort", required_argument, NULL, 's'}, - {"once", no_argument, NULL, ONCE_OPT }, - {"json", no_argument, NULL, 'J'}, {"output", required_argument, NULL, 'o'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, @@ -677,15 +279,6 @@ static void parse_args( struct irqtop_ctl *ctl, case 's': out->sort_func = set_sort_func(optarg[0]); break; - case ONCE_OPT: - ctl->run_once = 1; - ctl->request_exit = 1; - break; - case 'J': - out->json = 1; - ctl->run_once = 1; - ctl->request_exit = 1; - break; case 'o': outarg = optarg; break; @@ -702,15 +295,15 @@ static void parse_args( struct irqtop_ctl *ctl, if (!out->ncolumns) { out->columns[out->ncolumns++] = COL_IRQ; out->columns[out->ncolumns++] = COL_TOTAL; - if (!ctl->run_once) - out->columns[out->ncolumns++] = COL_DELTA; + out->columns[out->ncolumns++] = COL_DELTA; out->columns[out->ncolumns++] = COL_NAME; } /* add -o [+] to putput */ if (outarg && string_add_to_idarray(outarg, out->columns, ARRAY_SIZE(out->columns), - &out->ncolumns, column_name_to_id) < 0) + &out->ncolumns, + irq_column_name_to_id) < 0) exit(EXIT_FAILURE); } @@ -730,28 +323,25 @@ int main(int argc, char **argv) parse_args(&ctl, &out, argc, argv); - if (ctl.run_once) - retval = print_irq_data(&out); - else { - is_tty = isatty(STDIN_FILENO); - if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) - fputs(_("terminal setting retrieval"), stdout); + is_tty = isatty(STDIN_FILENO); + if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) + fputs(_("terminal setting retrieval"), stdout); - win = initscr(); - get_terminal_dimension(&ctl.cols, &ctl.rows); - resizeterm(ctl.rows, ctl.cols); - curs_set(0); + win = initscr(); + get_terminal_dimension(&ctl.cols, &ctl.rows); + resizeterm(ctl.rows, ctl.cols); + curs_set(0); - ctl.hostname = xgethostname(); - event_loop(&ctl, &out, win); + ctl.hostname = xgethostname(); + event_loop(&ctl, &out, win); - free_irqinfo(ctl.prev_stat); - free(ctl.hostname); + free_irqinfo(ctl.prev_stat); + free(ctl.hostname); + + if (is_tty) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); + delwin(win); + endwin(); - if (is_tty) - tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); - delwin(win); - endwin(); - } return retval; } From 02f2919e954e54193ba46a891116289a564b9df6 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 14:11:30 +0100 Subject: [PATCH 42/53] irqtop: minor cleanup Signed-off-by: Karel Zak --- sys-utils/irq-common.h | 11 ++++++++++- sys-utils/irqtop.c | 7 ++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/sys-utils/irq-common.h b/sys-utils/irq-common.h index 3a4cd6a03..c0853bd8b 100644 --- a/sys-utils/irq-common.h +++ b/sys-utils/irq-common.h @@ -1,6 +1,12 @@ +#ifndef UTIL_LINUX_H_IRQ_COMMON +#define UTIL_LINUX_H_IRQ_COMMON +#include "c.h" +#include "nls.h" + +/* supported columns */ enum { - COL_IRQ = 1, + COL_IRQ = 0, COL_TOTAL, COL_DELTA, COL_NAME, @@ -28,6 +34,7 @@ typedef int (sort_fp)(const struct irq_info *, const struct irq_info *); #define DEF_SORT_FUNC ((sort_fp *)sort_total) +/* output definition */ struct irq_output { int columns[__COL_COUNT * 2]; size_t ncolumns; @@ -73,3 +80,5 @@ sort_fp *set_sort_func(char key); struct libscols_table *get_scols_table(struct irq_output *out, struct irq_stat *prev, struct irq_stat **xstat); + +#endif /* UTIL_LINUX_H_IRQ_COMMON */ diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 3e8b24bb5..663f93230 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -56,10 +56,8 @@ #include -#include "c.h" #include "closestream.h" #include "monotonic.h" -#include "nls.h" #include "pathnames.h" #include "strutils.h" #include "timeutils.h" @@ -70,6 +68,7 @@ #define MAX_EVENTS 3 +/* top control struct */ struct irqtop_ctl { int cols; int rows; @@ -78,9 +77,9 @@ struct irqtop_ctl { char *hostname; unsigned int request_exit:1; - }; +/* user's input parser */ static void parse_input(struct irqtop_ctl *ctl, struct irq_output *out, char c) { switch (c) { @@ -227,8 +226,6 @@ static void __attribute__((__noreturn__)) usage(void) fputs(USAGE_OPTIONS, stdout); fputs(_(" -d, --delay delay updates\n"), stdout); - fputs(_(" --once only display interrupts once, then exit\n"), stdout); - fputs(_(" -J --json output json, implies displaying once\n"), stdout); fputs(_(" -o --output define which output columns to use (see below)\n"), stdout); fputs(_(" -s, --sort specify sort criteria by character (see below)\n"), stdout); fputs(USAGE_SEPARATOR, stdout); From 9c0740fc644c338967d25f8810a4c07871817d73 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 14:21:46 +0100 Subject: [PATCH 43/53] irqtop: move WINDOW back to control struct Signed-off-by: Karel Zak --- sys-utils/irq-common.c | 2 +- sys-utils/irqtop.c | 34 +++++++++++++++++++--------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/sys-utils/irq-common.c b/sys-utils/irq-common.c index 33faba43c..3c7871deb 100644 --- a/sys-utils/irq-common.c +++ b/sys-utils/irq-common.c @@ -56,7 +56,7 @@ static const struct colinfo infos[] = { [COL_NAME] = {"NAME", 0.70, SCOLS_FL_TRUNC, N_("name"), SCOLS_JSON_STRING}, }; -int irq_column_name_to_id(char const *const name, size_t const namesz) +int irq_column_name_to_id(const char *name, size_t namesz) { size_t i; diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 663f93230..c39650649 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -70,11 +70,13 @@ /* top control struct */ struct irqtop_ctl { - int cols; - int rows; + WINDOW *win; + int cols; + int rows; + char *hostname; + struct itimerspec timer; - struct irq_stat *prev_stat; - char *hostname; + struct irq_stat *prev_stat; unsigned int request_exit:1; }; @@ -102,7 +104,7 @@ static void parse_input(struct irqtop_ctl *ctl, struct irq_output *out, char c) } } -static int update_screen(struct irqtop_ctl *ctl, struct irq_output *out, WINDOW *win) +static int update_screen(struct irqtop_ctl *ctl, struct irq_output *out) { struct libscols_table *table; struct irq_stat *stat; @@ -118,11 +120,11 @@ static int update_screen(struct irqtop_ctl *ctl, struct irq_output *out, WINDOW /* header in interactive mode */ move(0, 0); strtime_iso(&now, ISO_TIMESTAMP, timestr, sizeof(timestr)); - wprintw(win, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), + wprintw(ctl->win, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), stat->total_irq, stat->delta_irq, ctl->hostname, timestr); scols_print_table_to_string(table, &data); - wprintw(win, "%s", data); + wprintw(ctl->win, "%s", data); free(data); /* clean up */ @@ -133,7 +135,7 @@ static int update_screen(struct irqtop_ctl *ctl, struct irq_output *out, WINDOW return 0; } -static int event_loop(struct irqtop_ctl *ctl, struct irq_output *out, WINDOW *win) +static int event_loop(struct irqtop_ctl *ctl, struct irq_output *out) { int efd, sfd, tfd; sigset_t sigmask; @@ -178,7 +180,7 @@ static int event_loop(struct irqtop_ctl *ctl, struct irq_output *out, WINDOW *wi if (epoll_ctl(efd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) != 0) err(EXIT_FAILURE, _("epoll_ctl failed")); - retval |= update_screen(ctl, out, win); + retval |= update_screen(ctl, out); refresh(); while (!ctl->request_exit) { @@ -209,7 +211,7 @@ static int event_loop(struct irqtop_ctl *ctl, struct irq_output *out, WINDOW *wi parse_input(ctl, out, c); } else abort(); - retval |= update_screen(ctl, out, win); + retval |= update_screen(ctl, out); refresh(); } } @@ -306,11 +308,13 @@ static void parse_args( struct irqtop_ctl *ctl, int main(int argc, char **argv) { - WINDOW *win = NULL; int is_tty = 0; int retval = EXIT_SUCCESS; struct termios saved_tty; - struct irq_output out = { .ncolumns = 0, .sort_func = DEF_SORT_FUNC }; + struct irq_output out = { + .ncolumns = 0, + .sort_func = DEF_SORT_FUNC + }; struct irqtop_ctl ctl = { .timer.it_interval = {3, 0}, .timer.it_value = {3, 0} @@ -324,20 +328,20 @@ int main(int argc, char **argv) if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1) fputs(_("terminal setting retrieval"), stdout); - win = initscr(); + ctl.win = initscr(); get_terminal_dimension(&ctl.cols, &ctl.rows); resizeterm(ctl.rows, ctl.cols); curs_set(0); ctl.hostname = xgethostname(); - event_loop(&ctl, &out, win); + event_loop(&ctl, &out); free_irqinfo(ctl.prev_stat); free(ctl.hostname); if (is_tty) tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty); - delwin(win); + delwin(ctl.win); endwin(); return retval; From 77f57b90f7d78d9e5473104fa2e1f91ab32404b0 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 14:49:53 +0100 Subject: [PATCH 44/53] irqtop: cleanup struct irq_stat use Signed-off-by: Karel Zak --- sys-utils/irq-common.c | 51 +++++++++++++++++++++++------------------- sys-utils/irq-common.h | 3 ++- sys-utils/irqtop.c | 4 ++-- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/sys-utils/irq-common.c b/sys-utils/irq-common.c index 3c7871deb..9766899c7 100644 --- a/sys-utils/irq-common.c +++ b/sys-utils/irq-common.c @@ -130,7 +130,7 @@ static struct libscols_table *new_scols_table(struct irq_output *out) } static void add_scols_line(struct irq_output *out, - struct irq_info *stat, + struct irq_info *info, struct libscols_table *table) { size_t i; @@ -147,16 +147,16 @@ static void add_scols_line(struct irq_output *out, switch (get_column_id(out, i)) { case COL_IRQ: - xasprintf(&str, "%s", stat->irq); + xasprintf(&str, "%s", info->irq); break; case COL_TOTAL: - xasprintf(&str, "%ld", stat->total); + xasprintf(&str, "%ld", info->total); break; case COL_DELTA: - xasprintf(&str, "%ld", stat->delta); + xasprintf(&str, "%ld", info->delta); break; case COL_NAME: - xasprintf(&str, "%s", stat->name); + xasprintf(&str, "%s", info->name); break; default: break; @@ -165,10 +165,6 @@ static void add_scols_line(struct irq_output *out, if (str && scols_line_refer_data(line, i, str) != 0) err_oom(); } - - /* FIXME */ - free(stat->irq); - free(stat->name); } static char *remove_repeated_spaces(char *str) @@ -281,10 +277,19 @@ static struct irq_stat *get_irqinfo(void) return NULL; } -void free_irqinfo(struct irq_stat *stat) +void free_irqstat(struct irq_stat *stat) { - if (stat) - free(stat->irq_info); + size_t i; + + if (!stat) + return; + + for (i = 0; i < stat->nr_irq; i++) { + free(stat->irq_info[i].name); + free(stat->irq_info[i].irq); + } + + free(stat->irq_info); free(stat); } @@ -318,10 +323,10 @@ struct libscols_table *get_scols_table(struct irq_output *out, struct irq_stat **xstat) { struct libscols_table *table; - struct irq_info *result, *curr; + struct irq_info *result; struct irq_stat *stat; size_t size; - size_t index; + size_t i; /* the stats */ stat = get_irqinfo(); @@ -334,10 +339,12 @@ struct libscols_table *get_scols_table(struct irq_output *out, if (prev) { stat->delta_irq = 0; - for (index = 0; index < stat->nr_irq; index++) { - result[index].delta = result[index].total - - prev->irq_info[index].total; - stat->delta_irq += result[index].delta; + for (i = 0; i < stat->nr_irq; i++) { + struct irq_info *cur = &result[i]; + struct irq_info *pre = &prev->irq_info[i]; + + cur->delta = cur->total - pre->total; + stat->delta_irq += cur->delta; } } sort_result(out, result, stat->nr_irq); @@ -346,17 +353,15 @@ struct libscols_table *get_scols_table(struct irq_output *out, if (!table) return NULL; - for (index = 0; index < stat->nr_irq; index++) { - curr = result + index; - add_scols_line(out, curr, table); - } + for (i = 0; i < stat->nr_irq; i++) + add_scols_line(out, &result[i], table); free(result); if (xstat) *xstat = stat; else - free_irqinfo(stat); + free_irqstat(stat); return table; } diff --git a/sys-utils/irq-common.h b/sys-utils/irq-common.h index c0853bd8b..1dc760da8 100644 --- a/sys-utils/irq-common.h +++ b/sys-utils/irq-common.h @@ -21,6 +21,7 @@ struct irq_info { unsigned long delta; /* delta count since previous update */ }; + struct irq_stat { unsigned int nr_irq; /* number of irq vector */ unsigned int nr_irq_info; /* number of irq info */ @@ -47,7 +48,7 @@ struct irq_output { }; int irq_column_name_to_id(char const *const name, size_t const namesz); -void free_irqinfo(struct irq_stat *stat); +void free_irqstat(struct irq_stat *stat); void irq_print_columns(FILE *f); diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index c39650649..64ab31bcd 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -130,7 +130,7 @@ static int update_screen(struct irqtop_ctl *ctl, struct irq_output *out) /* clean up */ scols_unref_table(table); if (ctl->prev_stat) - free_irqinfo(ctl->prev_stat); + free_irqstat(ctl->prev_stat); ctl->prev_stat = stat; return 0; } @@ -336,7 +336,7 @@ int main(int argc, char **argv) ctl.hostname = xgethostname(); event_loop(&ctl, &out); - free_irqinfo(ctl.prev_stat); + free_irqstat(ctl.prev_stat); free(ctl.hostname); if (is_tty) From e925cf378f23ba49d831bcf7072670dc8eed36d2 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 14:55:19 +0100 Subject: [PATCH 45/53] irqtop: small cleanup in main() --- sys-utils/irqtop.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 64ab31bcd..8c15712ee 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -309,10 +309,8 @@ static void parse_args( struct irqtop_ctl *ctl, int main(int argc, char **argv) { int is_tty = 0; - int retval = EXIT_SUCCESS; struct termios saved_tty; struct irq_output out = { - .ncolumns = 0, .sort_func = DEF_SORT_FUNC }; struct irqtop_ctl ctl = { @@ -344,5 +342,5 @@ int main(int argc, char **argv) delwin(ctl.win); endwin(); - return retval; + return EXIT_SUCCESS; } From 44e39c9940e3e2fbd66b3ec219d4b164cf03720f Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 15:28:57 +0100 Subject: [PATCH 46/53] irqtop: cleanup sort stuff * user "cmp" in the function names (it does not sort) * keep all in irq-common.c * use column names on command line (e.g. --sort NAME) * hardcode default to sort_result() Signed-off-by: Karel Zak --- sys-utils/irq-common.c | 64 +++++++++++++++++++++++++++++++++++------- sys-utils/irq-common.h | 33 +++------------------- sys-utils/irqtop.c | 25 ++++++----------- 3 files changed, 66 insertions(+), 56 deletions(-) diff --git a/sys-utils/irq-common.c b/sys-utils/irq-common.c index 9766899c7..e0f5c720c 100644 --- a/sys-utils/irq-common.c +++ b/sys-utils/irq-common.c @@ -293,28 +293,72 @@ void free_irqstat(struct irq_stat *stat) free(stat); } +static inline int cmp_name(const struct irq_info *a, + const struct irq_info *b) +{ + return (strcmp(a->name, b->name) > 0) ? 1 : 0; +} + +static inline int cmp_total(const struct irq_info *a, + const struct irq_info *b) +{ + return a->total < b->total; +} + +static inline int cmp_delta(const struct irq_info *a, + const struct irq_info *b) +{ + return a->delta < b->delta; +} + +static inline int cmp_interrupts(const struct irq_info *a, + const struct irq_info *b) +{ + return (strcmp(a->irq, b->irq) > 0) ? 1 : 0; +} + static void sort_result(struct irq_output *out, struct irq_info *result, size_t nmemb) { + irq_cmp_t *func = cmp_total; /* default */ + + if (out->sort_cmp_func) + func = out->sort_cmp_func; + qsort(result, nmemb, sizeof(*result), - (int (*)(const void *, - const void *))out->sort_func); + (int (*)(const void *, const void *)) func); } -sort_fp *set_sort_func(char key) +void set_sort_func_by_name(struct irq_output *out, const char *name) { - switch (key) { + if (strcasecmp(name, "IRQ") == 0) + out->sort_cmp_func = cmp_interrupts; + else if (strcasecmp(name, "TOTAL") == 0) + out->sort_cmp_func = cmp_total; + else if (strcasecmp(name, "DELTA") == 0) + out->sort_cmp_func = cmp_delta; + else if (strcasecmp(name, "NAME") == 0) + out->sort_cmp_func = cmp_name; + else + errx(EXIT_FAILURE, _("unssupported column name to sort output")); +} + +void set_sort_func_by_key(struct irq_output *out, char c) +{ + switch (c) { case 'i': - return sort_interrupts; + out->sort_cmp_func = cmp_interrupts; + break; case 't': - return sort_total; + out->sort_cmp_func = cmp_total; + break; case 'd': - return sort_delta; + out->sort_cmp_func = cmp_delta; + break; case 'n': - return sort_name; - default: - return DEF_SORT_FUNC; + out->sort_cmp_func = cmp_name; + break; } } diff --git a/sys-utils/irq-common.h b/sys-utils/irq-common.h index 1dc760da8..a0646b129 100644 --- a/sys-utils/irq-common.h +++ b/sys-utils/irq-common.h @@ -31,16 +31,14 @@ struct irq_stat { unsigned long delta_irq; /* delta irqs */ }; -typedef int (sort_fp)(const struct irq_info *, const struct irq_info *); - -#define DEF_SORT_FUNC ((sort_fp *)sort_total) +typedef int (irq_cmp_t)(const struct irq_info *, const struct irq_info *); /* output definition */ struct irq_output { int columns[__COL_COUNT * 2]; size_t ncolumns; - sort_fp *sort_func; + irq_cmp_t *sort_cmp_func; unsigned int json:1, @@ -52,31 +50,8 @@ void free_irqstat(struct irq_stat *stat); void irq_print_columns(FILE *f); -static inline int sort_name(const struct irq_info *a, - const struct irq_info *b) -{ - return (strcmp(a->name, b->name) > 0) ? 1 : 0; -} - -static inline int sort_total(const struct irq_info *a, - const struct irq_info *b) -{ - return a->total < b->total; -} - -static inline int sort_delta(const struct irq_info *a, - const struct irq_info *b) -{ - return a->delta < b->delta; -} - -static inline int sort_interrupts(const struct irq_info *a, - const struct irq_info *b) -{ - return (strcmp(a->irq, b->irq) > 0) ? 1 : 0; -} - -sort_fp *set_sort_func(char key); +void set_sort_func_by_name(struct irq_output *out, const char *name); +void set_sort_func_by_key(struct irq_output *out, const char c); struct libscols_table *get_scols_table(struct irq_output *out, struct irq_stat *prev, diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 8c15712ee..2739e4e35 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -85,22 +85,13 @@ struct irqtop_ctl { static void parse_input(struct irqtop_ctl *ctl, struct irq_output *out, char c) { switch (c) { - case 'i': - out->sort_func = sort_interrupts; - break; - case 't': - out->sort_func = sort_total; - break; - case 'd': - out->sort_func = sort_delta; - break; - case 'n': - out->sort_func = sort_name; - break; case 'q': case 'Q': ctl->request_exit = 1; break; + default: + set_sort_func_by_key(out, c); + break; } } @@ -228,8 +219,8 @@ static void __attribute__((__noreturn__)) usage(void) fputs(USAGE_OPTIONS, stdout); fputs(_(" -d, --delay delay updates\n"), stdout); - fputs(_(" -o --output define which output columns to use (see below)\n"), stdout); - fputs(_(" -s, --sort specify sort criteria by character (see below)\n"), stdout); + fputs(_(" -o, --output define which output columns to use\n"), stdout); + fputs(_(" -s, --sort specify sort column\n"), stdout); fputs(USAGE_SEPARATOR, stdout); printf(USAGE_HELP_OPTIONS(22)); @@ -263,7 +254,7 @@ static void parse_args( struct irqtop_ctl *ctl, }; int o; - while ((o = getopt_long(argc, argv, "d:o:s:hJV", longopts, NULL)) != -1) { + while ((o = getopt_long(argc, argv, "d:o:s:hV", longopts, NULL)) != -1) { switch (o) { case 'd': { @@ -276,7 +267,7 @@ static void parse_args( struct irqtop_ctl *ctl, } break; case 's': - out->sort_func = set_sort_func(optarg[0]); + set_sort_func_by_name(out, optarg); break; case 'o': outarg = optarg; @@ -311,7 +302,7 @@ int main(int argc, char **argv) int is_tty = 0; struct termios saved_tty; struct irq_output out = { - .sort_func = DEF_SORT_FUNC + .ncolumns = 0 }; struct irqtop_ctl ctl = { .timer.it_interval = {3, 0}, From cb267d2a295beac6c4b8187a41785c6a8dbf5756 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 16:16:38 +0100 Subject: [PATCH 47/53] irqtop: cleanup man page Signed-off-by: Karel Zak --- sys-utils/irqtop.1 | 47 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/sys-utils/irqtop.1 b/sys-utils/irqtop.1 index 42ca91495..db9756ba7 100644 --- a/sys-utils/irqtop.1 +++ b/sys-utils/irqtop.1 @@ -9,33 +9,30 @@ Display kernel interrupt counter information in .BR top (1) style view. .PP -The default output is subject to change. So whenever -possible, you should avoid using default outputs in your scripts. Always explicitly define expected columns -by using \fB\-\-columns\fR. +The default output is subject to change. So whenever possible, +you should avoid using default outputs in your scripts. Always +explicitly define expected columns by using \fB\-\-output\fR. .SH OPTIONS .TP -.BR \-d ,\ \-\-delay\ \c -.I seconds +.BR \-o , " \-\-output " \fIlist\fP +Specify which output columns to print. Use \fB\-\-help\fR to get a list of all supported columns. +The default list of columns may be extended if list is specified in the format +list. +.TP +.BR \-d , " \-\-delay " \fIseconds\fP Update interrupt output every .I seconds intervals. .TP -.BR \-J ,\ \-\-json -Use JSON output format. It's strongly recommended to use --columns to keep -the output stable. +.BR \-s , " \-\-sort " \fIcolumn\fP +Specify sort criteria by column name. See \fB\-\-help\fR output to get column +names. The sort criteria may be changes in interactive mode. .TP -.BR \-o ,\ \-\-once -Collect interrupt information and output it. This mode does not use -ncurses, so the output can easily be redirected to a pipe, file, or such. +.BR \-V ", " \-\-version +Display version information and exit. .TP -.BR \-s ,\ \-\-sort\ \c -.I char -Specify sort criteria by character. Pressing these characters in -interactive mode will make the ncurses output to be ordered accordingly. -.PP -.RS -.PD 0 -.\" Sort key commands are in same order as default output fields +.BR \-h ,\ \-\-help +Display help text and exit. +.SH COMMAND IN INTERACTIE MODE .TP .B i sort by short irq name or number field @@ -51,14 +48,6 @@ sort by long descriptive name field .TP .B q Q stop updates and exit program -.PD -.RE -.TP -.BR \-V ", " \-\-version -Display version information and exit. -.TP -.BR \-h ,\ \-\-help -Display help text and exit. .SH AUTHORS .MT pizhenwei@\:bytedance.com Zhenwei Pi @@ -67,6 +56,10 @@ Zhenwei Pi .MT kerolasa@\:iki.fi Sami Kerola .ME +.br +.MT kzak@\:redhat.com +Karel Zak +.ME .SH AVAILABILITY The example command is part of the util-linux package and is available from .UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ From a0f62b0b2002c85809933e23e13b885e510a3dbb Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 16:21:10 +0100 Subject: [PATCH 48/53] lsirq: add new command Signed-off-by: Karel Zak --- .gitignore | 1 + configure.ac | 14 +++++ sys-utils/Makemodule.am | 10 ++++ sys-utils/irq-common.c | 7 ++- sys-utils/irq-common.h | 2 +- sys-utils/irqtop.c | 2 +- sys-utils/lsirq.1 | 47 +++++++++++++++ sys-utils/lsirq.c | 128 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 sys-utils/lsirq.1 create mode 100644 sys-utils/lsirq.c diff --git a/.gitignore b/.gitignore index 0cd0b9078..a6f379146 100644 --- a/.gitignore +++ b/.gitignore @@ -115,6 +115,7 @@ ylwrap /losetup /lsblk /lsipc +/lsirq /lscpu /lslocks /lslogins diff --git a/configure.ac b/configure.ac index 76c23993e..28852e6d9 100644 --- a/configure.ac +++ b/configure.ac @@ -1768,14 +1768,28 @@ AC_ARG_ENABLE([ipcs], UL_BUILD_INIT([ipcs]) AM_CONDITIONAL([BUILD_IPCS], [test "x$build_ipcs" = xyes]) + AC_ARG_ENABLE([irqtop], AS_HELP_STRING([--disable-irqtop], [do not build irqtop]), [], [UL_DEFAULT_ENABLE([irqtop], [yes])] ) UL_BUILD_INIT([irqtop]) UL_REQUIRES_LINUX([irqtop]) +UL_REQUIRES_BUILD([irqtop], [libsmartcols]) +UL_REQUIRES_HAVE([irqtop], [open_memstream], [open_memstream function]) +UL_REQUIRES_HAVE([irqtop], [ncursesw,slang,ncurses], [ncursesw, ncurses or slang library]) AM_CONDITIONAL([BUILD_IRQTOP], [test "x$build_irqtop" = xyes]) +AC_ARG_ENABLE([lsirq], + AS_HELP_STRING([--disable-lsirq], [do not build lsirq]), + [], [UL_DEFAULT_ENABLE([lsirq], [yes])] +) +UL_BUILD_INIT([lsirq]) +UL_REQUIRES_LINUX([lsirq]) +UL_REQUIRES_BUILD([lsirq], [libsmartcols]) +AM_CONDITIONAL([BUILD_LSIRQ], [test "x$build_lsirq" = xyes]) + + UL_BUILD_INIT([choom], [check]) UL_REQUIRES_LINUX([choom]) AM_CONDITIONAL([BUILD_CHOOM], [test "x$build_choom" = xyes]) diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index 5a9e8c692..daeb34f89 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -61,6 +61,16 @@ irqtop_LDADD = $(LDADD) libcommon.la $(NCURSES_LIBS) $(REALTIME_LIBS) libsmartco irqtop_CFLAGS = $(AM_CFLAGS) $(NCURSES_CFLAGS) -I$(ul_libsmartcols_incdir) endif +if BUILD_LSIRQ +usrbin_exec_PROGRAMS += lsirq +dist_man_MANS += sys-utils/lsirq.1 +lsirq_SOURCES = sys-utils/lsirq.c \ + sys-utils/irq-common.c \ + sys-utils/irq-common.h +lsirq_LDADD = $(LDADD) libcommon.la libsmartcols.la +lsirq_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) +endif + if BUILD_LSIPC usrbin_exec_PROGRAMS += lsipc dist_man_MANS += sys-utils/lsipc.1 diff --git a/sys-utils/irq-common.c b/sys-utils/irq-common.c index e0f5c720c..f3dc49b43 100644 --- a/sys-utils/irq-common.c +++ b/sys-utils/irq-common.c @@ -85,12 +85,15 @@ static inline const struct colinfo *get_column_info( return &infos[get_column_id(out, num)]; } -void irq_print_columns(FILE *f) +void irq_print_columns(FILE *f, int nodelta) { size_t i; - for (i = 0; i < ARRAY_SIZE(infos); i++) + for (i = 0; i < ARRAY_SIZE(infos); i++) { + if (nodelta && i == COL_DELTA) + continue; fprintf(f, " %-5s %s\n", infos[i].name, _(infos[i].help)); + } } static struct libscols_table *new_scols_table(struct irq_output *out) diff --git a/sys-utils/irq-common.h b/sys-utils/irq-common.h index a0646b129..f5884b711 100644 --- a/sys-utils/irq-common.h +++ b/sys-utils/irq-common.h @@ -48,7 +48,7 @@ struct irq_output { int irq_column_name_to_id(char const *const name, size_t const namesz); void free_irqstat(struct irq_stat *stat); -void irq_print_columns(FILE *f); +void irq_print_columns(FILE *f, int nodelta); void set_sort_func_by_name(struct irq_output *out, const char *name); void set_sort_func_by_key(struct irq_output *out, const char c); diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index 2739e4e35..107bcdf6d 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -232,7 +232,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" q Q quit program\n"), stdout); fputs(USAGE_COLUMNS, stdout); - irq_print_columns(stdout); + irq_print_columns(stdout, 0); printf(USAGE_MAN_TAIL("irqtop(1)")); exit(EXIT_SUCCESS); diff --git a/sys-utils/lsirq.1 b/sys-utils/lsirq.1 new file mode 100644 index 000000000..405064374 --- /dev/null +++ b/sys-utils/lsirq.1 @@ -0,0 +1,47 @@ +.TH IRQTOP "1" "February 2020" "util-linux" "User Commands" +.SH NAME +lsirq \- utility to display kernel interrupt information +.SH SYNOPSIS +.B lsirq +[options] +.SH DESCRIPTION +Display kernel interrupt counter information. +.PP +The default output is subject to change. So whenever possible, +you should avoid using default outputs in your scripts. Always +explicitly define expected columns by using \fB\-\-output\fR. +.SH OPTIONS +.TP +.BR \-o , " \-\-output " \fIlist\fP +Specify which output columns to print. Use \fB\-\-help\fR to get a list of all supported columns. +The default list of columns may be extended if list is specified in the format +list. +.TP +.BR \-s , " \-\-sort " \fIcolumn\fP +Specify sort criteria by column name. See \fB\-\-help\fR output to get column +names. +.TP +.BR \-J , " \-\-json +Update interrupt output every +.TP +.BR \-V ", " \-\-version +Display version information and exit. +.TP +.BR \-h ,\ \-\-help +Display help text and exit. +.SH AUTHORS +.MT pizhenwei@\:bytedance.com +Zhenwei Pi +.ME +.br +.MT kerolasa@\:iki.fi +Sami Kerola +.ME +.br +.MT kzak@\:redhat.com +Karel Zak +.ME +.SH AVAILABILITY +The example command is part of the util-linux package and is available from +.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ +Linux Kernel Archive +.UE . diff --git a/sys-utils/lsirq.c b/sys-utils/lsirq.c new file mode 100644 index 000000000..7853af0ee --- /dev/null +++ b/sys-utils/lsirq.c @@ -0,0 +1,128 @@ +/* + * lsirq - utility to display kernel interrupt information. + * + * Copyright (C) 2019 zhenwei pi + * Copyright (C) 2020 Karel Zak + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "closestream.h" +#include "strutils.h" +#include "xalloc.h" + +#include "irq-common.h" + +static int print_irq_data(struct irq_output *out) +{ + struct libscols_table *table; + + table = get_scols_table(out, NULL, NULL); + if (!table) + return -1; + + scols_print_table(table); + scols_unref_table(table); + return 0; +} + +static void __attribute__((__noreturn__)) usage(void) +{ + fputs(USAGE_HEADER, stdout); + printf(_(" %s [options]\n"), program_invocation_short_name); + fputs(USAGE_SEPARATOR, stdout); + + puts(_("Utility to display kernel interrupt information.")); + + fputs(USAGE_OPTIONS, stdout); + fputs(_(" -J, --json use JSON output format\n"), stdout); + fputs(_(" -o, --output define which output columns to use\n"), stdout); + fputs(_(" -s, --sort specify sort column\n"), stdout); + fputs(USAGE_SEPARATOR, stdout); + printf(USAGE_HELP_OPTIONS(22)); + + fputs(USAGE_COLUMNS, stdout); + irq_print_columns(stdout, 1); + + printf(USAGE_MAN_TAIL("lsirq(1)")); + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + struct irq_output out = { + .ncolumns = 0 + }; + static const struct option longopts[] = { + {"sort", required_argument, NULL, 's'}, + {"output", required_argument, NULL, 'o'}, + {"json", no_argument, NULL, 'J'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} + }; + int c; + const char *outarg = NULL; + + setlocale(LC_ALL, ""); + + while ((c = getopt_long(argc, argv, "o:s:hJV", longopts, NULL)) != -1) { + switch (c) { + case 'J': + out.json = 1; + break; + case 'o': + outarg = optarg; + break; + case 's': + set_sort_func_by_name(&out, optarg); + break; + case 'V': + print_version(EXIT_SUCCESS); + case 'h': + usage(); + default: + errtryhelp(EXIT_FAILURE); + } + } + + /* default */ + if (!out.ncolumns) { + out.columns[out.ncolumns++] = COL_IRQ; + out.columns[out.ncolumns++] = COL_TOTAL; + out.columns[out.ncolumns++] = COL_NAME; + } + + /* add -o [+] to putput */ + if (outarg && string_add_to_idarray(outarg, out.columns, + ARRAY_SIZE(out.columns), + &out.ncolumns, + irq_column_name_to_id) < 0) + exit(EXIT_FAILURE); + + return print_irq_data(&out) == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} From dd52c4fa011d5fe5c4cd82908ec5fee18843b06e Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 16:26:06 +0100 Subject: [PATCH 49/53] lsirq: add -n option Signed-off-by: Karel Zak --- sys-utils/lsirq.1 | 5 ++++- sys-utils/lsirq.c | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sys-utils/lsirq.1 b/sys-utils/lsirq.1 index 405064374..7e9117d32 100644 --- a/sys-utils/lsirq.1 +++ b/sys-utils/lsirq.1 @@ -12,6 +12,9 @@ you should avoid using default outputs in your scripts. Always explicitly define expected columns by using \fB\-\-output\fR. .SH OPTIONS .TP +.BR \-n , " \-\-noheadings +Don't print headings. +.TP .BR \-o , " \-\-output " \fIlist\fP Specify which output columns to print. Use \fB\-\-help\fR to get a list of all supported columns. The default list of columns may be extended if list is specified in the format +list. @@ -21,7 +24,7 @@ Specify sort criteria by column name. See \fB\-\-help\fR output to get column names. .TP .BR \-J , " \-\-json -Update interrupt output every +Use JSON output format. .TP .BR \-V ", " \-\-version Display version information and exit. diff --git a/sys-utils/lsirq.c b/sys-utils/lsirq.c index 7853af0ee..35e726cd1 100644 --- a/sys-utils/lsirq.c +++ b/sys-utils/lsirq.c @@ -60,6 +60,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(USAGE_OPTIONS, stdout); fputs(_(" -J, --json use JSON output format\n"), stdout); + fputs(_(" -n, --noheadings don't print headings\n"), stdout); fputs(_(" -o, --output define which output columns to use\n"), stdout); fputs(_(" -s, --sort specify sort column\n"), stdout); fputs(USAGE_SEPARATOR, stdout); @@ -79,6 +80,7 @@ int main(int argc, char **argv) }; static const struct option longopts[] = { {"sort", required_argument, NULL, 's'}, + {"noheadings", no_argument, NULL, 'n'}, {"output", required_argument, NULL, 'o'}, {"json", no_argument, NULL, 'J'}, {"help", no_argument, NULL, 'h'}, @@ -90,11 +92,14 @@ int main(int argc, char **argv) setlocale(LC_ALL, ""); - while ((c = getopt_long(argc, argv, "o:s:hJV", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "no:s:hJV", longopts, NULL)) != -1) { switch (c) { case 'J': out.json = 1; break; + case 'n': + out.no_headings = 1; + break; case 'o': outarg = optarg; break; From 5a20c0dec3ce4c15f95863ce3529621c9394c1d3 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 6 Mar 2020 16:34:51 +0100 Subject: [PATCH 50/53] lsirq: add -P option Signed-off-by: Karel Zak --- sys-utils/irq-common.c | 1 + sys-utils/irq-common.h | 5 +++-- sys-utils/lsirq.1 | 4 ++++ sys-utils/lsirq.c | 7 ++++++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/sys-utils/irq-common.c b/sys-utils/irq-common.c index f3dc49b43..2992d3c7d 100644 --- a/sys-utils/irq-common.c +++ b/sys-utils/irq-common.c @@ -108,6 +108,7 @@ static struct libscols_table *new_scols_table(struct irq_output *out) } scols_table_enable_json(table, out->json); scols_table_enable_noheadings(table, out->no_headings); + scols_table_enable_export(table, out->pairs); if (out->json) scols_table_set_name(table, _("interrupts")); diff --git a/sys-utils/irq-common.h b/sys-utils/irq-common.h index f5884b711..9515daf49 100644 --- a/sys-utils/irq-common.h +++ b/sys-utils/irq-common.h @@ -41,8 +41,9 @@ struct irq_output { irq_cmp_t *sort_cmp_func; unsigned int - json:1, - no_headings:1; + json:1, /* JSON output */ + pairs:1, /* export, NAME="value" aoutput */ + no_headings:1; /* don't print header */ }; int irq_column_name_to_id(char const *const name, size_t const namesz); diff --git a/sys-utils/lsirq.1 b/sys-utils/lsirq.1 index 7e9117d32..ed48c541e 100644 --- a/sys-utils/lsirq.1 +++ b/sys-utils/lsirq.1 @@ -26,6 +26,10 @@ names. .BR \-J , " \-\-json Use JSON output format. .TP +.BR \-P , " \-\-pairs +Produce output in the form of key="value" pairs. All potentially unsafe characters +are hex-escaped (\\x). +.TP .BR \-V ", " \-\-version Display version information and exit. .TP diff --git a/sys-utils/lsirq.c b/sys-utils/lsirq.c index 35e726cd1..3d7e26abb 100644 --- a/sys-utils/lsirq.c +++ b/sys-utils/lsirq.c @@ -60,6 +60,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(USAGE_OPTIONS, stdout); fputs(_(" -J, --json use JSON output format\n"), stdout); + fputs(_(" -P, --pairs use key=\"value\" output format\n"), stdout); fputs(_(" -n, --noheadings don't print headings\n"), stdout); fputs(_(" -o, --output define which output columns to use\n"), stdout); fputs(_(" -s, --sort specify sort column\n"), stdout); @@ -83,6 +84,7 @@ int main(int argc, char **argv) {"noheadings", no_argument, NULL, 'n'}, {"output", required_argument, NULL, 'o'}, {"json", no_argument, NULL, 'J'}, + {"pairs", no_argument, NULL, 'P'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} @@ -92,11 +94,14 @@ int main(int argc, char **argv) setlocale(LC_ALL, ""); - while ((c = getopt_long(argc, argv, "no:s:hJV", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "no:s:hJPV", longopts, NULL)) != -1) { switch (c) { case 'J': out.json = 1; break; + case 'P': + out.pairs = 1; + break; case 'n': out.no_headings = 1; break; From 723ec12457d44d78e2ee6d7ca4f522aaf4f74353 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Fri, 6 Mar 2020 19:49:34 +0000 Subject: [PATCH 51/53] bash-completion: update irqtop and lsirq completions Signed-off-by: Sami Kerola --- bash-completion/Makemodule.am | 3 +++ bash-completion/irqtop | 8 +++----- bash-completion/lsirq | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 bash-completion/lsirq diff --git a/bash-completion/Makemodule.am b/bash-completion/Makemodule.am index 0a813f8d5..b80c23f7b 100644 --- a/bash-completion/Makemodule.am +++ b/bash-completion/Makemodule.am @@ -60,6 +60,9 @@ endif if BUILD_LSIPC dist_bashcompletion_DATA += bash-completion/lsipc endif +if BUILD_LSIRQ +dist_bashcompletion_DATA += bash-completion/lsirq +endif if BUILD_LSNS dist_bashcompletion_DATA += bash-completion/lsns endif diff --git a/bash-completion/irqtop b/bash-completion/irqtop index b6a71454d..613732559 100644 --- a/bash-completion/irqtop +++ b/bash-completion/irqtop @@ -10,10 +10,10 @@ _irqtop_module() return 0 ;; '-s'|'--sort') - COMPREPLY=( $(compgen -W "i t d n" -- $cur) ) + COMPREPLY=( $(compgen -W "irq total delta name" -- $cur) ) return 0 ;; - '--columns') + '-o'|'--output') local prefix realcur OUTPUT realcur="${cur##*,}" prefix="${cur%$realcur}" @@ -31,10 +31,8 @@ _irqtop_module() ;; esac OPTS=" --delay - --once - --json --sort - --columns + --output --help --version" COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) diff --git a/bash-completion/lsirq b/bash-completion/lsirq new file mode 100644 index 000000000..b644559e5 --- /dev/null +++ b/bash-completion/lsirq @@ -0,0 +1,38 @@ +_lsirq_module() +{ + local cur prev OPTS + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-o'|'--output') + local prefix realcur OUTPUT + realcur="${cur##*,}" + prefix="${cur%$realcur}" + for WORD in "IRQ TOTAL NAME"; do + if ! [[ $prefix == *"$WORD"* ]]; then + OUTPUT="$WORD ${OUTPUT:-""}" + fi + done + compopt -o nospace + COMPREPLY=( $(compgen -P "$prefix" -W "$OUTPUT" -S ',' -- $realcur) ) + return 0 + ;; + '-s'|'--sort') + COMPREPLY=( $(compgen -W "irq total name" -- $cur) ) + return 0 + ;; + '-h'|'--help'|'-V'|'--version') + return 0 + ;; + esac + OPTS=" --json + --pairs + --output + --sort + --help + --version" + COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) + return 0 +} +complete -F _lsirq_module lsirq From 968a50b30ebd75c56930b6a46e4a75a17476c433 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Fri, 6 Mar 2020 20:14:06 +0000 Subject: [PATCH 52/53] lsirq: mark --json and --pairs options mutually exclusive Before this change --pairs option would always win over --json. That is unnecessarily confusing, it is much better to disallow combination that does not make sense. Notice that the --noheadings in combination with --json or --pairs will not cause any effect. In strictest possible understanding --noheadings should also be exclusive. Looking from more relaxed point of view neither --json nor --pairs has heading, so excluding is not necessary because --noheading is happening already. Signed-off-by: Sami Kerola --- sys-utils/lsirq.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sys-utils/lsirq.c b/sys-utils/lsirq.c index 3d7e26abb..33fe48d25 100644 --- a/sys-utils/lsirq.c +++ b/sys-utils/lsirq.c @@ -32,6 +32,7 @@ #include #include "closestream.h" +#include "optutils.h" #include "strutils.h" #include "xalloc.h" @@ -91,10 +92,17 @@ int main(int argc, char **argv) }; int c; const char *outarg = NULL; + static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ + {'J', 'P'}, + {0} + }; + int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; setlocale(LC_ALL, ""); while ((c = getopt_long(argc, argv, "no:s:hJPV", longopts, NULL)) != -1) { + err_exclusive_options(c, longopts, excl, excl_st); + switch (c) { case 'J': out.json = 1; From fef6fa7d4059ad9e01b651e2e658bf43bc1ab2c3 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Fri, 6 Mar 2020 20:34:06 +0000 Subject: [PATCH 53/53] docs: fix spacing in irqtop and lsirq manual pages And convert font changing macros to ones used in howto-man-page.txt Signed-off-by: Sami Kerola --- sys-utils/irqtop.1 | 26 +++++++++++++++++--------- sys-utils/lsirq.1 | 21 +++++++++++++-------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/sys-utils/irqtop.1 b/sys-utils/irqtop.1 index db9756ba7..bbb637e05 100644 --- a/sys-utils/irqtop.1 +++ b/sys-utils/irqtop.1 @@ -9,14 +9,18 @@ Display kernel interrupt counter information in .BR top (1) style view. .PP -The default output is subject to change. So whenever possible, -you should avoid using default outputs in your scripts. Always -explicitly define expected columns by using \fB\-\-output\fR. +The default output is subject to change. So whenever possible, you should +avoid using default outputs in your scripts. Always explicitly define +expected columns by using +.BR \-\-output . .SH OPTIONS .TP .BR \-o , " \-\-output " \fIlist\fP -Specify which output columns to print. Use \fB\-\-help\fR to get a list of all supported columns. -The default list of columns may be extended if list is specified in the format +list. +Specify which output columns to print. Use +.B \-\-help +to get a list of all supported columns. The default list of columns may be +extended if list is specified in the format +.IR +list . .TP .BR \-d , " \-\-delay " \fIseconds\fP Update interrupt output every @@ -24,15 +28,18 @@ Update interrupt output every intervals. .TP .BR \-s , " \-\-sort " \fIcolumn\fP -Specify sort criteria by column name. See \fB\-\-help\fR output to get column -names. The sort criteria may be changes in interactive mode. +Specify sort criteria by column name. See +.B \-\-help +output to get column names. The sort criteria may be changes in interactive +mode. .TP .BR \-V ", " \-\-version Display version information and exit. .TP .BR \-h ,\ \-\-help Display help text and exit. -.SH COMMAND IN INTERACTIE MODE +.SH INTERACTIVE MODE KEY COMMANDS +.PD 0 .TP .B i sort by short irq name or number field @@ -48,6 +55,7 @@ sort by long descriptive name field .TP .B q Q stop updates and exit program +.PD 1 .SH AUTHORS .MT pizhenwei@\:bytedance.com Zhenwei Pi @@ -57,7 +65,7 @@ Zhenwei Pi Sami Kerola .ME .br -.MT kzak@\:redhat.com +.MT kzak@\:redhat.com Karel Zak .ME .SH AVAILABILITY diff --git a/sys-utils/lsirq.1 b/sys-utils/lsirq.1 index ed48c541e..767077123 100644 --- a/sys-utils/lsirq.1 +++ b/sys-utils/lsirq.1 @@ -7,21 +7,26 @@ lsirq \- utility to display kernel interrupt information .SH DESCRIPTION Display kernel interrupt counter information. .PP -The default output is subject to change. So whenever possible, -you should avoid using default outputs in your scripts. Always -explicitly define expected columns by using \fB\-\-output\fR. +The default output is subject to change. So whenever possible, you should +avoid using default outputs in your scripts. Always explicitly define +expected columns by using +.BR \-\-output . .SH OPTIONS .TP .BR \-n , " \-\-noheadings Don't print headings. .TP .BR \-o , " \-\-output " \fIlist\fP -Specify which output columns to print. Use \fB\-\-help\fR to get a list of all supported columns. -The default list of columns may be extended if list is specified in the format +list. +Specify which output columns to print. Use +.B \-\-help +to get a list of all supported columns. The default list of columns may be +extended if list is specified in the format +.IR +list . .TP .BR \-s , " \-\-sort " \fIcolumn\fP -Specify sort criteria by column name. See \fB\-\-help\fR output to get column -names. +Specify sort criteria by column name. See +.B \-\-help +output to get column names. .TP .BR \-J , " \-\-json Use JSON output format. @@ -44,7 +49,7 @@ Zhenwei Pi Sami Kerola .ME .br -.MT kzak@\:redhat.com +.MT kzak@\:redhat.com Karel Zak .ME .SH AVAILABILITY