[PATCH v1] cyclictest: Generate optstring dynamically from long_options

Costa Shulyupin
2026-01-10T11:37:58Z

The hard-coded optstring in cyclictest has become bloated and
error-prone, even resulting in a duplicate 'D:' entry.

Modify the long_options array to use the 'val' member of struct option
to store the corresponding short-option character. Add getopt_auto() to
automatically construct the optstring from this array at runtime.

This eliminates the need to manually maintain a redundant optstring and
prevents synchronization errors when adding or modifying command-line
options.

Signed-off-by: Costa Shulyupin <costa.shul@redhat.com>
---
Makefile | 3 +-
src/cyclictest/cyclictest.c | 147 +++++++++++++++---------------------
src/include/opt.h | 11 +++
src/lib/opt.c | 29 +++++++
4 files changed, 101 insertions(+), 89 deletions(-)
create mode 100644 src/include/opt.h
create mode 100644 src/lib/opt.c

diff --git a/Makefile b/Makefile
index e3d02cd25924..12bd42850bb1 100644
--- a/Makefile
+++ b/Makefile
@@ -194,7 +194,8 @@ oslat: $(OBJDIR)/oslat.o $(OBJDIR)/librttest.a $(OBJDIR)/librttestnuma.a
bzip2 -c $< > $@

LIBOBJS =$(addprefix $(OBJDIR)/,rt-error.o rt-get_cpu.o rt-sched.o rt-utils.o \
- histogram.o)
+ histogram.o opt.o)
+
$(OBJDIR)/librttest.a: $(LIBOBJS)
$(AR) rcs $@ $^

diff --git a/src/cyclictest/cyclictest.c b/src/cyclictest/cyclictest.c
index b4bce8915b43..a1566159d398 100644
--- a/src/cyclictest/cyclictest.c
+++ b/src/cyclictest/cyclictest.c
@@ -17,7 +17,6 @@
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
-#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <sched.h>
@@ -39,6 +38,7 @@
#include "rt-numa.h"
#include "rt-error.h"
#include "histogram.h"
+#include "opt.h"

#include <bionic.h>

@@ -1078,17 +1078,10 @@ static char *policyname(int policy)

enum option_values {
- OPT_AFFINITY=1, OPT_BREAKTRACE, OPT_CLOCK,
- OPT_DEFAULT_SYSTEM, OPT_DISTANCE, OPT_DURATION, OPT_LATENCY,
- OPT_FIFO, OPT_HISTOGRAM, OPT_HISTOFALL, OPT_HISTFILE,
- OPT_INTERVAL, OPT_JSON, OPT_MAINAFFINITY, OPT_LOOPS, OPT_MLOCKALL,
- OPT_REFRESH, OPT_NANOSLEEP, OPT_NSECS, OPT_OSCOPE, OPT_PRIORITY,
- OPT_QUIET, OPT_PRIOSPREAD, OPT_RELATIVE, OPT_RESOLUTION,
- OPT_SYSTEM, OPT_SMP, OPT_THREADS, OPT_TRIGGER,
- OPT_TRIGGER_NODES, OPT_UNBUFFERED, OPT_NUMA, OPT_VERBOSE,
- OPT_DBGCYCLIC, OPT_POLICY, OPT_HELP, OPT_NUMOPTS,
- OPT_ALIGNED, OPT_SECALIGNED, OPT_LAPTOP, OPT_SMI,
- OPT_TRACEMARK, OPT_POSIX_TIMERS, OPT_DEEPEST_IDLE_STATE,
+ OPT_DEFAULT_SYSTEM, OPT_LATENCY, OPT_HISTFILE, OPT_JSON,
+ OPT_MAINAFFINITY, OPT_PRIOSPREAD, OPT_SECALIGNED, OPT_SMI,
+ OPT_TRIGGER, OPT_TRIGGER_NODES, OPT_TRACEMARK, OPT_DBGCYCLIC,
+ OPT_POLICY, OPT_HELP, OPT_LAPTOP, OPT_DEEPEST_IDLE_STATE,
};

