]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/irqtop.c
sys-utils: cleanup license lines, add SPDX
[thirdparty/util-linux.git] / sys-utils / irqtop.c
CommitLineData
ae0dfe14 1/*
9abd5e4b
KZ
2 * SPDX-License-Identifier: GPL-2.1-or-later
3 *
ae0dfe14 4 * irqtop.c - utility to display kernel interrupt information.
5 *
94e7e258
KZ
6 * Copyright (C) 2019 zhenwei pi <pizhenwei@bytedance.com>
7 * Copyright (C) 2020 Karel Zak <kzak@redhat.com>
ae0dfe14 8 *
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.
ae0dfe14 13 */
57e3dc06
SK
14#include <ctype.h>
15#include <errno.h>
16#include <getopt.h>
ae0dfe14 17#include <limits.h>
18#include <locale.h>
57e3dc06 19#include <signal.h>
ae0dfe14 20#include <stdio.h>
57e3dc06 21#include <stdlib.h>
ae0dfe14 22#include <string.h>
fa8c5cd5 23#include <sys/epoll.h>
ae0dfe14 24#include <sys/ioctl.h>
ae0dfe14 25#include <sys/select.h>
fa8c5cd5 26#include <sys/signalfd.h>
ae0dfe14 27#include <sys/time.h>
fa8c5cd5 28#include <sys/timerfd.h>
ae0dfe14 29#include <sys/types.h>
57e3dc06 30#include <termios.h>
ae0dfe14 31#include <unistd.h>
ae0dfe14 32
57e3dc06
SK
33#ifdef HAVE_SLCURSES_H
34# include <slcurses.h>
35#elif defined(HAVE_SLANG_SLCURSES_H)
36# include <slang/slcurses.h>
37#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
38# include <ncursesw/ncurses.h>
39#elif defined(HAVE_NCURSES_H)
40# include <ncurses.h>
41#elif defined(HAVE_NCURSES_NCURSES_H)
42# include <ncurses/ncurses.h>
43#endif
44
45#ifdef HAVE_WIDECHAR
46# include <wctype.h>
47# include <wchar.h>
48#endif
49
8d8cef80
SK
50#include <libsmartcols.h>
51
57e3dc06 52#include "closestream.h"
4b2fadb1 53#include "cpuset.h"
465e279a 54#include "monotonic.h"
57e3dc06 55#include "pathnames.h"
4e55ffbd 56#include "strutils.h"
a163d853 57#include "timeutils.h"
a11660d1 58#include "ttyutils.h"
9aea73b3 59#include "xalloc.h"
57e3dc06 60
94e7e258 61#include "irq-common.h"
d7f9cdf5 62
94e7e258 63#define MAX_EVENTS 3
c6ccf2ec 64
232e85af
KZ
65enum irqtop_cpustat_mode {
66 IRQTOP_CPUSTAT_AUTO,
67 IRQTOP_CPUSTAT_ENABLE,
68 IRQTOP_CPUSTAT_DISABLE,
17f7caa4 69};
70
02f2919e 71/* top control struct */
a937ed0f 72struct irqtop_ctl {
9c0740fc
KZ
73 WINDOW *win;
74 int cols;
75 int rows;
76 char *hostname;
77
fa8c5cd5 78 struct itimerspec timer;
9c0740fc 79 struct irq_stat *prev_stat;
4b2fadb1 80 size_t setsize;
81 cpu_set_t *cpuset;
487c7466 82
232e85af 83 enum irqtop_cpustat_mode cpustat_mode;
94e7e258 84 unsigned int request_exit:1;
b6ce063b 85 unsigned int softirq:1;
a937ed0f 86};
ae0dfe14 87
02f2919e 88/* user's input parser */
c6ccf2ec 89static void parse_input(struct irqtop_ctl *ctl, struct irq_output *out, char c)
ae0dfe14 90{
57e3dc06 91 switch (c) {
ae0dfe14 92 case 'q':
93 case 'Q':
4e55ffbd 94 ctl->request_exit = 1;
ae0dfe14 95 break;
44e39c99
KZ
96 default:
97 set_sort_func_by_key(out, c);
98 break;
ae0dfe14 99 }
100}
101
9c0740fc 102static int update_screen(struct irqtop_ctl *ctl, struct irq_output *out)
ae0dfe14 103{
17f7caa4 104 struct libscols_table *table, *cpus = NULL;
29135b23 105 struct irq_stat *stat;
ec95b436 106 time_t now = time(NULL);
1b889dcc 107 char timestr[64], *data, *data0, *p;
29135b23 108
a23aecc1 109 /* make irqs table */
4b2fadb1 110 table = get_scols_table(out, ctl->prev_stat, &stat, ctl->softirq, ctl->setsize,
111 ctl->cpuset);
212ca752 112 if (!table) {
29135b23
SK
113 ctl->request_exit = 1;
114 return 1;
115 }
1b889dcc
KZ
116 scols_table_enable_maxout(table, 1);
117 scols_table_enable_nowrap(table, 1);
118 scols_table_reduce_termwidth(table, 1);
119
a23aecc1 120 /* make cpus table */
232e85af 121 if (ctl->cpustat_mode != IRQTOP_CPUSTAT_DISABLE) {
4b2fadb1 122 cpus = get_scols_cpus_table(out, ctl->prev_stat, stat, ctl->setsize,
123 ctl->cpuset);
17f7caa4 124 scols_table_reduce_termwidth(cpus, 1);
232e85af 125 if (ctl->cpustat_mode == IRQTOP_CPUSTAT_AUTO)
17f7caa4 126 scols_table_enable_nowrap(cpus, 1);
127 }
a23aecc1
KZ
128
129 /* print header */
ec95b436
KZ
130 move(0, 0);
131 strtime_iso(&now, ISO_TIMESTAMP, timestr, sizeof(timestr));
9c0740fc 132 wprintw(ctl->win, _("irqtop | total: %ld delta: %ld | %s | %s\n\n"),
68afc56b 133 stat->total_irq, stat->delta_irq, ctl->hostname, timestr);
8d8cef80 134
17f7caa4 135 /* print cpus table or not by -c option */
136 if (cpus) {
137 scols_print_table_to_string(cpus, &data);
138 wprintw(ctl->win, "%s\n\n", data);
139 free(data);
140 }
a23aecc1
KZ
141
142 /* print irqs table */
1b889dcc
KZ
143 scols_print_table_to_string(table, &data0);
144 data = data0;
145
1b889dcc
KZ
146 p = strchr(data, '\n');
147 if (p) {
a23aecc1 148 /* print header in reverse mode */
1b889dcc
KZ
149 *p = '\0';
150 attron(A_REVERSE);
151 wprintw(ctl->win, "%s\n", data);
152 attroff(A_REVERSE);
153 data = p + 1;
154 }
155
9c0740fc 156 wprintw(ctl->win, "%s", data);
1b889dcc 157 free(data0);
ec95b436 158
daee2720 159 /* clean up */
212ca752 160 scols_unref_table(table);
68afc56b 161 if (ctl->prev_stat)
77f57b90 162 free_irqstat(ctl->prev_stat);
68afc56b 163 ctl->prev_stat = stat;
29135b23
SK
164 return 0;
165}
166
9c0740fc 167static int event_loop(struct irqtop_ctl *ctl, struct irq_output *out)
fa8c5cd5
SK
168{
169 int efd, sfd, tfd;
170 sigset_t sigmask;
171 struct signalfd_siginfo siginfo;
172 struct epoll_event ev, events[MAX_EVENTS];
173 long int nr;
174 uint64_t unused;
175 int retval = 0;
176
177 efd = epoll_create1(0);
178
179 if ((tfd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0)
180 err(EXIT_FAILURE, _("cannot not create timerfd"));
181 if (timerfd_settime(tfd, 0, &ctl->timer, NULL) != 0)
182 err(EXIT_FAILURE, _("cannot set timerfd"));
642f6cb0 183
fa8c5cd5
SK
184 ev.events = EPOLLIN;
185 ev.data.fd = tfd;
186 if (epoll_ctl(efd, EPOLL_CTL_ADD, tfd, &ev) != 0)
187 err(EXIT_FAILURE, _("epoll_ctl failed"));
188
189 if (sigfillset(&sigmask) != 0)
190 err(EXIT_FAILURE, _("sigfillset failed"));
191 if (sigprocmask(SIG_BLOCK, &sigmask, NULL) != 0)
192 err(EXIT_FAILURE, _("sigprocmask failed"));
642f6cb0
KZ
193
194 sigaddset(&sigmask, SIGWINCH);
195 sigaddset(&sigmask, SIGTERM);
196 sigaddset(&sigmask, SIGINT);
197 sigaddset(&sigmask, SIGQUIT);
198
199 if ((sfd = signalfd(-1, &sigmask, SFD_CLOEXEC)) < 0)
fa8c5cd5 200 err(EXIT_FAILURE, _("cannot not create signalfd"));
642f6cb0 201
fa8c5cd5
SK
202 ev.events = EPOLLIN;
203 ev.data.fd = sfd;
204 if (epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &ev) != 0)
205 err(EXIT_FAILURE, _("epoll_ctl failed"));
206
207 ev.events = EPOLLIN;
208 ev.data.fd = STDIN_FILENO;
209 if (epoll_ctl(efd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) != 0)
210 err(EXIT_FAILURE, _("epoll_ctl failed"));
211
9c0740fc 212 retval |= update_screen(ctl, out);
fa8c5cd5
SK
213 refresh();
214
215 while (!ctl->request_exit) {
216 const ssize_t nr_events = epoll_wait(efd, events, MAX_EVENTS, -1);
217
218 for (nr = 0; nr < nr_events; nr++) {
219 if (events[nr].data.fd == tfd) {
220 if (read(tfd, &unused, sizeof(unused)) < 0)
221 warn(_("read failed"));
222 } else if (events[nr].data.fd == sfd) {
223 if (read(sfd, &siginfo, sizeof(siginfo)) < 0) {
224 warn(_("read failed"));
225 continue;
226 }
642f6cb0 227 if (siginfo.ssi_signo == SIGWINCH) {
fa8c5cd5 228 get_terminal_dimension(&ctl->cols, &ctl->rows);
5868026e 229#if HAVE_RESIZETERM
642f6cb0 230 resizeterm(ctl->rows, ctl->cols);
5868026e 231#endif
642f6cb0 232 }
fa8c5cd5
SK
233 else {
234 ctl->request_exit = 1;
235 break;
236 }
237 } else if (events[nr].data.fd == STDIN_FILENO) {
238 char c;
239
240 if (read(STDIN_FILENO, &c, 1) != 1)
241 warn(_("read failed"));
c6ccf2ec 242 parse_input(ctl, out, c);
fa8c5cd5
SK
243 } else
244 abort();
9c0740fc 245 retval |= update_screen(ctl, out);
fa8c5cd5
SK
246 refresh();
247 }
248 }
249 return retval;
250}
251
bd83424b
KZ
252static void __attribute__((__noreturn__)) usage(void)
253{
bd83424b
KZ
254 fputs(USAGE_HEADER, stdout);
255 printf(_(" %s [options]\n"), program_invocation_short_name);
256 fputs(USAGE_SEPARATOR, stdout);
257
258 puts(_("Interactive utility to display kernel interrupt information."));
259
260 fputs(USAGE_OPTIONS, stdout);
17f7caa4 261 fputs(_(" -c, --cpu-stat <mode> show per-cpu stat (auto, enable, disable)\n"), stdout);
4b2fadb1 262 fputs(_(" -C, --cpu-list <list> specify cpus in list format\n"), stdout);
bd83424b 263 fputs(_(" -d, --delay <secs> delay updates\n"), stdout);
44e39c99
KZ
264 fputs(_(" -o, --output <list> define which output columns to use\n"), stdout);
265 fputs(_(" -s, --sort <column> specify sort column\n"), stdout);
b6ce063b 266 fputs(_(" -S, --softirq show softirqs instead of interrupts\n"), stdout);
bd83424b 267 fputs(USAGE_SEPARATOR, stdout);
bad4c729 268 fprintf(stdout, USAGE_HELP_OPTIONS(22));
bd83424b
KZ
269
270 fputs(_("\nThe following interactive key commands are valid:\n"), stdout);
271 fputs(_(" i sort by IRQ\n"), stdout);
272 fputs(_(" t sort by TOTAL\n"), stdout);
273 fputs(_(" d sort by DELTA\n"), stdout);
274 fputs(_(" n sort by NAME\n"), stdout);
275 fputs(_(" q Q quit program\n"), stdout);
276
277 fputs(USAGE_COLUMNS, stdout);
a0f62b0b 278 irq_print_columns(stdout, 0);
bd83424b 279
bad4c729 280 fprintf(stdout, USAGE_MAN_TAIL("irqtop(1)"));
bd83424b
KZ
281 exit(EXIT_SUCCESS);
282}
283
c6ccf2ec
KZ
284static void parse_args( struct irqtop_ctl *ctl,
285 struct irq_output *out,
286 int argc,
287 char **argv)
29135b23 288{
487c7466 289 const char *outarg = NULL;
ae0dfe14 290 static const struct option longopts[] = {
0037eb44 291 {"cpu-stat", required_argument, NULL, 'c'},
4b2fadb1 292 {"cpu-list", required_argument, NULL, 'C'},
57e3dc06
SK
293 {"delay", required_argument, NULL, 'd'},
294 {"sort", required_argument, NULL, 's'},
487c7466 295 {"output", required_argument, NULL, 'o'},
b6ce063b 296 {"softirq", no_argument, NULL, 'S'},
57e3dc06
SK
297 {"help", no_argument, NULL, 'h'},
298 {"version", no_argument, NULL, 'V'},
299 {NULL, 0, NULL, 0}
ae0dfe14 300 };
29135b23 301 int o;
ae0dfe14 302
4b2fadb1 303 while ((o = getopt_long(argc, argv, "c:C:d:o:s:ShV", longopts, NULL)) != -1) {
ae0dfe14 304 switch (o) {
17f7caa4 305 case 'c':
306 if (!strcmp(optarg, "auto"))
232e85af 307 ctl->cpustat_mode = IRQTOP_CPUSTAT_AUTO;
17f7caa4 308 else if (!strcmp(optarg, "enable"))
232e85af 309 ctl->cpustat_mode = IRQTOP_CPUSTAT_ENABLE;
17f7caa4 310 else if (!strcmp(optarg, "disable"))
232e85af 311 ctl->cpustat_mode = IRQTOP_CPUSTAT_DISABLE;
17f7caa4 312 else
313 errx(EXIT_FAILURE, _("unsupported mode '%s'"), optarg);
314 break;
4b2fadb1 315 case 'C':
316 {
317 int ncpus = get_max_number_of_cpus();
318 if (ncpus <= 0)
319 errx(EXIT_FAILURE, _("cannot determine NR_CPUS; aborting"));
320
321 ctl->cpuset = cpuset_alloc(ncpus, &ctl->setsize, NULL);
322 if (!ctl->cpuset)
323 err(EXIT_FAILURE, _("cpuset_alloc failed"));
324
325 if (cpulist_parse(optarg, ctl->cpuset, ctl->setsize, 0))
326 errx(EXIT_FAILURE, _("failed to parse CPU list: %s"),
327 optarg);
328 }
329 break;
ae0dfe14 330 case 'd':
fa8c5cd5
SK
331 {
332 struct timeval delay;
333
334 strtotimeval_or_err(optarg, &delay,
335 _("failed to parse delay argument"));
336 TIMEVAL_TO_TIMESPEC(&delay, &ctl->timer.it_interval);
337 ctl->timer.it_value = ctl->timer.it_interval;
338 }
ae0dfe14 339 break;
340 case 's':
44e39c99 341 set_sort_func_by_name(out, optarg);
ae0dfe14 342 break;
487c7466
KZ
343 case 'o':
344 outarg = optarg;
8d8cef80 345 break;
b6ce063b 346 case 'S':
347 ctl->softirq = 1;
348 break;
ae0dfe14 349 case 'V':
57e3dc06 350 print_version(EXIT_SUCCESS);
ae0dfe14 351 case 'h':
57e3dc06 352 usage();
ae0dfe14 353 default:
29135b23 354 errtryhelp(EXIT_FAILURE);
ae0dfe14 355 }
356 }
487c7466
KZ
357
358 /* default */
c6ccf2ec
KZ
359 if (!out->ncolumns) {
360 out->columns[out->ncolumns++] = COL_IRQ;
361 out->columns[out->ncolumns++] = COL_TOTAL;
94e7e258 362 out->columns[out->ncolumns++] = COL_DELTA;
b2a49bc2 363 out->columns[out->ncolumns++] = COL_NAME;
487c7466
KZ
364 }
365
366 /* add -o [+]<list> to putput */
c6ccf2ec
KZ
367 if (outarg && string_add_to_idarray(outarg, out->columns,
368 ARRAY_SIZE(out->columns),
94e7e258
KZ
369 &out->ncolumns,
370 irq_column_name_to_id) < 0)
487c7466 371 exit(EXIT_FAILURE);
29135b23
SK
372}
373
374int main(int argc, char **argv)
375{
642f6cb0 376 int is_tty = 0;
29135b23 377 struct termios saved_tty;
9c0740fc 378 struct irq_output out = {
44e39c99 379 .ncolumns = 0
9c0740fc 380 };
fa8c5cd5
SK
381 struct irqtop_ctl ctl = {
382 .timer.it_interval = {3, 0},
383 .timer.it_value = {3, 0}
384 };
29135b23
SK
385
386 setlocale(LC_ALL, "");
29135b23 387
c6ccf2ec 388 parse_args(&ctl, &out, argc, argv);
ae0dfe14 389
94e7e258
KZ
390 is_tty = isatty(STDIN_FILENO);
391 if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1)
392 fputs(_("terminal setting retrieval"), stdout);
642f6cb0 393
9c0740fc 394 ctl.win = initscr();
94e7e258 395 get_terminal_dimension(&ctl.cols, &ctl.rows);
5868026e 396#if HAVE_RESIZETERM
94e7e258 397 resizeterm(ctl.rows, ctl.cols);
5868026e 398#endif
94e7e258 399 curs_set(0);
642f6cb0 400
94e7e258 401 ctl.hostname = xgethostname();
9c0740fc 402 event_loop(&ctl, &out);
ae0dfe14 403
77f57b90 404 free_irqstat(ctl.prev_stat);
94e7e258 405 free(ctl.hostname);
4b2fadb1 406 cpuset_free(ctl.cpuset);
94e7e258
KZ
407
408 if (is_tty)
409 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty);
9c0740fc 410 delwin(ctl.win);
94e7e258 411 endwin();
ae0dfe14 412
e925cf37 413 return EXIT_SUCCESS;
ae0dfe14 414}