Merge branch 'topic/irq'

This commit is contained in:
Karel Zak 2020-03-09 10:25:48 +01:00
commit a8fe698c45
14 changed files with 1227 additions and 6 deletions

2
.gitignore vendored
View File

@ -103,6 +103,7 @@ ylwrap
/ipcmk
/ipcrm
/ipcs
/irqtop
/isosize
/kill
/last
@ -114,6 +115,7 @@ ylwrap
/losetup
/lsblk
/lsipc
/lsirq
/lscpu
/lslocks
/lslogins

View File

@ -5,12 +5,6 @@ script (lib/pty-session.c)
--------------------------
- (!) add #ifdefs and optional code for non-signalfd() systems
irqtop or lsirq
---------------
- add tool to list and monitor IRQs from /proc/interrupts
- libsmartcols is expected
- initial work is done by zhenwei pi, see https://github.com/pacepi/irqtop
col
---
- use unsigned sizes for columns and lines

View File

@ -45,6 +45,9 @@ endif
if BUILD_IPCS
dist_bashcompletion_DATA += bash-completion/ipcs
endif
if BUILD_IRQTOP
dist_bashcompletion_DATA += bash-completion/irqtop
endif
if BUILD_ISOSIZE
dist_bashcompletion_DATA += bash-completion/isosize
endif
@ -57,6 +60,9 @@ endif
if BUILD_LSIPC
dist_bashcompletion_DATA += bash-completion/lsipc
endif
if BUILD_LSIRQ
dist_bashcompletion_DATA += bash-completion/lsirq
endif
if BUILD_LSNS
dist_bashcompletion_DATA += bash-completion/lsns
endif

41
bash-completion/irqtop Normal file
View File

@ -0,0 +1,41 @@
_irqtop_module()
{
local cur prev OPTS
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
case $prev in
'-d'|'--delay')
COMPREPLY=( $(compgen -W "secs" -- $cur) )
return 0
;;
'-s'|'--sort')
COMPREPLY=( $(compgen -W "irq total delta name" -- $cur) )
return 0
;;
'-o'|'--output')
local prefix realcur OUTPUT
realcur="${cur##*,}"
prefix="${cur%$realcur}"
for WORD in "IRQ TOTAL DELTA NAME"; do
if ! [[ $prefix == *"$WORD"* ]]; then
OUTPUT="$WORD ${OUTPUT:-""}"
fi
done
compopt -o nospace
COMPREPLY=( $(compgen -P "$prefix" -W "$OUTPUT" -S ',' -- $realcur) )
return 0
;;
'-h'|'--help'|'-V'|'--version')
return 0
;;
esac
OPTS=" --delay
--sort
--output
--help
--version"
COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
return 0
}
complete -F _irqtop_module irqtop

38
bash-completion/lsirq Normal file
View File

@ -0,0 +1,38 @@
_lsirq_module()
{
local cur prev OPTS
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
case $prev in
'-o'|'--output')
local prefix realcur OUTPUT
realcur="${cur##*,}"
prefix="${cur%$realcur}"
for WORD in "IRQ TOTAL NAME"; do
if ! [[ $prefix == *"$WORD"* ]]; then
OUTPUT="$WORD ${OUTPUT:-""}"
fi
done
compopt -o nospace
COMPREPLY=( $(compgen -P "$prefix" -W "$OUTPUT" -S ',' -- $realcur) )
return 0
;;
'-s'|'--sort')
COMPREPLY=( $(compgen -W "irq total name" -- $cur) )
return 0
;;
'-h'|'--help'|'-V'|'--version')
return 0
;;
esac
OPTS=" --json
--pairs
--output
--sort
--help
--version"
COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
return 0
}
complete -F _lsirq_module lsirq

View File

