[PATCH 3/3] lsirq,irqtop: add support for reading data from given file

Joe Jin
2025-02-27T04:50:03Z

This is helpful for analyzng data saved from other system.

Signed-off-by: Joe Jin <joe.jin@oracle.com>
Cc: Zhenwei Pi <pizhenwei@bytedance.com>
Cc: Sami Kerola <kerolasa@iki.fi>
---
bash-completion/irqtop | 4 ++++
bash-completion/lsirq | 4 ++++
sys-utils/irq-common.c | 19 +++++++++----------
sys-utils/irq-common.h | 3 ++-
sys-utils/irqtop.1.adoc | 3 +++
sys-utils/irqtop.c | 28 +++++++++++++++++++++++++---
sys-utils/lsirq.1.adoc | 3 +++
sys-utils/lsirq.c | 28 ++++++++++++++++++++++++----
8 files changed, 74 insertions(+), 18 deletions(-)

diff --git a/bash-completion/irqtop b/bash-completion/irqtop
index 47b7b0af6..3bea5fc0e 100644
--- a/bash-completion/irqtop
+++ b/bash-completion/irqtop
@@ -15,6 +15,9 @@ _irqtop_module()
'-C'|'--cpu-list')
return 0
;;
+ '-i'|'--input')
+ COMPREPLY=( $(compgen -W "input file" -- $cur) )
+ ;;
'-t'|'--threshold')
return 0
;;
@@ -51,6 +54,7 @@ _irqtop_module()
--cpu-stat
--cpu-list
--delay
+ --input
--number
--sort
--output
diff --git a/bash-completion/lsirq b/bash-completion/lsirq
index 4c3c9f04f..b913eecd0 100644
--- a/bash-completion/lsirq
+++ b/bash-completion/lsirq
@@ -5,6 +5,9 @@ _lsirq_module()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
case $prev in
+ '-i'|'--input')
+ COMPREPLY=( $(compgen -W "input file" -- $cur) )
+ ;;
'-o'|'--output')
local prefix realcur OUTPUT
realcur="${cur##*,}"
@@ -35,6 +38,7 @@ _lsirq_module()
OPTS=" --json
--pairs
--noheadings
+ --input
--output
--softirq
--sort
diff --git a/sys-utils/irq-common.c b/sys-utils/irq-common.c
index f069d8a63..560dd4b82 100644
--- a/sys-utils/irq-common.c
+++ b/sys-utils/irq-common.c
@@ -233,7 +233,8 @@ static bool cpu_in_list(int cpu, size_t setsize, cpu_set_t *cpuset)
/*
* irqinfo - parse the system's interrupts
*/
-static struct irq_stat *get_irqinfo(int softirq, size_t setsize, cpu_set_t *cpuset)
+static struct irq_stat *get_irqinfo(const char *input_file, int softirq,
+ size_t setsize, cpu_set_t *cpuset)
{
FILE *irqfile;
char *line = NULL, *tmp;
@@ -247,18 +248,15 @@ static struct irq_stat *get_irqinfo(int softirq, size_t setsize, cpu_set_t *cpus
stat->irq_info = xmalloc(sizeof(*stat->irq_info) * IRQ_INFO_LEN);
stat->nr_irq_info = IRQ_INFO_LEN;

- if (softirq)
- irqfile = fopen(_PATH_PROC_SOFTIRQS, "r");
- else
- irqfile = fopen(_PATH_PROC_INTERRUPTS, "r");
+ irqfile = fopen(input_file, "r");
if (!irqfile) {
- warn(_("cannot open %s"), _PATH_PROC_INTERRUPTS);
+ warn(_("cannot open %s"), input_file);
goto free_stat;
}

/* read header firstly */
if (getline(&line, &len, irqfile) < 0) {
- warn(_("cannot read %s"), _PATH_PROC_INTERRUPTS);
+ warn(_("cannot read %s"), input_file);
goto close_file;
}

@@ -270,7 +268,7 @@ static struct irq_stat *get_irqinfo(int softirq, size_t setsize, cpu_set_t *cpus

stat->cpus = xcalloc(stat->nr_active_cpu, sizeof(struct irq_cpu));

- /* parse each line of _PATH_PROC_INTERRUPTS */
+ /* parse each line of input file */
while (getline(&line, &len, irqfile) >= 0) {
unsigned long count;
size_t index;
@@ -527,7 +525,8 @@ struct libscols_table *get_scols_cpus_table(struct irq_output *out,
return NULL;
}

-struct libscols_table *get_scols_table(struct irq_output *out,
+struct libscols_table *get_scols_table(const char *input_file,
+ struct irq_output *out,
struct irq_stat *prev,
struct irq_stat **xstat,
int softirq,
@@ -542,7 +541,7 @@ struct libscols_table *get_scols_table(struct irq_output *out,
size_t i;

/* the stats */
- stat = get_irqinfo(softirq, setsize, cpuset);
+ stat = get_irqinfo(input_file, softirq, setsize, cpuset);
if (!stat)
return NULL;

diff --git a/sys-utils/irq-common.h b/sys-utils/irq-common.h
index 02b72d752..b9cf72d2a 100644
--- a/sys-utils/irq-common.h
+++ b/sys-utils/irq-common.h
@@ -73,7 +73,8 @@ 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 libscols_table *get_scols_table(const char *input_file,
+ struct irq_output *out,
struct irq_stat *prev,
struct irq_stat **xstat,
int softirq,
diff --git a/sys-utils/irqtop.1.adoc b/sys-utils/irqtop.1.adoc
index 75cfe2e41..715008d07 100644
--- a/sys-utils/irqtop.1.adoc
+++ b/sys-utils/irqtop.1.adoc
@@ -37,6 +37,9 @@ Specify cpus in list format to show.
*-d*, *--delay* _seconds_::
Update interrupt output every _seconds_ intervals.

+*-i*, *--input* _file_::
+Read data from _file_ (Which was created by other tools, e.g. sosreport).
+
*-n*, *--number* _number_::
Specifies the maximum _number_ of iterations before quitting.

diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c
index ba5680671..4cf1dc79a 100644
--- a/sys-utils/irqtop.c
+++ b/sys-utils/irqtop.c
@@ -87,6 +87,8 @@ struct irqtop_ctl {
bool batch;
bool request_exit,
softirq;
+
+ char *input;
};

#define irqtop_batch_mode(ctl) ((ctl)->batch == true)
@@ -122,8 +124,9 @@ static int update_screen(struct irqtop_ctl *ctl, struct irq_output *out)
char timestr[64], *data, *data0, *p;

/* make irqs table */
- table = get_scols_table(out, ctl->prev_stat, &stat, ctl->softirq,
- ctl->threshold, ctl->setsize, ctl->cpuset);
+ table = get_scols_table(ctl->input, out, ctl->prev_stat, &stat,
+ ctl->softirq, ctl->threshold, ctl->setsize,
+ ctl->cpuset);
if (!table) {
ctl->request_exit = 1;
return 1;
@@ -292,6 +295,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -c, --cpu-stat <mode> show per-cpu stat (auto, enable, disable)\n"), stdout);
fputs(_(" -C, --cpu-list specify cpus in list format\n"), stdout);
fputs(_(" -d, --delay <secs> delay updates\n"), stdout);
+ fputs(_(" -i, --input <file> read data from file\n"), stdout);
fputs(_(" -n, --number <number> the maximum number of iterations\n"), stdout);
fputs(_(" -o, --output define which output columns to use\n"), stdout);
fputs(_(" -s, --sort <column> specify sort column\n"), stdout);
@@ -325,6 +329,7 @@ static void parse_args( struct irqtop_ctl *ctl,
{"cpu-stat", required_argument, NULL, 'c'},
{"cpu-list", required_argument, NULL, 'C'},
{"delay", required_argument, NULL, 'd'},
+ {"input", required_argument, NULL, 'i'},
{"number", required_argument, NULL, 'n'},
{"sort", required_argument, NULL, 's'},
{"output", required_argument, NULL, 'o'},
@@ -336,7 +341,7 @@ static void parse_args( struct irqtop_ctl *ctl,
};
int o;

- while ((o = getopt_long(argc, argv, "bc:C:d:n:o:s:St:hV", longopts, NULL)) != -1) {
+ while ((o = getopt_long(argc, argv, "bc:C:d:i:n:o:s:St:hV", longopts, NULL)) != -1) {
switch (o) {
case 'b':
ctl->batch = true;
@@ -376,6 +381,13 @@ static void parse_args( struct irqtop_ctl *ctl,
ctl->timer.it_value = ctl->timer.it_interval;
}
break;
+ case 'i':
+ ctl->input = strdup(optarg);
+ if (!ctl->input)
+ err_oom();
+ ctl->number = 1;
+ ctl->batch = true;
+ break;
case 'n':
ctl->number = str2num_or_err(optarg, 10,
_("failed to parse number argument"),
@@ -402,6 +414,15 @@ static void parse_args( struct irqtop_ctl *ctl,
}
}

+ if (ctl->input == NULL) {
+ if (ctl->softirq == 1)
+ ctl->input = strdup(_PATH_PROC_SOFTIRQS);
+ else
+ ctl->input = strdup(_PATH_PROC_INTERRUPTS);
+ if (!ctl->input)
+ err_oom();
+ }
+
/* default */
if (!out->ncolumns) {
out->columns[out->ncolumns++] = COL_IRQ;
@@ -453,6 +474,7 @@ int main(int argc, char **argv)

free_irqstat(ctl.prev_stat);
free(ctl.hostname);
+ free(ctl.input);
cpuset_free(ctl.cpuset);

if (ctl.batch == false) {
diff --git a/sys-utils/lsirq.1.adoc b/sys-utils/lsirq.1.adoc
index 02aea16b3..dd265710c 100644
--- a/sys-utils/lsirq.1.adoc
+++ b/sys-utils/lsirq.1.adoc
@@ -25,6 +25,9 @@ The default output is subject to change. So whenever possible, you should avoid
*-n*, *--noheadings*::
Don't print headings.

+*-i*, *--input* _file_::
+Read data from _file_ (Which was created by other tools, e.g. sosreport).
+
*-o*, *--output* _list_::
Specify which output columns to print. Use *--help* to get a list of all supported columns. The default list of columns may be extended if list is specified in the format _+list_.

diff --git a/sys-utils/lsirq.c b/sys-utils/lsirq.c
index e31addaf5..45d542919 100644
--- a/sys-utils/lsirq.c
+++ b/sys-utils/lsirq.c
@@ -29,16 +29,17 @@
#include "optutils.h"
#include "strutils.h"
#include "xalloc.h"
+#include "pathnames.h"

#include "irq-common.h"

-static int print_irq_data(struct irq_output *out,
+static int print_irq_data(const char *input_file, struct irq_output *out,
int softirq, unsigned long threshold,
size_t setsize, cpu_set_t *cpuset)
{
struct libscols_table *table;

- table = get_scols_table(out, NULL, NULL, softirq, threshold, setsize, cpuset);
+ table = get_scols_table(input_file, out, NULL, NULL, softirq, threshold, setsize, cpuset);
if (!table)
return -1;

@@ -58,6 +59,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(USAGE_OPTIONS, stdout);
fputs(_(" -J, --json use JSON output format\n"), stdout);
fputs(_(" -P, --pairs use key=\"value\" output format\n"), stdout);
+ fputs(_(" -i, --input read data from input file\n"), stdout);
fputs(_(" -n, --noheadings don't print headings\n"), stdout);
fputs(_(" -o, --output define which output columns to use\n"), stdout);
fputs(_(" -s, --sort <column> specify sort column\n"), stdout);
@@ -82,6 +84,7 @@ int main(int argc, char **argv)
static const struct option longopts[] = {
{"sort", required_argument, NULL, 's'},
{"noheadings", no_argument, NULL, 'n'},
+ {"input", required_argument, NULL, 'i'},
{"output", required_argument, NULL, 'o'},
{"threshold", required_argument, NULL, 't'},
{"cpu-list", required_argument, NULL, 'C'},
@@ -103,10 +106,11 @@ int main(int argc, char **argv)
cpu_set_t *cpuset = NULL;
size_t setsize = 0;
int softirq = 0;
+ char *input = NULL;

setlocale(LC_ALL, "");

- while ((c = getopt_long(argc, argv, "no:s:t:C:ShJPV", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "i:no:s:t:C:ShJPV", longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);

switch (c) {
@@ -116,6 +120,11 @@ int main(int argc, char **argv)
case 'P':
out.pairs = 1;
break;
+ case 'i':
+ input = strdup(optarg);
+ if (!input)
+ err_oom();
+ break;
case 'n':
out.no_headings = 1;
break;
@@ -157,6 +166,15 @@ int main(int argc, char **argv)
}
}

+ if (input == NULL) {
+ if (softirq == 1)
+ input = strdup(_PATH_PROC_SOFTIRQS);
+ else
+ input = strdup(_PATH_PROC_INTERRUPTS);
+ if (!input)
+ err_oom();
+ }
+
/* default */
if (!out.ncolumns) {
out.columns[out.ncolumns++] = COL_IRQ;
@@ -171,8 +189,10 @@ int main(int argc, char **argv)
irq_column_name_to_id) < 0)
exit(EXIT_FAILURE);

- if (print_irq_data(&out, softirq, threshold, setsize, cpuset) < 0)
+ if (print_irq_data(input, &out, softirq, threshold, setsize, cpuset) < 0)
return EXIT_FAILURE;

+ free(input);
+
return EXIT_SUCCESS;
}
--
2.43.5