/* Process commandline options */
@@ -1097,63 +1090,63 @@ static void process_options(int argc, char *argv[], int max_cpus)
int error = 0;
int option_affinity = 0;

+ /*
+ * Options for getopt
+ * Ordered alphabetically by single letter name
+ */
+ static struct option long_options[] = {
+ {"affinity", optional_argument, NULL, 'a'},
+ {"aligned", optional_argument, NULL, 'A'},
+ {"breaktrace", required_argument, NULL, 'b'},
+ {"clock", required_argument, NULL, 'c'},
+ {"default-system", no_argument, NULL, OPT_DEFAULT_SYSTEM },
+ {"distance", required_argument, NULL, 'd'},
+ {"duration", required_argument, NULL, 'D'},
+ {"latency", required_argument, NULL, OPT_LATENCY },
+ {"fifo", required_argument, NULL, 'F'},
+ {"histogram", required_argument, NULL, 'h'},
+ {"histofall", required_argument, NULL, 'H'},
+ {"histfile", required_argument, NULL, OPT_HISTFILE },
+ {"interval", required_argument, NULL, 'i'},
+ {"json", required_argument, NULL, OPT_JSON },
+ {"laptop", no_argument, NULL, OPT_LAPTOP },
+ {"loops", required_argument, NULL, 'l'},
+ {"mainaffinity", required_argument, NULL, OPT_MAINAFFINITY},
+ {"mlockall", no_argument, NULL, 'm'},
+ {"refresh_on_max", no_argument, NULL, 'M'},
+ {"nsecs", no_argument, NULL, 'N'},
+ {"oscope", required_argument, NULL, 'o'},
+ {"priority", required_argument, NULL, 'p'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"priospread", no_argument, NULL, OPT_PRIOSPREAD },
+ {"relative", no_argument, NULL, 'r'},
+ {"resolution", no_argument, NULL, 'R'},
+ {"secaligned", optional_argument, NULL, OPT_SECALIGNED },
+ {"system", no_argument, NULL, 's'},
+ {"smi", no_argument, NULL, OPT_SMI },
+ {"smp", no_argument, NULL, 'S'},
+ {"spike", required_argument, NULL, OPT_TRIGGER },
+ {"spike-nodes", required_argument, NULL, OPT_TRIGGER_NODES },
+ {"threads", optional_argument, NULL, 't'},
+ {"tracemark", no_argument, NULL, OPT_TRACEMARK },
+ {"unbuffered", no_argument, NULL, 'u'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"dbg_cyclictest", no_argument, NULL, OPT_DBGCYCLIC },
+ {"policy", required_argument, NULL, OPT_POLICY },
+ {"help", no_argument, NULL, OPT_HELP },
+ {"posix_timers", no_argument, NULL, 'x'},
+ {"deepest-idle-state", required_argument, NULL, OPT_DEEPEST_IDLE_STATE },
+ {NULL, 0, NULL, 0 },
+ };
+ char *opts = build_optstring(long_options);
+
for (;;) {
- int option_index = 0;
- /*
- * Options for getopt
- * Ordered alphabetically by single letter name
- */
- static struct option long_options[] = {
- {"affinity", optional_argument, NULL, OPT_AFFINITY},
- {"aligned", optional_argument, NULL, OPT_ALIGNED },
- {"breaktrace", required_argument, NULL, OPT_BREAKTRACE },
- {"clock", required_argument, NULL, OPT_CLOCK },
- {"default-system", no_argument, NULL, OPT_DEFAULT_SYSTEM },
- {"distance", required_argument, NULL, OPT_DISTANCE },
- {"duration", required_argument, NULL, OPT_DURATION },
- {"latency", required_argument, NULL, OPT_LATENCY },
- {"fifo", required_argument, NULL, OPT_FIFO },
- {"histogram", required_argument, NULL, OPT_HISTOGRAM },
- {"histofall", required_argument, NULL, OPT_HISTOFALL },
- {"histfile", required_argument, NULL, OPT_HISTFILE },
- {"interval", required_argument, NULL, OPT_INTERVAL },
- {"json", required_argument, NULL, OPT_JSON },
- {"laptop", no_argument, NULL, OPT_LAPTOP },
- {"loops", required_argument, NULL, OPT_LOOPS },
- {"mainaffinity", required_argument, NULL, OPT_MAINAFFINITY},
- {"mlockall", no_argument, NULL, OPT_MLOCKALL },
- {"refresh_on_max", no_argument, NULL, OPT_REFRESH },
- {"nsecs", no_argument, NULL, OPT_NSECS },
- {"oscope", required_argument, NULL, OPT_OSCOPE },
- {"priority", required_argument, NULL, OPT_PRIORITY },
- {"quiet", no_argument, NULL, OPT_QUIET },
- {"priospread", no_argument, NULL, OPT_PRIOSPREAD },
- {"relative", no_argument, NULL, OPT_RELATIVE },
- {"resolution", no_argument, NULL, OPT_RESOLUTION },
- {"secaligned", optional_argument, NULL, OPT_SECALIGNED },
- {"system", no_argument, NULL, OPT_SYSTEM },
- {"smi", no_argument, NULL, OPT_SMI },
- {"smp", no_argument, NULL, OPT_SMP },
- {"spike", required_argument, NULL, OPT_TRIGGER },
- {"spike-nodes", required_argument, NULL, OPT_TRIGGER_NODES },
- {"threads", optional_argument, NULL, OPT_THREADS },
- {"tracemark", no_argument, NULL, OPT_TRACEMARK },
- {"unbuffered", no_argument, NULL, OPT_UNBUFFERED },
- {"verbose", no_argument, NULL, OPT_VERBOSE },
- {"dbg_cyclictest", no_argument, NULL, OPT_DBGCYCLIC },
- {"policy", required_argument, NULL, OPT_POLICY },
- {"help", no_argument, NULL, OPT_HELP },
- {"posix_timers", no_argument, NULL, OPT_POSIX_TIMERS },
- {"deepest-idle-state", required_argument, NULL, OPT_DEEPEST_IDLE_STATE },
- {NULL, 0, NULL, 0 },
- };
- int c = getopt_long(argc, argv, "a::A::b:c:d:D:F:h:H:i:l:MNo:p:mqrRsSt::uvD:x",
- long_options, &option_index);
+ int c = getopt_long(argc, argv, opts, long_options, NULL);
+
if (c == -1)
break;
switch (c) {
case 'a':
- case OPT_AFFINITY:
option_affinity = 1;
/* smp sets AFFINITY_USEALL in OPT_SMP */
if (smp)
@@ -1181,7 +1174,6 @@ static void process_options(int argc, char *argv[], int max_cpus)
numa_bitmask_weight(affinity_mask));
break;
case 'A':
- case OPT_ALIGNED:
aligned = 1;
if (optarg != NULL)
offset = atoi(optarg) * 1000;
@@ -1191,29 +1183,22 @@ static void process_options(int argc, char *argv[], int max_cpus)
offset = 0;
break;
case 'b':
- case OPT_BREAKTRACE:
tracelimit = atoi(optarg); break;
case 'c':
- case OPT_CLOCK:
clocksel = atoi(optarg); break;
case OPT_DEFAULT_SYSTEM:
power_management = 1; break;
case 'd':
- case OPT_DISTANCE:
distance = atoi(optarg); break;
case 'D':
- case OPT_DURATION:
duration = parse_time_string(optarg); break;
case 'F':
- case OPT_FIFO:
use_fifo = 1;
strncpy(fifopath, optarg, strnlen(optarg, MAX_PATH-1));
break;
case 'H':
- case OPT_HISTOFALL:
histofall = 1; /* fall through */
case 'h':
- case OPT_HISTOGRAM:
histogram = atoi(optarg);
if (!histogram)
display_help(1);
@@ -1223,13 +1208,11 @@ static void process_options(int argc, char *argv[], int max_cpus)
strncpy(histfile, optarg, strnlen(optarg, MAX_PATH-1));
break;
case 'i':
- case OPT_INTERVAL:
interval = atoi(optarg); break;
case OPT_JSON:
strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
break;
case 'l':
- case OPT_LOOPS:
max_cycles = atoi(optarg); break;
case OPT_MAINAFFINITY:
if (optarg) {
@@ -1242,31 +1225,23 @@ static void process_options(int argc, char *argv[], int max_cpus)
}
break;
case 'm':
- case OPT_MLOCKALL:
lockall = 1; break;
case 'M':
- case OPT_REFRESH:
refresh_on_max = 1; break;
case 'N':
- case OPT_NSECS:
use_nsecs = 1; break;
case 'o':
- case OPT_OSCOPE:
oscope_reduction = atoi(optarg); break;
case 'p':
- case OPT_PRIORITY:
priority = atoi(optarg);
if (policy != SCHED_FIFO && policy != SCHED_RR)
policy = SCHED_FIFO;
break;
case 'q':
- case OPT_QUIET:
quiet = 1; break;
case 'r':
- case OPT_RELATIVE:
timermode = TIMER_RELTIME; break;
case 'R':
- case OPT_RESOLUTION:
check_clock_resolution = 1; break;
case OPT_SECALIGNED:
secaligned = 1;
@@ -1278,10 +1253,8 @@ static void process_options(int argc, char *argv[], int max_cpus)
offset = 0;
break;
case 's':
- case OPT_SYSTEM:
use_system = MODE_SYS_OFFSET; break;
case 'S':
- case OPT_SMP: /* SMP testing */
if (numa)
fatal("numa and smp options are mutually exclusive\n");
smp = 1;
@@ -1289,7 +1262,6 @@ static void process_options(int argc, char *argv[], int max_cpus)
setaffinity = AFFINITY_USEALL;
break;
case 't':
- case OPT_THREADS:
if (smp) {
warn("-t ignored due to smp mode\n");
break;
@@ -1309,12 +1281,10 @@ static void process_options(int argc, char *argv[], int max_cpus)
trigger_list_size = atoi(optarg);
break;
case 'u':
- case OPT_UNBUFFERED:
setvbuf(stdout, NULL, _IONBF, 0); break;
case 'v':
- case OPT_VERBOSE: verbose = 1; break;
+ verbose = 1; break;
case 'x':
- case OPT_POSIX_TIMERS:
use_nanosleep = MODE_CYCLIC; break;
case '?':
case OPT_HELP:
@@ -1437,6 +1407,7 @@ static void process_options(int argc, char *argv[], int max_cpus)
rt_bitmask_free(affinity_mask);
display_help(1);
}
+ free(opts);
}

