]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/irq-common.c
2 * irq-common.c - functions to display kernel interrupt information.
4 * Copyright (C) 2019 zhenwei pi <pizhenwei@bytedance.com>
5 * Copyright (C) 2020 Karel Zak <kzak@redhat.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 #include <sys/types.h>
33 #include <libsmartcols.h>
37 #include "pathnames.h"
41 #include "irq-common.h"
43 #define IRQ_INFO_LEN 64
53 static const struct colinfo infos
[] = {
54 [COL_IRQ
] = {"IRQ", 0.10, SCOLS_FL_RIGHT
, N_("interrupts"), SCOLS_JSON_STRING
},
55 [COL_TOTAL
] = {"TOTAL", 0.10, SCOLS_FL_RIGHT
, N_("total count"), SCOLS_JSON_NUMBER
},
56 [COL_DELTA
] = {"DELTA", 0.10, SCOLS_FL_RIGHT
, N_("delta count"), SCOLS_JSON_NUMBER
},
57 [COL_NAME
] = {"NAME", 0.70, SCOLS_FL_TRUNC
, N_("name"), SCOLS_JSON_STRING
},
60 /* make softirq friendly to end-user */
65 { .irq
= "HI", .desc
= "high priority tasklet softirq" },
66 { .irq
= "TIMER", .desc
= "timer softirq" },
67 { .irq
= "NET_TX", .desc
= "network transmit softirq", },
68 { .irq
= "NET_RX", .desc
= "network receive softirq" },
69 { .irq
= "BLOCK", .desc
= "block device softirq" },
70 { .irq
= "IRQ_POLL", .desc
= "IO poll softirq" },
71 { .irq
= "TASKLET", .desc
= "normal priority tasklet softirq" },
72 { .irq
= "SCHED", .desc
= "schedule softirq" },
73 { .irq
= "HRTIMER", .desc
= "high resolution timer softirq" },
74 { .irq
= "RCU", .desc
= "RCU softirq" },
77 static void get_softirq_desc(struct irq_info
*curr
)
79 int i
, size
= ARRAY_SIZE(softirq_descs
);
81 for (i
= 0; i
< size
; i
++) {
82 if (!strcmp(curr
->irq
, softirq_descs
[i
].irq
))
87 curr
->name
= xstrdup(softirq_descs
[i
].desc
);
89 curr
->name
= xstrdup("");
92 int irq_column_name_to_id(const char *name
, size_t namesz
)
97 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
98 const char *cn
= infos
[i
].name
;
100 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
103 warnx(_("unknown column: %s"), name
);
107 static inline int get_column_id(struct irq_output
*out
, size_t const num
)
109 assert(num
< out
->ncolumns
);
110 assert(out
->columns
[num
] < (int)ARRAY_SIZE(infos
));
112 return out
->columns
[num
];
115 static inline const struct colinfo
*get_column_info(
116 struct irq_output
*out
, unsigned num
)
118 return &infos
[get_column_id(out
, num
)];
121 void irq_print_columns(FILE *f
, int nodelta
)
125 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
126 if (nodelta
&& i
== COL_DELTA
)
128 fprintf(f
, " %-5s %s\n", infos
[i
].name
, _(infos
[i
].help
));
132 static struct libscols_table
*new_scols_table(struct irq_output
*out
)
135 struct libscols_table
*table
;
137 table
= scols_new_table();
139 warn(_("failed to initialize output table"));
142 scols_table_enable_json(table
, out
->json
);
143 scols_table_enable_noheadings(table
, out
->no_headings
);
144 scols_table_enable_export(table
, out
->pairs
);
147 scols_table_set_name(table
, "interrupts");
149 for (i
= 0; i
< out
->ncolumns
; i
++) {
150 const struct colinfo
*col
= get_column_info(out
, i
);
151 int flags
= col
->flags
;
152 struct libscols_column
*cl
;
154 cl
= scols_table_new_column(table
, col
->name
, col
->whint
, flags
);
156 warnx(_("failed to initialize output column"));
160 scols_column_set_json_type(cl
, col
->json_type
);
165 scols_unref_table(table
);
169 static struct libscols_line
*new_scols_line(struct libscols_table
*table
)
171 struct libscols_line
*line
= scols_table_new_line(table
, NULL
);
173 warn(_("failed to add line to output"));
179 static void add_scols_line(struct irq_output
*out
,
180 struct irq_info
*info
,
181 struct libscols_table
*table
)
184 struct libscols_line
*line
= new_scols_line(table
);
186 for (i
= 0; i
< out
->ncolumns
; i
++) {
189 switch (get_column_id(out
, i
)) {
191 xasprintf(&str
, "%s", info
->irq
);
194 xasprintf(&str
, "%ld", info
->total
);
197 xasprintf(&str
, "%ld", info
->delta
);
200 xasprintf(&str
, "%s", info
->name
);
206 if (str
&& scols_line_refer_data(line
, i
, str
) != 0)
211 static char *remove_repeated_spaces(char *str
)
213 char *inp
= str
, *outp
= str
;
214 uint8_t prev_space
= 0;
232 static bool cpu_in_list(int cpu
, size_t setsize
, cpu_set_t
*cpuset
)
234 /* no -C/--cpu-list specified, use all the CPUs */
238 return CPU_ISSET_S(cpu
, setsize
, cpuset
);
242 * irqinfo - parse the system's interrupts
244 static struct irq_stat
*get_irqinfo(int softirq
, size_t setsize
, cpu_set_t
*cpuset
)
247 char *line
= NULL
, *tmp
;
249 struct irq_stat
*stat
;
250 struct irq_info
*curr
;
252 /* NAME + ':' + 11 bytes/cpu + IRQ_NAME_LEN */
253 stat
= xcalloc(1, sizeof(*stat
));
255 stat
->irq_info
= xmalloc(sizeof(*stat
->irq_info
) * IRQ_INFO_LEN
);
256 stat
->nr_irq_info
= IRQ_INFO_LEN
;
259 irqfile
= fopen(_PATH_PROC_SOFTIRQS
, "r");
261 irqfile
= fopen(_PATH_PROC_INTERRUPTS
, "r");
263 warn(_("cannot open %s"), _PATH_PROC_INTERRUPTS
);
267 /* read header firstly */
268 if (getline(&line
, &len
, irqfile
) < 0) {
269 warn(_("cannot read %s"), _PATH_PROC_INTERRUPTS
);
274 while ((tmp
= strstr(tmp
, "CPU")) != NULL
) {
275 tmp
+= 3; /* skip this "CPU", find next */
276 stat
->nr_active_cpu
++;
279 stat
->cpus
= xcalloc(stat
->nr_active_cpu
, sizeof(struct irq_cpu
));
281 /* parse each line of _PATH_PROC_INTERRUPTS */
282 while (getline(&line
, &len
, irqfile
) >= 0) {
287 tmp
= strchr(line
, ':');
291 length
= strlen(line
);
293 curr
= stat
->irq_info
+ stat
->nr_irq
++;
294 memset(curr
, 0, sizeof(*curr
));
296 curr
->irq
= xstrdup(line
);
297 ltrim_whitespace((unsigned char *)curr
->irq
);
300 for (index
= 0; (index
< stat
->nr_active_cpu
) && (tmp
- line
< length
); index
++) {
301 struct irq_cpu
*cpu
= &stat
->cpus
[index
];
303 if (sscanf(tmp
, " %10lu", &count
) != 1)
305 if (cpu_in_list(index
, setsize
, cpuset
)) {
306 curr
->total
+= count
;
308 stat
->total_irq
+= count
;
314 /* softirq always has no desc, add additional desc for softirq */
316 get_softirq_desc(curr
);
318 if (tmp
- line
< length
) {
319 /* strip all space before desc */
320 while (isspace(*tmp
))
322 tmp
= remove_repeated_spaces(tmp
);
323 rtrim_whitespace((unsigned char *)tmp
);
324 curr
->name
= xstrdup(tmp
);
325 } else /* no irq name string, we have to set '\0' here */
326 curr
->name
= xstrdup("");
329 if (stat
->nr_irq
== stat
->nr_irq_info
) {
330 stat
->nr_irq_info
*= 2;
331 stat
->irq_info
= xrealloc(stat
->irq_info
,
332 sizeof(*stat
->irq_info
) * stat
->nr_irq_info
);
342 free(stat
->irq_info
);
349 void free_irqstat(struct irq_stat
*stat
)
356 for (i
= 0; i
< stat
->nr_irq
; i
++) {
357 free(stat
->irq_info
[i
].name
);
358 free(stat
->irq_info
[i
].irq
);
361 free(stat
->irq_info
);
366 static inline int cmp_name(const struct irq_info
*a
,
367 const struct irq_info
*b
)
369 return strcoll(a
->name
, b
->name
);
372 static inline int cmp_total(const struct irq_info
*a
,
373 const struct irq_info
*b
)
375 return a
->total
< b
->total
;
378 static inline int cmp_delta(const struct irq_info
*a
,
379 const struct irq_info
*b
)
381 return a
->delta
< b
->delta
;
384 static inline int cmp_interrupts(const struct irq_info
*a
,
385 const struct irq_info
*b
)
387 return strverscmp(a
->irq
, b
->irq
);
390 static void sort_result(struct irq_output
*out
,
391 struct irq_info
*result
,
394 irq_cmp_t
*func
= cmp_total
; /* default */
396 if (out
->sort_cmp_func
)
397 func
= out
->sort_cmp_func
;
399 qsort(result
, nmemb
, sizeof(*result
),
400 (int (*)(const void *, const void *)) func
);
403 void set_sort_func_by_name(struct irq_output
*out
, const char *name
)
405 if (strcasecmp(name
, "IRQ") == 0)
406 out
->sort_cmp_func
= cmp_interrupts
;
407 else if (strcasecmp(name
, "TOTAL") == 0)
408 out
->sort_cmp_func
= cmp_total
;
409 else if (strcasecmp(name
, "DELTA") == 0)
410 out
->sort_cmp_func
= cmp_delta
;
411 else if (strcasecmp(name
, "NAME") == 0)
412 out
->sort_cmp_func
= cmp_name
;
414 errx(EXIT_FAILURE
, _("unsupported column name to sort output"));
417 void set_sort_func_by_key(struct irq_output
*out
, char c
)
421 out
->sort_cmp_func
= cmp_interrupts
;
424 out
->sort_cmp_func
= cmp_total
;
427 out
->sort_cmp_func
= cmp_delta
;
430 out
->sort_cmp_func
= cmp_name
;
435 struct libscols_table
*get_scols_cpus_table(struct irq_output
*out
,
436 struct irq_stat
*prev
,
437 struct irq_stat
*curr
,
441 struct libscols_table
*table
;
442 struct libscols_column
*cl
;
443 struct libscols_line
*ln
;
444 char colname
[sizeof("cpu") + sizeof(stringify_value(LONG_MAX
))];
448 for (i
= 0; i
< curr
->nr_active_cpu
; i
++) {
449 struct irq_cpu
*pre
= &prev
->cpus
[i
];
450 struct irq_cpu
*cur
= &curr
->cpus
[i
];
452 cur
->delta
= cur
->total
- pre
->total
;
456 table
= scols_new_table();
458 warn(_("failed to initialize output table"));
461 scols_table_enable_json(table
, out
->json
);
462 scols_table_enable_noheadings(table
, out
->no_headings
);
463 scols_table_enable_export(table
, out
->pairs
);
466 scols_table_set_name(table
, _("cpu-interrupts"));
468 scols_table_new_column(table
, "", 0, SCOLS_FL_RIGHT
);
470 for (i
= 0; i
< curr
->nr_active_cpu
; i
++) {
471 if (!cpu_in_list(i
, setsize
, cpuset
))
473 snprintf(colname
, sizeof(colname
), "cpu%zu", i
);
474 cl
= scols_table_new_column(table
, colname
, 0, SCOLS_FL_RIGHT
);
476 warnx(_("failed to initialize output column"));
480 scols_column_set_json_type(cl
, SCOLS_JSON_STRING
);
483 /* per cpu % of total */
484 ln
= new_scols_line(table
);
485 if (!ln
|| (!out
->json
&& scols_line_set_data(ln
, 0, "%irq:") != 0))
488 for (i
= 0, j
= 0; i
< curr
->nr_active_cpu
; i
++) {
489 struct irq_cpu
*cpu
= &curr
->cpus
[i
];
492 if (!cpu_in_list(i
, setsize
, cpuset
))
494 xasprintf(&str
, "%0.1f", (double)((long double) cpu
->total
/ (long double) curr
->total_irq
* 100.0));
495 if (str
&& scols_line_refer_data(ln
, ++j
, str
) != 0)
499 /* per cpu % of delta */
500 ln
= new_scols_line(table
);
501 /* xgettext:no-c-format */
502 if (!ln
|| (!out
->json
&& scols_line_set_data(ln
, 0, _("%delta:")) != 0))
505 for (i
= 0, j
= 0; i
< curr
->nr_active_cpu
; i
++) {
506 struct irq_cpu
*cpu
= &curr
->cpus
[i
];
509 if (!cpu_in_list(i
, setsize
, cpuset
))
511 if (!curr
->delta_irq
)
513 xasprintf(&str
, "%0.1f", (double)((long double) cpu
->delta
/ (long double) curr
->delta_irq
* 100.0));
514 if (str
&& scols_line_refer_data(ln
, ++j
, str
) != 0)
520 scols_unref_table(table
);
524 struct libscols_table
*get_scols_table(struct irq_output
*out
,
525 struct irq_stat
*prev
,
526 struct irq_stat
**xstat
,
531 struct libscols_table
*table
;
532 struct irq_info
*result
;
533 struct irq_stat
*stat
;
538 stat
= get_irqinfo(softirq
, setsize
, cpuset
);
542 size
= sizeof(*stat
->irq_info
) * stat
->nr_irq
;
543 result
= xmalloc(size
);
544 memcpy(result
, stat
->irq_info
, size
);
548 for (i
= 0; i
< stat
->nr_irq
; i
++) {
549 struct irq_info
*cur
= &result
[i
];
550 struct irq_info
*pre
= &prev
->irq_info
[i
];
552 cur
->delta
= cur
->total
- pre
->total
;
553 stat
->delta_irq
+= cur
->delta
;
556 sort_result(out
, result
, stat
->nr_irq
);
558 table
= new_scols_table(out
);
565 for (i
= 0; i
< stat
->nr_irq
; i
++)
566 add_scols_line(out
, &result
[i
], table
);