Merge branch 'topic/irq'
This commit is contained in:
commit
a8fe698c45
|
@ -103,6 +103,7 @@ ylwrap
|
|||
/ipcmk
|
||||
/ipcrm
|
||||
/ipcs
|
||||
/irqtop
|
||||
/isosize
|
||||
/kill
|
||||
/last
|
||||
|
@ -114,6 +115,7 @@ ylwrap
|
|||
/losetup
|
||||
/lsblk
|
||||
/lsipc
|
||||
/lsirq
|
||||
/lscpu
|
||||
/lslocks
|
||||
/lslogins
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
22
configure.ac
22
configure.ac
|
@ -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])
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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 .
|
|
@ -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;
|
||||
}
|
|
@ -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 .
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue