]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/irq-common.c
2 * SPDX-License-Identifier: GPL-2.1-or-later
4 * irq-common.c - functions to display kernel interrupt information.
6 * Copyright (C) 2019 zhenwei pi <pizhenwei@bytedance.com>
7 * Copyright (C) 2020 Karel Zak <kzak@redhat.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
22 #include <sys/types.h>
25 #include <libsmartcols.h>
29 #include "pathnames.h"
33 #include "irq-common.h"
35 #define IRQ_INFO_LEN 64
45 static const struct colinfo infos
[] = {
46 [COL_IRQ
] = {"IRQ", 0.10, SCOLS_FL_RIGHT
, N_("interrupts"), SCOLS_JSON_STRING
},
47 [COL_TOTAL
] = {"TOTAL", 0.10, SCOLS_FL_RIGHT
, N_("total count"), SCOLS_JSON_NUMBER
},
48 [COL_DELTA
] = {"DELTA", 0.10, SCOLS_FL_RIGHT
, N_("delta count"), SCOLS_JSON_NUMBER
},
49 [COL_NAME
] = {"NAME", 0.70, SCOLS_FL_TRUNC
, N_("name"), SCOLS_JSON_STRING
},
52 /* make softirq friendly to end-user */
57 { .irq
= "HI", .desc
= "high priority tasklet softirq" },
58 { .irq
= "TIMER", .desc
= "timer softirq" },
59 { .irq
= "NET_TX", .desc
= "network transmit softirq", },
60 { .irq
= "NET_RX", .desc
= "network receive softirq" },
61 { .irq
= "BLOCK", .desc
= "block device softirq" },
62 { .irq
= "IRQ_POLL", .desc
= "IO poll softirq" },
63 { .irq
= "TASKLET", .desc
= "normal priority tasklet softirq" },
64 { .irq
= "SCHED", .desc
= "schedule softirq" },
65 { .irq
= "HRTIMER", .desc
= "high resolution timer softirq" },
66 { .irq
= "RCU", .desc
= "RCU softirq" },
69 static void get_softirq_desc(struct irq_info
*curr
)
71 int i
, size
= ARRAY_SIZE(softirq_descs
);
73 for (i
= 0; i
< size
; i
++) {
74 if (!strcmp(curr
->irq
, softirq_descs
[i
].irq
))
79 curr
->name
= xstrdup(softirq_descs
[i
].desc
);
81 curr
->name
= xstrdup("");
84 int irq_column_name_to_id(const char *name
, size_t namesz
)
89 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
90 const char *cn
= infos
[i
].name
;
92 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
95 warnx(_("unknown column: %s"), name
);
99 static inline int get_column_id(struct irq_output
*out
, size_t const num
)
101 assert(num
< out
->ncolumns
);
102 assert(out
->columns
[num
] < (int)ARRAY_SIZE(infos
));
104 return out
->columns
[num
];
107 static inline const struct colinfo
*get_column_info(
108 struct irq_output
*out
, unsigned num
)
110 return &infos
[get_column_id(out
, num
)];
113 void irq_print_columns(FILE *f
, int nodelta
)
117 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
118 if (nodelta
&& i
== COL_DELTA
)
120 fprintf(f
, " %-5s %s\n", infos
[i
].name
, _(infos
[i
].help
));
124 static struct libscols_table
*new_scols_table(struct irq_output
*out
)
127 struct libscols_table
*table
;
129 table
= scols_new_table();
131 warn(_("failed to initialize output table"));
134 scols_table_enable_json(table
, out
->json
);
135 scols_table_enable_noheadings(table
, out
->no_headings
);
136 scols_table_enable_export(table
, out
->pairs
);
139 scols_table_set_name(table
, "interrupts");
141 for (i
= 0; i
< out
->ncolumns
; i
++) {
142 const struct colinfo
*col
= get_column_info(out
, i
);
143 int flags
= col
->flags
;
144 struct libscols_column
*cl
;
146 cl
= scols_table_new_column(table
, col
->name
, col
->whint
, flags
);
148 warnx(_("failed to initialize output column"));
152 scols_column_set_json_type(cl
, col
->json_type
);
157 scols_unref_table(table
);
161 static struct libscols_line
*new_scols_line(struct libscols_table
*table
)
163 struct libscols_line
*line
= scols_table_new_line(table
, NULL
);
165 warn(_("failed to add line to output"));
171 static void add_scols_line(struct irq_output
*out
,
172 struct irq_info
*info
,
173 struct libscols_table
*table
)
176 struct libscols_line
*line
= new_scols_line(table
);
178 for (i
= 0; i
< out
->ncolumns
; i
++) {
181 switch (get_column_id(out
, i
)) {
183 xasprintf(&str
, "%s", info
->irq
);
186 xasprintf(&str
, "%ld", info
->total
);
189 xasprintf(&str
, "%ld", info
->delta
);
192 xasprintf(&str
, "%s", info
->name
);
198 if (str
&& scols_line_refer_data(line
, i
, str
) != 0)
203 static char *remove_repeated_spaces(char *str
)
205 char *inp
= str
, *outp
= str
;
206 uint8_t prev_space
= 0;
224 static bool cpu_in_list(int cpu
, size_t setsize
, cpu_set_t
*cpuset
)
226 /* no -C/--cpu-list specified, use all the CPUs */
230 return CPU_ISSET_S(cpu
, setsize
, cpuset
);
234 * irqinfo - parse the system's interrupts
236 static struct irq_stat
*get_irqinfo(int softirq
, size_t setsize
, cpu_set_t
*cpuset
)
239 char *line
= NULL
, *tmp
;
241 struct irq_stat
*stat
;
242 struct irq_info
*curr
;
244 /* NAME + ':' + 11 bytes/cpu + IRQ_NAME_LEN */
245 stat
= xcalloc(1, sizeof(*stat
));
247 stat
->irq_info
= xmalloc(sizeof(*stat
->irq_info
) * IRQ_INFO_LEN
);
248 stat
->nr_irq_info
= IRQ_INFO_LEN
;
251 irqfile
= fopen(_PATH_PROC_SOFTIRQS
, "r");
253 irqfile
= fopen(_PATH_PROC_INTERRUPTS
, "r");
255 warn(_("cannot open %s"), _PATH_PROC_INTERRUPTS
);
259 /* read header firstly */
260 if (getline(&line
, &len
, irqfile
) < 0) {
261 warn(_("cannot read %s"), _PATH_PROC_INTERRUPTS
);
266 while ((tmp
= strstr(tmp
, "CPU")) != NULL
) {
267 tmp
+= 3; /* skip this "CPU", find next */
268 stat
->nr_active_cpu
++;
271 stat
->cpus
= xcalloc(stat
->nr_active_cpu
, sizeof(struct irq_cpu
));
273 /* parse each line of _PATH_PROC_INTERRUPTS */
274 while (getline(&line
, &len
, irqfile
) >= 0) {
279 tmp
= strchr(line
, ':');
283 length
= strlen(line
);
285 curr
= stat
->irq_info
+ stat
->nr_irq
++;
286 memset(curr
, 0, sizeof(*curr
));
288 curr
->irq
= xstrdup(line
);
289 ltrim_whitespace((unsigned char *)curr
->irq
);
292 for (index
= 0; (index
< stat
->nr_active_cpu
) && (tmp
- line
< length
); index
++) {
293 struct irq_cpu
*cpu
= &stat
->cpus
[index
];
295 if (sscanf(tmp
, " %10lu", &count
) != 1)
297 if (cpu_in_list(index
, setsize
, cpuset
)) {
298 curr
->total
+= count
;
300 stat
->total_irq
+= count
;
306 /* softirq always has no desc, add additional desc for softirq */
308 get_softirq_desc(curr
);
310 if (tmp
- line
< length
) {
311 /* strip all space before desc */
312 while (isspace(*tmp
))
314 tmp
= remove_repeated_spaces(tmp
);
315 rtrim_whitespace((unsigned char *)tmp
);
316 curr
->name
= xstrdup(tmp
);
317 } else /* no irq name string, we have to set '\0' here */
318 curr
->name
= xstrdup("");
321 if (stat
->nr_irq
== stat
->nr_irq_info
) {
322 stat
->nr_irq_info
*= 2;
323 stat
->irq_info
= xreallocarray(stat
->irq_info
, stat
->nr_irq_info
,
324 sizeof(*stat
->irq_info
));
334 free(stat
->irq_info
);
341 void free_irqstat(struct irq_stat
*stat
)
348 for (i
= 0; i
< stat
->nr_irq
; i
++) {
349 free(stat
->irq_info
[i
].name
);
350 free(stat
->irq_info
[i
].irq
);
353 free(stat
->irq_info
);
358 static inline int cmp_name(const struct irq_info
*a
,
359 const struct irq_info
*b
)
361 return strcoll(a
->name
, b
->name
);
364 static inline int cmp_ulong_descending(unsigned long a
,
375 static inline int cmp_total(const struct irq_info
*a
,
376 const struct irq_info
*b
)
378 int cmp
= cmp_ulong_descending(a
->total
, b
->total
);
379 return cmp
? cmp
: cmp_name(a
, b
);
382 static inline int cmp_delta(const struct irq_info
*a
,
383 const struct irq_info
*b
)
385 int cmp
= cmp_ulong_descending(a
->delta
, b
->delta
);
386 return cmp
? cmp
: cmp_name(a
, b
);
389 static inline int cmp_interrupts(const struct irq_info
*a
,
390 const struct irq_info
*b
)
392 return strverscmp(a
->irq
, b
->irq
);
395 static void sort_result(struct irq_output
*out
,
396 struct irq_info
*result
,
399 irq_cmp_t
*func
= cmp_total
; /* default */
401 if (out
->sort_cmp_func
)
402 func
= out
->sort_cmp_func
;
404 qsort(result
, nmemb
, sizeof(*result
),
405 (int (*)(const void *, const void *)) func
);
408 void set_sort_func_by_name(struct irq_output
*out
, const char *name
)
410 if (strcasecmp(name
, "IRQ") == 0)
411 out
->sort_cmp_func
= cmp_interrupts
;
412 else if (strcasecmp(name
, "TOTAL") == 0)
413 out
->sort_cmp_func
= cmp_total
;
414 else if (strcasecmp(name
, "DELTA") == 0)
415 out
->sort_cmp_func
= cmp_delta
;
416 else if (strcasecmp(name
, "NAME") == 0)
417 out
->sort_cmp_func
= cmp_name
;
419 errx(EXIT_FAILURE
, _("unsupported column name to sort output"));
422 void set_sort_func_by_key(struct irq_output
*out
, char c
)
426 out
->sort_cmp_func
= cmp_interrupts
;
429 out
->sort_cmp_func
= cmp_total
;
432 out
->sort_cmp_func
= cmp_delta
;
435 out
->sort_cmp_func
= cmp_name
;
440 struct libscols_table
*get_scols_cpus_table(struct irq_output
*out
,
441 struct irq_stat
*prev
,
442 struct irq_stat
*curr
,
446 struct libscols_table
*table
;
447 struct libscols_column
*cl
;
448 struct libscols_line
*ln
;
449 char colname
[sizeof("cpu") + sizeof(stringify_value(LONG_MAX
))];
453 for (i
= 0; i
< curr
->nr_active_cpu
; i
++) {
454 struct irq_cpu
*pre
= &prev
->cpus
[i
];
455 struct irq_cpu
*cur
= &curr
->cpus
[i
];
457 cur
->delta
= cur
->total
- pre
->total
;
461 table
= scols_new_table();
463 warn(_("failed to initialize output table"));
466 scols_table_enable_json(table
, out
->json
);
467 scols_table_enable_noheadings(table
, out
->no_headings
);
468 scols_table_enable_export(table
, out
->pairs
);
471 scols_table_set_name(table
, _("cpu-interrupts"));
473 scols_table_new_column(table
, "", 0, SCOLS_FL_RIGHT
);
475 for (i
= 0; i
< curr
->nr_active_cpu
; i
++) {
476 if (!cpu_in_list(i
, setsize
, cpuset
))
478 snprintf(colname
, sizeof(colname
), "cpu%zu", i
);
479 cl
= scols_table_new_column(table
, colname
, 0, SCOLS_FL_RIGHT
);
481 warnx(_("failed to initialize output column"));
485 scols_column_set_json_type(cl
, SCOLS_JSON_STRING
);
488 /* per cpu % of total */
489 ln
= new_scols_line(table
);
490 if (!ln
|| (!out
->json
&& scols_line_set_data(ln
, 0, "%irq:") != 0))
493 for (i
= 0, j
= 0; i
< curr
->nr_active_cpu
; i
++) {
494 struct irq_cpu
*cpu
= &curr
->cpus
[i
];
497 if (!cpu_in_list(i
, setsize
, cpuset
))
499 xasprintf(&str
, "%0.1f", (double)((long double) cpu
->total
/ (long double) curr
->total_irq
* 100.0));
500 if (str
&& scols_line_refer_data(ln
, ++j
, str
) != 0)
504 /* per cpu % of delta */
505 ln
= new_scols_line(table
);
506 /* xgettext:no-c-format */
507 if (!ln
|| (!out
->json
&& scols_line_set_data(ln
, 0, _("%delta:")) != 0))
510 for (i
= 0, j
= 0; i
< curr
->nr_active_cpu
; i
++) {
511 struct irq_cpu
*cpu
= &curr
->cpus
[i
];
514 if (!cpu_in_list(i
, setsize
, cpuset
))
516 if (!curr
->delta_irq
)
518 xasprintf(&str
, "%0.1f", (double)((long double) cpu
->delta
/ (long double) curr
->delta_irq
* 100.0));
519 if (str
&& scols_line_refer_data(ln
, ++j
, str
) != 0)
525 scols_unref_table(table
);
529 struct libscols_table
*get_scols_table(struct irq_output
*out
,
530 struct irq_stat
*prev
,
531 struct irq_stat
**xstat
,
536 struct libscols_table
*table
;
537 struct irq_info
*result
;
538 struct irq_stat
*stat
;
543 stat
= get_irqinfo(softirq
, setsize
, cpuset
);
547 size
= sizeof(*stat
->irq_info
) * stat
->nr_irq
;
548 result
= xmalloc(size
);
549 memcpy(result
, stat
->irq_info
, size
);
553 for (i
= 0; i
< stat
->nr_irq
; i
++) {
554 struct irq_info
*cur
= &result
[i
];
555 struct irq_info
*pre
= &prev
->irq_info
[i
];
557 cur
->delta
= cur
->total
- pre
->total
;
558 stat
->delta_irq
+= cur
->delta
;
561 sort_result(out
, result
, stat
->nr_irq
);
563 table
= new_scols_table(out
);
570 for (i
= 0; i
< stat
->nr_irq
; i
++)
571 add_scols_line(out
, &result
[i
], table
);