@ -1768,6 +1768,28 @@ AC_ARG_ENABLE([ipcs],
UL_BUILD_INIT([ipcs])
AM_CONDITIONAL([BUILD_IPCS], [test "x$build_ipcs" = xyes])
AC_ARG_ENABLE([irqtop],
AS_HELP_STRING([--disable-irqtop], [do not build irqtop]),
[], [UL_DEFAULT_ENABLE([irqtop], [yes])]
)
UL_BUILD_INIT([irqtop])
UL_REQUIRES_LINUX([irqtop])
UL_REQUIRES_BUILD([irqtop], [libsmartcols])
UL_REQUIRES_HAVE([irqtop], [open_memstream], [open_memstream function])
UL_REQUIRES_HAVE([irqtop], [ncursesw,slang,ncurses], [ncursesw, ncurses or slang library])
AM_CONDITIONAL([BUILD_IRQTOP], [test "x$build_irqtop" = xyes])
AC_ARG_ENABLE([lsirq],
AS_HELP_STRING([--disable-lsirq], [do not build lsirq]),
[], [UL_DEFAULT_ENABLE([lsirq], [yes])]
)
UL_BUILD_INIT([lsirq])
UL_REQUIRES_LINUX([lsirq])
UL_REQUIRES_BUILD([lsirq], [libsmartcols])
AM_CONDITIONAL([BUILD_LSIRQ], [test "x$build_lsirq" = xyes])
UL_BUILD_INIT([choom], [check])
UL_REQUIRES_LINUX([choom])
AM_CONDITIONAL([BUILD_CHOOM], [test "x$build_choom" = xyes])

View File

@ -186,6 +186,10 @@
#define _PATH_PROC_IPC_SHMMAX "/proc/sys/kernel/shmmax"
#define _PATH_PROC_IPC_SHMMNI "/proc/sys/kernel/shmmni"
/* irqtop paths */
#define _PATH_PROC_INTERRUPTS "/proc/interrupts"
#define _PATH_PROC_UPTIME "/proc/uptime"
/* kernel command line */
#define _PATH_PROC_CMDLINE "/proc/cmdline"

View File

@ -50,6 +50,27 @@ ipcs_SOURCES = sys-utils/ipcs.c \
ipcs_LDADD = $(LDADD) libcommon.la
endif
if BUILD_IRQTOP
usrbin_exec_PROGRAMS += irqtop
dist_man_MANS += sys-utils/irqtop.1
irqtop_SOURCES = sys-utils/irqtop.c \
sys-utils/irq-common.c \
sys-utils/irq-common.h \
lib/monotonic.c
irqtop_LDADD = $(LDADD) libcommon.la $(NCURSES_LIBS) $(REALTIME_LIBS) libsmartcols.la
irqtop_CFLAGS = $(AM_CFLAGS) $(NCURSES_CFLAGS) -I$(ul_libsmartcols_incdir)
endif
if BUILD_LSIRQ
usrbin_exec_PROGRAMS += lsirq
dist_man_MANS += sys-utils/lsirq.1
lsirq_SOURCES = sys-utils/lsirq.c \
sys-utils/irq-common.c \
sys-utils/irq-common.h
lsirq_LDADD = $(LDADD) libcommon.la libsmartcols.la
lsirq_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
endif
if BUILD_LSIPC
usrbin_exec_PROGRAMS += lsipc
dist_man_MANS += sys-utils/lsipc.1

415
sys-utils/irq-common.c Normal file
View File

@ -0,0 +1,415 @@
/*
* irq-common.c - functions to display kernel interrupt information.
*
* Copyright (C) 2019 zhenwei pi <pizhenwei@bytedance.com>
* Copyright (C) 2020 Karel Zak <kzak@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <libsmartcols.h>
#include "c.h"
#include "nls.h"
#include "pathnames.h"
#include "strutils.h"
#include "xalloc.h"
#include "irq-common.h"
#define IRQ_INFO_LEN 64
struct colinfo {
const char *name;
double whint;
int flags;
const char *help;
int json_type;
};
static const struct colinfo infos[] = {
[COL_IRQ] = {"IRQ", 0.10, SCOLS_FL_RIGHT, N_("interrupts"), SCOLS_JSON_STRING},
[COL_TOTAL] = {"TOTAL", 0.10, SCOLS_FL_RIGHT, N_("total count"), SCOLS_JSON_NUMBER},
[COL_DELTA] = {"DELTA", 0.10, SCOLS_FL_RIGHT, N_("delta count"), SCOLS_JSON_NUMBER},
[COL_NAME] = {"NAME", 0.70, SCOLS_FL_TRUNC, N_("name"), SCOLS_JSON_STRING},
};
int irq_column_name_to_id(const char *name, size_t namesz)
{
size_t i;
assert(name);
for (i = 0; i < ARRAY_SIZE(infos); i++) {
const char *cn = infos[i].name;
if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
return i;
}
warnx(_("unknown column: %s"), name);
return -1;
}
static inline int get_column_id(struct irq_output *out, size_t const num)
{
assert(num < out->ncolumns);
assert(out->columns[num] < (int)ARRAY_SIZE(infos));
return out->columns[num];
}
static inline const struct colinfo *get_column_info(
struct irq_output *out, unsigned num)
{
return &infos[get_column_id(out, num)];
}
void irq_print_columns(FILE *f, int nodelta)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(infos); i++) {
if (nodelta && i == COL_DELTA)
continue;
fprintf(f, " %-5s %s\n", infos[i].name, _(infos[i].help));
}
}
static struct libscols_table *new_scols_table(struct irq_output *out)
{
size_t i;
struct libscols_table *table;
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, _("interrupts"));
for (i = 0; i < out->ncolumns; i++) {
const struct colinfo *col = get_column_info(out, i);
int flags = col->flags;
struct libscols_column *cl;
cl = scols_table_new_column(table, col->name, col->whint, flags);
if (cl == NULL) {
warnx(_("failed to initialize output column"));
goto err;
}
if (out->json)
scols_column_set_json_type(cl, col->json_type);
}
return table;
err:
scols_unref_table(table);
return NULL;
}
static void add_scols_line(struct irq_output *out,
struct irq_info *info,
struct libscols_table *table)
{
size_t i;
struct libscols_line *line;
line = scols_table_new_line(table, NULL);
if (!line) {
warn(_("failed to add line to output"));
return;
}
for (i = 0; i < out->ncolumns; i++) {
char *str = NULL;
switch (get_column_id(out, i)) {
case COL_IRQ:
xasprintf(&str, "%s", info->irq);
break;
case COL_TOTAL:
xasprintf(&str, "%ld", info->total);
break;
case COL_DELTA:
xasprintf(&str, "%ld", info->delta);
break;
case COL_NAME:
xasprintf(&str, "%s", info->name);
break;
default:
break;
}
if (str && scols_line_refer_data(line, i, str) != 0)
err_oom();
}
}
static char *remove_repeated_spaces(char *str)
{
char *inp = str, *outp = str;
uint8_t prev_space = 0;
while (*inp) {
if (isspace(*inp)) {
if (!prev_space) {
*outp++ = ' ';
prev_space = 1;
}
} else {
*outp++ = *inp;
prev_space = 0;
}
++inp;
}
*outp = '\0';
return str;
}
/*
* irqinfo - parse the system's interrupts
*/
static struct irq_stat *get_irqinfo(void)
{
FILE *irqfile;
char *line = NULL, *tmp;
size_t len = 0;
struct irq_stat *stat;
struct irq_info *curr;
/* NAME + ':' + 11 bytes/cpu + IRQ_NAME_LEN */
stat = xcalloc(1, sizeof(*stat));
stat->irq_info = xmalloc(sizeof(*stat->irq_info) * IRQ_INFO_LEN);
stat->nr_irq_info = IRQ_INFO_LEN;
irqfile = fopen(_PATH_PROC_INTERRUPTS, "r");
if (!irqfile) {
warn(_("cannot open %s"), _PATH_PROC_INTERRUPTS);
goto free_stat;
}
/* read header firstly */
if (getline(&line, &len, irqfile) < 0) {
warn(_("cannot read %s"), _PATH_PROC_INTERRUPTS);
goto close_file;
}
tmp = line;
while ((tmp = strstr(tmp, "CPU")) != NULL) {
tmp += 3; /* skip this "CPU", find next */
stat->nr_active_cpu++;
}
/* parse each line of _PATH_PROC_INTERRUPTS */
while (getline(&line, &len, irqfile) >= 0) {
unsigned long count;
int index, length;
tmp = strchr(line, ':');
if (!tmp)
continue;
length = strlen(line);
curr = stat->irq_info + stat->nr_irq++;
memset(curr, 0, sizeof(*curr));
*tmp = '\0';
curr->irq = xstrdup(line);
ltrim_whitespace((unsigned char *)curr->irq);
tmp += 1;
for (index = 0; (index < stat->nr_active_cpu) && (tmp - line < length); index++) {
sscanf(tmp, " %10lu", &count);
curr->total += count;
stat->total_irq += count;
tmp += 11;
}
if (tmp - line < length) {
/* strip all space before desc */
while (isspace(*tmp))
tmp++;
tmp = remove_repeated_spaces(tmp);
rtrim_whitespace((unsigned char *)tmp);
curr->name = xstrdup(tmp);
} else /* no irq name string, we have to set '\0' here */
curr->name = xstrdup("");
if (stat->nr_irq == stat->nr_irq_info) {
stat->nr_irq_info *= 2;
stat->irq_info = xrealloc(stat->irq_info,
sizeof(*stat->irq_info) * stat->nr_irq_info);
}
}
fclose(irqfile);
free(line);
return stat;
close_file:
fclose(irqfile);
free_stat:
free(stat->irq_info);
free(stat);
free(line);
return NULL;
}
void free_irqstat(struct irq_stat *stat)
{
size_t i;
if (!stat)
return;
for (i = 0; i < stat->nr_irq; i++) {
free(stat->irq_info[i].name);
free(stat->irq_info[i].irq);
}
free(stat->irq_info);
free(stat);
}
static inline int cmp_name(const struct irq_info *a,
const struct irq_info *b)
{
return (strcmp(a->name, b->name) > 0) ? 1 : 0;
}
static inline int cmp_total(const struct irq_info *a,
const struct irq_info *b)
{
return a->total < b->total;
}
static inline int cmp_delta(const struct irq_info *a,
const struct irq_info *b)
{
return a->delta < b->delta;
}
static inline int cmp_interrupts(const struct irq_info *a,
const struct irq_info *b)
{
return (strcmp(a->irq, b->irq) > 0) ? 1 : 0;
}
static void sort_result(struct irq_output *out,
struct irq_info *result,
size_t nmemb)
{
irq_cmp_t *func = cmp_total; /* default */
if (out->sort_cmp_func)
func = out->sort_cmp_func;
qsort(result, nmemb, sizeof(*result),
(int (*)(const void *, const void *)) func);
}
void set_sort_func_by_name(struct irq_output *out, const char *name)
{
if (strcasecmp(name, "IRQ") == 0)
out->sort_cmp_func = cmp_interrupts;
else if (strcasecmp(name, "TOTAL") == 0)
out->sort_cmp_func = cmp_total;
else if (strcasecmp(name, "DELTA") == 0)
out->sort_cmp_func = cmp_delta;
else if (strcasecmp(name, "NAME") == 0)
out->sort_cmp_func = cmp_name;
else
errx(EXIT_FAILURE, _("unssupported column name to sort output"));
}
void set_sort_func_by_key(struct irq_output *out, char c)
{
switch (c) {
case 'i':
out->sort_cmp_func = cmp_interrupts;
break;
case 't':
out->sort_cmp_func = cmp_total;
break;
case 'd':
out->sort_cmp_func = cmp_delta;
break;
case 'n':
out->sort_cmp_func = cmp_name;
break;
}
}
struct libscols_table *get_scols_table(struct irq_output *out,
struct irq_stat *prev,
struct irq_stat **xstat)
{
struct libscols_table *table;
struct irq_info *result;
struct irq_stat *stat;
size_t size;
size_t i;
/* the stats */
stat = get_irqinfo();
if (!stat)
return NULL;
size = sizeof(*stat->irq_info) * stat->nr_irq;
result = xmalloc(size);
memcpy(result, stat->irq_info, size);
if (prev) {
stat->delta_irq = 0;
for (i = 0; i < stat->nr_irq; i++) {
struct irq_info *cur = &result[i];
struct irq_info *pre = &prev->irq_info[i];
cur->delta = cur->total - pre->total;
stat->delta_irq += cur->delta;
}
}
sort_result(out, result, stat->nr_irq);
table = new_scols_table(out);
if (!table)
return NULL;
for (i = 0; i < stat->nr_irq; i++)
add_scols_line(out, &result[i], table);
free(result);
if (xstat)
*xstat = stat;
else
free_irqstat(stat);
return table;
}

61
sys-utils/irq-common.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef UTIL_LINUX_H_IRQ_COMMON
#define UTIL_LINUX_H_IRQ_COMMON
#include "c.h"
#include "nls.h"
/* supported columns */
enum {
COL_IRQ = 0,
COL_TOTAL,
COL_DELTA,
COL_NAME,
__COL_COUNT
};
struct irq_info {
char *irq; /* short name of this irq */
char *name; /* descriptive name of this irq */
unsigned long total; /* total count since system start up */
unsigned long delta; /* delta count since previous update */
};
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 */
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 */
struct irq_output {
int columns[__COL_COUNT * 2];
size_t ncolumns;
irq_cmp_t *sort_cmp_func;
unsigned int
json:1, /* JSON output */
pairs:1, /* export, NAME="value" aoutput */
no_headings:1; /* don't print header */
};
int irq_column_name_to_id(char const *const name, size_t const namesz);
void free_irqstat(struct irq_stat *stat);
void irq_print_columns(FILE *f, int nodelta);
void set_sort_func_by_name(struct irq_output *out, const char *name);
void set_sort_func_by_key(struct irq_output *out, const char c);
struct libscols_table *get_scols_table(struct irq_output *out,
struct irq_stat *prev,
struct irq_stat **xstat);
#endif /* UTIL_LINUX_H_IRQ_COMMON */

75
sys-utils/irqtop.1 Normal file
View File

@ -0,0 +1,75 @@
.TH IRQTOP "1" "February 2020" "util-linux" "User Commands"
.SH NAME
irqtop \- utility to display kernel interrupt information
.SH SYNOPSIS
.B irqtop
[options]
.SH DESCRIPTION
Display kernel interrupt counter information in
.BR top (1)
style view.
.PP
The default output is subject to change. So whenever possible, you should
avoid using default outputs in your scripts. Always explicitly define
expected columns by using
.BR \-\-output .
.SH OPTIONS
.TP
.BR \-o , " \-\-output " \fIlist\fP
Specify which output columns to print. Use
.B \-\-help
to get a list of all supported columns. The default list of columns may be
extended if list is specified in the format
.IR +list .
.TP
.BR \-d , " \-\-delay " \fIseconds\fP
Update interrupt output every
.I seconds
intervals.
.TP
.BR \-s , " \-\-sort " \fIcolumn\fP
Specify sort criteria by column name. See
.B \-\-help
output to get column names. The sort criteria may be changes in interactive
mode.
.TP
.BR \-V ", " \-\-version
Display version information and exit.
.TP
.BR \-h ,\ \-\-help
Display help text and exit.
.SH INTERACTIVE MODE KEY COMMANDS
.PD 0
.TP
.B i
sort by short irq name or number field
.TP
.B t
sort by total count of interrupts (the default)
.TP
.B d
sort by delta count of interrupts
.TP
.B n
sort by long descriptive name field
.TP
.B q Q
stop updates and exit program
.PD 1
.SH AUTHORS
.MT pizhenwei@\:bytedance.com
Zhenwei Pi
.ME
.br
.MT kerolasa@\:iki.fi
Sami Kerola
.ME
.br
.MT kzak@\:redhat.com
Karel Zak
.ME
.SH AVAILABILITY
The example command is part of the util-linux package and is available from
.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
Linux Kernel Archive
.UE .

337
sys-utils/irqtop.c Normal file
View File

@ -0,0 +1,337 @@
/*
* irqtop.c - utility to display kernel interrupt information.
*
* Copyright (C) 2019 zhenwei pi <pizhenwei@bytedance.com>
* Copyright (C) 2020 Karel Zak <kzak@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <locale.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/signalfd.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#ifdef HAVE_SLCURSES_H
# include <slcurses.h>
#elif defined(HAVE_SLANG_SLCURSES_H)
# include <slang/slcurses.h>
#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
# include <ncursesw/ncurses.h>
#elif defined(HAVE_NCURSES_H)
# include <ncurses.h>
#elif defined(HAVE_NCURSES_NCURSES_H)
# include <ncurses/ncurses.h>
#endif
#ifdef HAVE_WIDECHAR
# include <wctype.h>
# include <wchar.h>
#endif
#include <libsmartcols.h>
#include "closestream.h"
#include "monotonic.h"
#include "pathnames.h"
#include "strutils.h"
#include "timeutils.h"
#include "ttyutils.h"
#include "xalloc.h"
#include "irq-common.h"
#define MAX_EVENTS 3
/* top control struct */
struct irqtop_ctl {
WINDOW *win;
int cols;
int rows;
char *hostname;
struct itimerspec timer;
struct irq_stat *prev_stat;
unsigned int request_exit:1;
};
/* user's input parser */
static void parse_input(struct irqtop_ctl *ctl, struct irq_output *out, char c)
{
switch (c) {
case 'q':
case 'Q':
ctl->request_exit = 1;
break;
default:
set_sort_func_by_key(out, c);
break;
}
}
static int update_screen(struct irqtop_ctl *ctl, struct irq_output *out)
{
struct libscols_table *table;
struct irq_stat *stat;
time_t now = time(NULL);
char timestr[64], *data;
table = get_scols_table(out, ctl->prev_stat, &stat);
if (!table) {
ctl->request_exit = 1;
return 1;
}
/* header in interactive mode */
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);
scols_print_table_to_string(table, &data);
wprintw(ctl->win, "%s", data);
free(data);
/* clean up */
scols_unref_table(table);
if (ctl->prev_stat)
free_irqstat(ctl->prev_stat);
ctl->prev_stat = stat;
return 0;
}
static int event_loop(struct irqtop_ctl *ctl, struct irq_output *out)
{
int efd, sfd, tfd;
sigset_t sigmask;
struct signalfd_siginfo siginfo;
struct epoll_event ev, events[MAX_EVENTS];
long int nr;
uint64_t unused;
int retval = 0;
efd = epoll_create1(0);
if ((tfd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0)
err(EXIT_FAILURE, _("cannot not create timerfd"));
if (timerfd_settime(tfd, 0, &ctl->timer, NULL) != 0)
err(EXIT_FAILURE, _("cannot set timerfd"));
ev.events = EPOLLIN;
ev.data.fd = tfd;
if (epoll_ctl(efd, EPOLL_CTL_ADD, tfd, &ev) != 0)
err(EXIT_FAILURE, _("epoll_ctl failed"));
if (sigfillset(&sigmask) != 0)
err(EXIT_FAILURE, _("sigfillset failed"));
if (sigprocmask(SIG_BLOCK, &sigmask, NULL) != 0)
err(EXIT_FAILURE, _("sigprocmask failed"));
sigaddset(&sigmask, SIGWINCH);
sigaddset(&sigmask, SIGTERM);
sigaddset(&sigmask, SIGINT);
sigaddset(&sigmask, SIGQUIT);
if ((sfd = signalfd(-1, &sigmask, SFD_CLOEXEC)) < 0)
err(EXIT_FAILURE, _("cannot not create signalfd"));
ev.events = EPOLLIN;
ev.data.fd = sfd;
if (epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &ev) != 0)
err(EXIT_FAILURE, _("epoll_ctl failed"));
ev.events = EPOLLIN;
ev.data.fd = STDIN_FILENO;
if (epoll_ctl(efd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) != 0)
err(EXIT_FAILURE, _("epoll_ctl failed"));
retval |= update_screen(ctl, out);
refresh();
while (!ctl->request_exit) {
const ssize_t nr_events = epoll_wait(efd, events, MAX_EVENTS, -1);
for (nr = 0; nr < nr_events; nr++) {
if (events[nr].data.fd == tfd) {
if (read(tfd, &unused, sizeof(unused)) < 0)
warn(_("read failed"));
} else if (events[nr].data.fd == sfd) {
if (read(sfd, &siginfo, sizeof(siginfo)) < 0) {
warn(_("read failed"));
continue;
}
if (siginfo.ssi_signo == SIGWINCH) {
get_terminal_dimension(&ctl->cols, &ctl->rows);
resizeterm(ctl->rows, ctl->cols);
}
else {
ctl->request_exit = 1;
break;
}
} else if (events[nr].data.fd == STDIN_FILENO) {
char c;
if (read(STDIN_FILENO, &c, 1) != 1)
warn(_("read failed"));
parse_input(ctl, out, c);
} else
abort();
retval |= update_screen(ctl, out);
refresh();
}
}
return retval;
}
static void __attribute__((__noreturn__)) usage(void)
{
fputs(USAGE_HEADER, stdout);
printf(_(" %s [options]\n"), program_invocation_short_name);
fputs(USAGE_SEPARATOR, stdout);
puts(_("Interactive utility to display kernel interrupt information."));
fputs(USAGE_OPTIONS, stdout);
fputs(_(" -d, --delay <secs> delay updates\n"), stdout);
fputs(_(" -o, --output <list> define which output columns to use\n"), stdout);
fputs(_(" -s, --sort <column> specify sort column\n"), stdout);
fputs(USAGE_SEPARATOR, stdout);
printf(USAGE_HELP_OPTIONS(22));
fputs(_("\nThe following interactive key commands are valid:\n"), stdout);
fputs(_(" i sort by IRQ\n"), stdout);
fputs(_(" t sort by TOTAL\n"), stdout);
fputs(_(" d sort by DELTA\n"), stdout);
fputs(_(" n sort by NAME\n"), stdout);
fputs(_(" q Q quit program\n"), stdout);
fputs(USAGE_COLUMNS, stdout);
irq_print_columns(stdout, 0);
printf(USAGE_MAN_TAIL("irqtop(1)"));
exit(EXIT_SUCCESS);
}
static void parse_args( struct irqtop_ctl *ctl,
struct irq_output *out,
int argc,
char **argv)
{
const char *outarg = NULL;
static const struct option longopts[] = {
{"delay", required_argument, NULL, 'd'},
{"sort", required_argument, NULL, 's'},
{"output", required_argument, NULL, 'o'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
int o;
while ((o = getopt_long(argc, argv, "d:o:s:hV", longopts, NULL)) != -1) {
switch (o) {
case 'd':
{
struct timeval delay;
strtotimeval_or_err(optarg, &delay,
_("failed to parse delay argument"));
TIMEVAL_TO_TIMESPEC(&delay, &ctl->timer.it_interval);
ctl->timer.it_value = ctl->timer.it_interval;
}
break;
case 's':
set_sort_func_by_name(out, optarg);
break;
case 'o':
outarg = optarg;
break;
case 'V':
print_version(EXIT_SUCCESS);
case 'h':
usage();
default:
errtryhelp(EXIT_FAILURE);
}
}
/* default */
if (!out->ncolumns) {
out->columns[out->ncolumns++] = COL_IRQ;
out->columns[out->ncolumns++] = COL_TOTAL;
out->columns[out->ncolumns++] = COL_DELTA;
out->columns[out->ncolumns++] = COL_NAME;
}
/* add -o [+]<list> to putput */
if (outarg && string_add_to_idarray(outarg, out->columns,
ARRAY_SIZE(out->columns),
&out->ncolumns,
irq_column_name_to_id) < 0)
exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
int is_tty = 0;
struct termios saved_tty;
struct irq_output out = {
.ncolumns = 0
};
struct irqtop_ctl ctl = {
.timer.it_interval = {3, 0},
.timer.it_value = {3, 0}
};
setlocale(LC_ALL, "");
parse_args(&ctl, &out, argc, argv);
is_tty = isatty(STDIN_FILENO);
if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1)
fputs(_("terminal setting retrieval"), stdout);
ctl.win = initscr();
get_terminal_dimension(&ctl.cols, &ctl.rows);
resizeterm(ctl.rows, ctl.cols);
curs_set(0);
ctl.hostname = xgethostname();
event_loop(&ctl, &out);
free_irqstat(ctl.prev_stat);
free(ctl.hostname);
if (is_tty)
tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty);
delwin(ctl.win);
endwin();
return EXIT_SUCCESS;
}

