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 <kzak@redhat.com>
This commit is contained in:
Karel Zak 2021-02-24 20:10:56 +01:00
parent 1b889dcc07
commit a23aecc1bf
3 changed files with 124 additions and 7 deletions

View File

@ -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,

View File

@ -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 */

View File

@ -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);