]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/irq-common.c
f36ed693c8cbe59113897b8bf728548a2dfdc9a3
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
= xreallocarray(stat
->irq_info
, stat
->nr_irq_info
,
332 sizeof(*stat
->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_ulong_descending(unsigned long a
,
383 static inline int cmp_total(const struct irq_info
*a
,
384 const struct irq_info
*b
)
386 int cmp
= cmp_ulong_descending(a
->total
, b
->total
);
387 return cmp
? cmp
: cmp_name(a
, b
);
390 static inline int cmp_delta(const struct irq_info
*a
,
391 const struct irq_info
*b
)
393 int cmp
= cmp_ulong_descending(a
->delta
, b
->delta
);
394 return cmp
? cmp
: cmp_name(a
, b
);
397 static inline int cmp_interrupts(const struct irq_info
*a
,
398 const struct irq_info
*b
)
400 return strverscmp(a
->irq
, b
->irq
);
403 static void sort_result(struct irq_output
*out
,
404 struct irq_info
*result
,
407 irq_cmp_t
*func
= cmp_total
; /* default */
409 if (out
->sort_cmp_func
)
410 func
= out
->sort_cmp_func
;
412 qsort(result
, nmemb
, sizeof(*result
),
413 (int (*)(const void *, const void *)) func
);
416 void set_sort_func_by_name(struct irq_output
*out
, const char *name
)
418 if (strcasecmp(name
, "IRQ") == 0)
419 out
->sort_cmp_func
= cmp_interrupts
;
420 else if (strcasecmp(name
, "TOTAL") == 0)
421 out
->sort_cmp_func
= cmp_total
;
422 else if (strcasecmp(name
, "DELTA") == 0)
423 out
->sort_cmp_func
= cmp_delta
;
424 else if (strcasecmp(name
, "NAME") == 0)
425 out
->sort_cmp_func
= cmp_name
;
427 errx(EXIT_FAILURE
, _("unsupported column name to sort output"));
430 void set_sort_func_by_key(struct irq_output
*out
, char c
)
434 out
->sort_cmp_func
= cmp_interrupts
;
437 out
->sort_cmp_func
= cmp_total
;
440 out
->sort_cmp_func
= cmp_delta
;
443 out
->sort_cmp_func
= cmp_name
;
448 struct libscols_table
*get_scols_cpus_table(struct irq_output
*out
,
449 struct irq_stat
*prev
,
450 struct irq_stat
*curr
,
454 struct libscols_table
*table
;
455 struct libscols_column
*cl
;
456 struct libscols_line
*ln
;
457 char colname
[sizeof("cpu") + sizeof(stringify_value(LONG_MAX
))];
461 for (i
= 0; i
< curr
->nr_active_cpu
; i
++) {
462 struct irq_cpu
*pre
= &prev
->cpus
[i
];
463 struct irq_cpu
*cur
= &curr
->cpus
[i
];
465 cur
->delta
= cur
->total
- pre
->total
;
469 table
= scols_new_table();
471 warn(_("failed to initialize output table"));
474 scols_table_enable_json(table
, out
->json
);
475 scols_table_enable_noheadings(table
, out
->no_headings
);
476 scols_table_enable_export(table
, out
->pairs
);
479 scols_table_set_name(table
, _("cpu-interrupts"));
481 scols_table_new_column(table
, "", 0, SCOLS_FL_RIGHT
);
483 for (i
= 0; i
< curr
->nr_active_cpu
; i
++) {
484 if (!cpu_in_list(i
, setsize
, cpuset
))
486 snprintf(colname
, sizeof(colname
), "cpu%zu", i
);
487 cl
= scols_table_new_column(table
, colname
, 0, SCOLS_FL_RIGHT
);
489 warnx(_("failed to initialize output column"));
493 scols_column_set_json_type(cl
, SCOLS_JSON_STRING
);
496 /* per cpu % of total */
497 ln
= new_scols_line(table
);
498 if (!ln
|| (!out
->json
&& scols_line_set_data(ln
, 0, "%irq:") != 0))
501 for (i
= 0, j
= 0; i
< curr
->nr_active_cpu
; i
++) {
502 struct irq_cpu
*cpu
= &curr
->cpus
[i
];
505 if (!cpu_in_list(i
, setsize
, cpuset
))
507 xasprintf(&str
, "%0.1f", (double)((long double) cpu
->total
/ (long double) curr
->total_irq
* 100.0));
508 if (str
&& scols_line_refer_data(ln
, ++j
, str
) != 0)
512 /* per cpu % of delta */
513 ln
= new_scols_line(table
);
514 /* xgettext:no-c-format */
515 if (!ln
|| (!out
->json
&& scols_line_set_data(ln
, 0, _("%delta:")) != 0))
518 for (i
= 0, j
= 0; i
< curr
->nr_active_cpu
; i
++) {
519 struct irq_cpu
*cpu
= &curr
->cpus
[i
];
522 if (!cpu_in_list(i
, setsize
, cpuset
))
524 if (!curr
->delta_irq
)
526 xasprintf(&str
, "%0.1f", (double)((long double) cpu
->delta
/ (long double) curr
->delta_irq
* 100.0));
527 if (str
&& scols_line_refer_data(ln
, ++j
, str
) != 0)
533 scols_unref_table(table
);
537 struct libscols_table
*get_scols_table(struct irq_output
*out
,
538 struct irq_stat
*prev
,
539 struct irq_stat
**xstat
,
544 struct libscols_table
*table
;
545 struct irq_info
*result
;
546 struct irq_stat
*stat
;
551 stat
= get_irqinfo(softirq
, setsize
, cpuset
);
555 size
= sizeof(*stat
->irq_info
) * stat
->nr_irq
;
556 result
= xmalloc(size
);
557 memcpy(result
, stat
->irq_info
, size
);
561 for (i
= 0; i
< stat
->nr_irq
; i
++) {
562 struct irq_info
*cur
= &result
[i
];
563 struct irq_info
*pre
= &prev
->irq_info
[i
];
565 cur
->delta
= cur
->total
- pre
->total
;
566 stat
->delta_irq
+= cur
->delta
;
569 sort_result(out
, result
, stat
->nr_irq
);
571 table
= new_scols_table(out
);
578 for (i
= 0; i
< stat
->nr_irq
; i
++)
579 add_scols_line(out
, &result
[i
], table
);