59
sys-utils/lsirq.1 Normal file
View File

@ -0,0 +1,59 @@
.TH IRQTOP "1" "February 2020" "util-linux" "User Commands"
.SH NAME
lsirq \- utility to display kernel interrupt information
.SH SYNOPSIS
.B lsirq
[options]
.SH DESCRIPTION
Display kernel interrupt counter information.
.PP
The default output is subject to change. So whenever possible, you should
avoid using default outputs in your scripts. Always explicitly define
expected columns by using
.BR \-\-output .
.SH OPTIONS
.TP
.BR \-n , " \-\-noheadings
Don't print headings.
.TP
.BR \-o , " \-\-output " \fIlist\fP
Specify which output columns to print. Use
.B \-\-help
to get a list of all supported columns. The default list of columns may be
extended if list is specified in the format
.IR +list .
.TP
.BR \-s , " \-\-sort " \fIcolumn\fP
Specify sort criteria by column name. See
.B \-\-help
output to get column names.
.TP
.BR \-J , " \-\-json
Use JSON output format.
.TP
.BR \-P , " \-\-pairs
Produce output in the form of key="value" pairs. All potentially unsafe characters
are hex-escaped (\\x<code>).
.TP
.BR \-V ", " \-\-version
Display version information and exit.
.TP
.BR \-h ,\ \-\-help
Display help text and exit.
.SH AUTHORS
.MT pizhenwei@\:bytedance.com
Zhenwei Pi
.ME
.br
.MT kerolasa@\:iki.fi
Sami Kerola
.ME
.br
.MT kzak@\:redhat.com
Karel Zak
.ME
.SH AVAILABILITY
The example command is part of the util-linux package and is available from
.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
Linux Kernel Archive
.UE .