static int check_timer(void)
diff --git a/src/include/opt.h b/src/include/opt.h
new file mode 100644
index 000000000000..4d01917be024
--- /dev/null
+++ b/src/include/opt.h
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#include <getopt.h>
+
+/*
+ * build_optstring - allocate and build optstring from long_options
+ *
+ * Free resulted optstring aftre use.
+ */
+char *build_optstring(const struct option *long_opts);
diff --git a/src/lib/opt.c b/src/lib/opt.c
new file mode 100644
index 000000000000..d235788001d9
--- /dev/null
+++ b/src/lib/opt.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <stdlib.h>
+
+#include "opt.h"
+
+char *build_optstring(const struct option *long_opts)
+{
+ char *opts = NULL;
+ int n = 0;
+
+ for (int i = 0; long_opts[i].name; i++) {
+ opts = realloc(opts, n + 4);
+
+ if (long_opts[i].flag || long_opts[i].val < 32 || long_opts[i].val > 127)
+ continue;
+
+ opts[n++] = long_opts[i].val;
+
+ if (long_opts[i].has_arg == required_argument)
+ opts[n++] = ':';
+ else if (long_opts[i].has_arg == optional_argument) {
+ opts[n++] = ':';
+ opts[n++] = ':';
+ }
+ }
+ opts[n] = '\0';
+
+ return opts;
+}
--
2.52.0