From: Karel Zak Date: Wed, 24 Feb 2021 19:10:56 +0000 (+0100) Subject: irqtop: add per-cpu stats X-Git-Tag: v2.37-rc1~92 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a23aecc1bfcde8b66eff3c261f1d972fcbe06d62;p=thirdparty%2Futil-linux.git irqtop: add per-cpu stats irqtop | total: 1245107402 delta: 7394 | ws.net.home | 2021-02-24 20:11:09+01:00 cpu0 cpu1 cpu2 cpu3 cpu4 cpu5 cpu6 cpu7 %irq: 12.9 13.0 12.8 11.9 12.4 13.4 11.6 12.0 %delta: 13.7 9.7 22.6 7.9 9.5 17.5 8.3 10.8 IRQ TOTAL DELTA NAME LOC 989162414 6111 Local timer interrupts TLB 100492740 67 TLB shootdowns CAL 95058001 321 Function call interrupts 42 23893801 241 IR-PCI-MSI 1048576-edge nvidia 30 20209392 494 IR-PCI-MSI 327680-edge xhci_hcd RES 12996335 86 Rescheduling interrupts 29 1354219 4 IR-PCI-MSI 512000-edge ahci[0000:00:1f.2] 41 682653 31 IR-PCI-MSI 409600-edge eno1 ... Signed-off-by: Karel Zak --- diff --git a/sys-utils/irq-common.c b/sys-utils/irq-common.c index dac2e546ea..79dce481e5 100644 --- a/sys-utils/irq-common.c +++ b/sys-utils/irq-common.c @@ -143,7 +143,7 @@ static struct libscols_table *new_scols_table(struct irq_output *out) scols_table_enable_export(table, out->pairs); if (out->json) - scols_table_set_name(table, _("interrupts")); + scols_table_set_name(table, "interrupts"); for (i = 0; i < out->ncolumns; i++) { const struct colinfo *col = get_column_info(out, i); @@ -262,10 +262,13 @@ static struct irq_stat *get_irqinfo(int softirq) stat->nr_active_cpu++; } + stat->cpus = xcalloc(stat->nr_active_cpu, sizeof(struct irq_cpu)); + /* parse each line of _PATH_PROC_INTERRUPTS */ while (getline(&line, &len, irqfile) >= 0) { unsigned long count; - int index, length; + size_t index; + int length; tmp = strchr(line, ':'); if (!tmp) @@ -281,9 +284,13 @@ static struct irq_stat *get_irqinfo(int softirq) tmp += 1; for (index = 0; (index < stat->nr_active_cpu) && (tmp - line < length); index++) { + struct irq_cpu *cpu = &stat->cpus[index]; + sscanf(tmp, " %10lu", &count); curr->total += count; + cpu->total += count; stat->total_irq += count; + tmp += 11; } @@ -316,6 +323,7 @@ static struct irq_stat *get_irqinfo(int softirq) fclose(irqfile); free_stat: free(stat->irq_info); + free(stat->cpus); free(stat); free(line); return NULL; @@ -334,6 +342,7 @@ void free_irqstat(struct irq_stat *stat) } free(stat->irq_info); + free(stat->cpus); free(stat); } @@ -406,6 +415,94 @@ void set_sort_func_by_key(struct irq_output *out, char c) } } +struct libscols_table *get_scols_cpus_table(struct irq_output *out, + struct irq_stat *prev, + struct irq_stat *curr) +{ + struct libscols_table *table; + struct libscols_column *cl; + struct libscols_line *ln; + char colname[sizeof(stringify_value(LONG_MAX))]; + size_t i; + + if (prev) { + for (i = 0; i < curr->nr_active_cpu; i++) { + struct irq_cpu *pre = &prev->cpus[i]; + struct irq_cpu *cur = &curr->cpus[i]; + + cur->delta = cur->total - pre->total; + } + } + + 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); + scols_table_enable_export(table, out->pairs); + + if (out->json) + scols_table_set_name(table, _("cpu-interrupts")); + else + scols_table_new_column(table, "", 0, SCOLS_FL_RIGHT); + + for (i = 0; i < curr->nr_active_cpu; i++) { + snprintf(colname, sizeof(colname), "cpu%zu", i); + cl = scols_table_new_column(table, colname, 0, SCOLS_FL_RIGHT); + if (cl == NULL) { + warnx(_("failed to initialize output column")); + goto err; + } + if (out->json) + scols_column_set_json_type(cl, SCOLS_JSON_STRING); + } + + /* per cpu % of total */ + ln = scols_table_new_line(table, NULL); + if (!ln) { + warn(_("failed to add line to output")); + goto err; + } + if (!out->json) + scols_line_set_data(ln, 0, "%irq:"); + + for (i = 0; i < curr->nr_active_cpu; i++) { + struct irq_cpu *cpu = &curr->cpus[i]; + char *str; + + xasprintf(&str, "%0.1f", (double)((long double) cpu->total / (long double) curr->total_irq * 100.0)); + if (str && scols_line_refer_data(ln, i + 1, str) != 0) + goto err; + } + + /* per cpu % of delta */ + ln = scols_table_new_line(table, NULL); + if (!ln) { + warn(_("failed to add line to output")); + goto err; + } + if (!out->json) + scols_line_set_data(ln, 0, "%delta:"); + + for (i = 0; i < curr->nr_active_cpu; i++) { + struct irq_cpu *cpu = &curr->cpus[i]; + char *str; + + if (!curr->delta_irq) + continue; + xasprintf(&str, "%0.1f", (double)((long double) cpu->delta / (long double) curr->delta_irq * 100.0)); + if (str && scols_line_refer_data(ln, i + 1, str) != 0) + goto err; + } + + return table; + err: + scols_unref_table(table); + return NULL; +} + struct libscols_table *get_scols_table(struct irq_output *out, struct irq_stat *prev, struct irq_stat **xstat, diff --git a/sys-utils/irq-common.h b/sys-utils/irq-common.h index f296ad06fe..5dbc652f64 100644 --- a/sys-utils/irq-common.h +++ b/sys-utils/irq-common.h @@ -21,16 +21,22 @@ struct irq_info { unsigned long delta; /* delta count since previous update */ }; +struct irq_cpu { + unsigned long total; + unsigned long delta; +}; 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 */ + struct irq_cpu *cpus; /* array of irq_cpu */ + size_t nr_active_cpu; /* number of active cpu */ unsigned long total_irq; /* total irqs */ unsigned long delta_irq; /* delta irqs */ }; + typedef int (irq_cmp_t)(const struct irq_info *, const struct irq_info *); /* output definition */ @@ -59,4 +65,8 @@ struct libscols_table *get_scols_table(struct irq_output *out, struct irq_stat **xstat, int softirq); +struct libscols_table *get_scols_cpus_table(struct irq_output *out, + struct irq_stat *prev, + struct irq_stat *curr); + #endif /* UTIL_LINUX_H_IRQ_COMMON */ diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c index ca37c368f6..1a5cec0cc9 100644 --- a/sys-utils/irqtop.c +++ b/sys-utils/irqtop.c @@ -98,33 +98,43 @@ 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) { - struct libscols_table *table; + struct libscols_table *table, *cpus; struct irq_stat *stat; time_t now = time(NULL); char timestr[64], *data, *data0, *p; + /* make irqs table */ table = get_scols_table(out, ctl->prev_stat, &stat, ctl->softirq); if (!table) { ctl->request_exit = 1; return 1; } - scols_table_enable_maxout(table, 1); scols_table_enable_nowrap(table, 1); scols_table_reduce_termwidth(table, 1); - /* header in interactive mode */ + /* make cpus table */ + cpus = get_scols_cpus_table(out, ctl->prev_stat, stat); + scols_table_reduce_termwidth(cpus, 1); + + /* print header */ move(0, 0); strtime_iso(&now, ISO_TIMESTAMP, timestr, sizeof(timestr)); wprintw(ctl->win, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"), stat->total_irq, stat->delta_irq, ctl->hostname, timestr); + /* print cpus table */ + scols_print_table_to_string(cpus, &data); + wprintw(ctl->win, "%s\n\n", data); + free(data); + + /* print irqs table */ scols_print_table_to_string(table, &data0); data = data0; - /* print header in reverse mode */ p = strchr(data, '\n'); if (p) { + /* print header in reverse mode */ *p = '\0'; attron(A_REVERSE); wprintw(ctl->win, "%s\n", data);