146
sys-utils/lsirq.c Normal file
View File

@ -0,0 +1,146 @@
/*
* lsirq - utility to display kernel interrupt information.
*
* Copyright (C) 2019 zhenwei pi <pizhenwei@bytedance.com>
* Copyright (C) 2020 Karel Zak <kzak@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <libsmartcols.h>
#include "closestream.h"
#include "optutils.h"
#include "strutils.h"
#include "xalloc.h"
#include "irq-common.h"
static int print_irq_data(struct irq_output *out)
{
struct libscols_table *table;
table = get_scols_table(out, NULL, NULL);
if (!table)
return -1;
scols_print_table(table);
scols_unref_table(table);
return 0;
}
static void __attribute__((__noreturn__)) usage(void)
{
fputs(USAGE_HEADER, stdout);
printf(_(" %s [options]\n"), program_invocation_short_name);
fputs(USAGE_SEPARATOR, stdout);
puts(_("Utility to display kernel interrupt information."));
fputs(USAGE_OPTIONS, stdout);
fputs(_(" -J, --json use JSON output format\n"), stdout);
fputs(_(" -P, --pairs use key=\"value\" output format\n"), stdout);
fputs(_(" -n, --noheadings don't print headings\n"), stdout);
fputs(_(" -o, --output <list> define which output columns to use\n"), stdout);
fputs(_(" -s, --sort <column> specify sort column\n"), stdout);
fputs(USAGE_SEPARATOR, stdout);
printf(USAGE_HELP_OPTIONS(22));
fputs(USAGE_COLUMNS, stdout);
irq_print_columns(stdout, 1);
printf(USAGE_MAN_TAIL("lsirq(1)"));
exit(EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
struct irq_output out = {
.ncolumns = 0
};
static const struct option longopts[] = {
{"sort", required_argument, NULL, 's'},
{"noheadings", no_argument, NULL, 'n'},
{"output", required_argument, NULL, 'o'},
{"json", no_argument, NULL, 'J'},
{"pairs", no_argument, NULL, 'P'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
int c;
const char *outarg = NULL;
static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
{'J', 'P'},
{0}
};
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
setlocale(LC_ALL, "");
while ((c = getopt_long(argc, argv, "no:s:hJPV", longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
switch (c) {
case 'J':
out.json = 1;
break;
case 'P':
out.pairs = 1;
break;
case 'n':
out.no_headings = 1;
break;
case 'o':
outarg = optarg;
break;
case 's':
set_sort_func_by_name(&out, optarg);
break;
case 'V':
print_version(EXIT_SUCCESS);
case 'h':
usage();
default:
errtryhelp(EXIT_FAILURE);
}
}
/* default */
if (!out.ncolumns) {
out.columns[out.ncolumns++] = COL_IRQ;
out.columns[out.ncolumns++] = COL_TOTAL;
out.columns[out.ncolumns++] = COL_NAME;
}
/* add -o [+]<list> to putput */
if (outarg && string_add_to_idarray(outarg, out.columns,
ARRAY_SIZE(out.columns),
&out.ncolumns,
irq_column_name_to_id) < 0)
exit(EXIT_FAILURE);
return print_irq_data(&out) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}