* [PATCH 01/12] rteval: Fix spelling of 'occurrences' in measurement modules
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 02/12] rteval: Fix typo in comment John Kacur
` (10 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur
Fixed typo 'occurances' -> 'occurrences' in both cyclictest.py and
timerlat.py measurement modules for consistency.
Signed-off-by: John Kacur <jkacur@redhat.com>
---
rteval/modules/measurement/cyclictest.py | 6 +++---
rteval/modules/measurement/timerlat.py | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py
index 3d25c20b69cb..f32ad2690f45 100644
--- a/rteval/modules/measurement/cyclictest.py
+++ b/rteval/modules/measurement/cyclictest.py
@@ -92,7 +92,7 @@ class RunData:
mid = int(self.__numsamples / 2) + 1
# mean, mode, and median
- occurances = 0
+ occurrences = 0
lastkey = -1
for i in keys:
if mid > total and mid <= total + self.__samples[i]:
@@ -104,8 +104,8 @@ class RunData:
lastkey = i
total += self.__samples[i]
total_us += (i * self.__samples[i])
- if self.__samples[i] > occurances:
- occurances = self.__samples[i]
+ if self.__samples[i] > occurrences:
+ occurrences = self.__samples[i]
self.__mode = i
self.__mean = float(total_us) / float(self.__numsamples)
diff --git a/rteval/modules/measurement/timerlat.py b/rteval/modules/measurement/timerlat.py
index 5bfc495217ea..733929666784 100644
--- a/rteval/modules/measurement/timerlat.py
+++ b/rteval/modules/measurement/timerlat.py
@@ -84,7 +84,7 @@ class TLRunData:
mid = int(self.__numsamples / 2) + 1
# mean, mode and median
- occurances = 0
+ occurrences = 0
lastkey = -1
for i in keys:
if mid > total and mid <= total + self.__samples[i]:
@@ -96,8 +96,8 @@ class TLRunData:
lastkey = i
total += self.__samples[i]
total_us += i * self.__samples[i]
- if self.__samples[i] > occurances:
- occurances = self.__samples[i]
+ if self.__samples[i] > occurrences:
+ occurrences = self.__samples[i]
self.__mode = i
self.__mean = float(total_us) / float(self.__numsamples)
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 02/12] rteval: Fix typo in comment
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
2025-11-07 18:26 ` [PATCH 01/12] rteval: Fix spelling of 'occurrences' in measurement modules John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 03/12] rteval: Remove unused function remove_offline John Kacur
` (9 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur
Fixed typo 'summay.xml' -> 'summary.xml' in rteval-cmd comment.
Signed-off-by: John Kacur <jkacur@redhat.com>
---
rteval-cmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rteval-cmd b/rteval-cmd
index 4e13d312a24a..b936ade23d86 100755
--- a/rteval-cmd
+++ b/rteval-cmd
@@ -68,7 +68,7 @@ def summarize(repfile, xslt):
xsltdoc = lxml.etree.parse(xsltfp)
xsltprs = lxml.etree.XSLT(xsltdoc)
- # Load the summay.xml report - with some simple sanity checks
+ # Load the summary.xml report - with some simple sanity checks
with open(summaryfile, "r") as xmlfp:
xmldoc = lxml.etree.parse(xmlfp)
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 03/12] rteval: Remove unused function remove_offline
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
2025-11-07 18:26 ` [PATCH 01/12] rteval: Fix spelling of 'occurrences' in measurement modules John Kacur
2025-11-07 18:26 ` [PATCH 02/12] rteval: Fix typo in comment John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 04/12] rteval: timerlat: Fix typo in log message John Kacur
` (8 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur
Remove unused function remove_offline()
Signed-off-by: John Kacur <jkacur@redhat.com>
---
rteval-cmd | 7 -------
1 file changed, 7 deletions(-)
diff --git a/rteval-cmd b/rteval-cmd
index b936ade23d86..7af179321fe2 100755
--- a/rteval-cmd
+++ b/rteval-cmd
@@ -207,13 +207,6 @@ def parse_options(cfg, parser, cmdargs):
return cmd_args
-def remove_offline(cpulist):
- """ return cpulist in collapsed compressed form with only online cpus """
- tmplist = expand_cpulist(cpulist)
- tmplist = SysTopology().online_cpulist(tmplist)
- return collapse_cpulist(tmplist)
-
-
if __name__ == '__main__':
from rteval.sysinfo import dmi
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 04/12] rteval: timerlat: Fix typo in log message
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
` (2 preceding siblings ...)
2025-11-07 18:26 ` [PATCH 03/12] rteval: Remove unused function remove_offline John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 05/12] rteval: cyclictest: Fix typo in comment John Kacur
` (7 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur
Fixed typo 'sampples' -> 'samples' in debug log message.
Signed-off-by: John Kacur <jkacur@redhat.com>
---
rteval/modules/measurement/timerlat.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rteval/modules/measurement/timerlat.py b/rteval/modules/measurement/timerlat.py
index 733929666784..95fb7f136c25 100644
--- a/rteval/modules/measurement/timerlat.py
+++ b/rteval/modules/measurement/timerlat.py
@@ -66,7 +66,7 @@ class TLRunData:
""" Calculate statistics """
# Check to see if we have any samples. If there are 1 or 0, return
if self.__numsamples <= 1:
- self._log(Log.DEBUG, f"skipping {self.__id} ({self.__numsamples} sampples)")
+ self._log(Log.DEBUG, f"skipping {self.__id} ({self.__numsamples} samples)")
self.__mad = 0
self.__stddev = 0
return
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 05/12] rteval: cyclictest: Fix typo in comment
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
` (3 preceding siblings ...)
2025-11-07 18:26 ` [PATCH 04/12] rteval: timerlat: Fix typo in log message John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 06/12] rteval: rtevalConfig: Remove redundant 'is True' comparison John Kacur
` (6 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur
Fixed typo 'runing' -> 'running' in comment.
Signed-off-by: John Kacur <jkacur@redhat.com>
---
rteval/modules/measurement/cyclictest.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py
index f32ad2690f45..f7039ab2749f 100644
--- a/rteval/modules/measurement/cyclictest.py
+++ b/rteval/modules/measurement/cyclictest.py
@@ -270,7 +270,7 @@ class Cyclictest(rtevalModulePrototype):
def _WorkloadTask(self):
if self.__started:
- # Don't restart cyclictest if it is already runing
+ # Don't restart cyclictest if it is already running
return
self._log(Log.DEBUG, f'starting with cmd: {" ".join(self.__cmd)}')
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 06/12] rteval: rtevalConfig: Remove redundant 'is True' comparison
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
` (4 preceding siblings ...)
2025-11-07 18:26 ` [PATCH 05/12] rteval: cyclictest: Fix typo in comment John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 07/12] rteval: Clean up MANIFEST.in and fix newnet.py copyright header John Kacur
` (5 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur
Simplified boolean check by removing redundant 'is True' comparison.
ConfigParsed() already returns a boolean value.
Signed-off-by: John Kacur <jkacur@redhat.com>
---
rteval/rtevalConfig.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rteval/rtevalConfig.py b/rteval/rtevalConfig.py
index 0c2fc6b5761a..e0922c5adda6 100644
--- a/rteval/rtevalConfig.py
+++ b/rteval/rtevalConfig.py
@@ -234,7 +234,7 @@ class rtevalConfig:
self.__info("no config file")
return
- if self.ConfigParsed(cfgfile) is True:
+ if self.ConfigParsed(cfgfile):
# Don't try to reread this file if it's already been parsed
return
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 07/12] rteval: Clean up MANIFEST.in and fix newnet.py copyright header
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
` (5 preceding siblings ...)
2025-11-07 18:26 ` [PATCH 06/12] rteval: rtevalConfig: Remove redundant 'is True' comparison John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 08/12] rteval: Add pyproject.toml for modern Python packaging John Kacur
` (4 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur
Remove unnecessary entries from MANIFEST.in (rteval-cmd inclusion and
dist/ exclusions) and fix the copyright header in newnet.py (remove
duplicate SPDX tag, fix missing email bracket, update year to 2025).
Signed-off-by: John Kacur <jkacur@redhat.com>
---
MANIFEST.in | 3 +--
rteval/sysinfo/newnet.py | 3 +--
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index 82560a8f957b..fa6ed3ab7de5 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,2 @@
-include COPYING MANIFEST.in rteval-cmd
+include COPYING MANIFEST.in
recursive-include doc *.? *.txt
-exclude dist/rteval dist/rteval.8.gz
diff --git a/rteval/sysinfo/newnet.py b/rteval/sysinfo/newnet.py
index 5dd95ae94e19..07427bb27771 100644
--- a/rteval/sysinfo/newnet.py
+++ b/rteval/sysinfo/newnet.py
@@ -1,8 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
''' Module to obtain network information for the rteval report '''
#
-# Copyright 2022 John Kacur <jkacur@redhat.com
-# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright 2022 - 2025 John Kacur <jkacur@redhat.com>
#
import os
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 08/12] rteval: Add pyproject.toml for modern Python packaging
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
` (6 preceding siblings ...)
2025-11-07 18:26 ` [PATCH 07/12] rteval: Clean up MANIFEST.in and fix newnet.py copyright header John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 09/12] rteval: Improve argparse implementation and remove manual sys.argv parsing John Kacur
` (3 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur, Claude
Add pyproject.toml to support modern PEP 517/518/621 packaging while
keeping setup.py for backwards compatibility with older distributions.
The pyproject.toml provides project metadata (name, version, dependencies,
authors) and works alongside setup.py which handles script installation
(copying rteval-cmd to rteval).
Add John Kacur to the list of authors and set him as the maintainer
in both pyproject.toml and setup.py metadata.
Requires Python >=3.10 and setuptools >=61.0.
Assisted-by: Claude <noreply@anthropic.com>
Signed-off-by: John Kacur <jkacur@redhat.com>
---
pyproject.toml | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++
rteval-cmd | 2 +-
setup.py | 6 ++++--
3 files changed, 63 insertions(+), 3 deletions(-)
create mode 100644 pyproject.toml
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000000..9fc681f3f91b
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,58 @@
+[build-system]
+requires = ["setuptools>=61.0", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "rteval"
+dynamic = ["version"]
+description = "Evaluate system performance for Realtime"
+readme = {text = """\
+The rteval script is used to judge the behavior of a hardware
+platform while running a Realtime Linux kernel under a moderate
+to heavy load.
+
+Provides control logic for starting a system load and then running a
+response time measurement utility (cyclictest) for a specified amount
+of time. When the run is finished, the sample data from cyclictest is
+analyzed for standard statistical measurements (i.e mode, median, range,
+mean, variance and standard deviation) and a report is generated.
+""", content-type = "text/plain"}
+requires-python = ">=3.10"
+license = {text = "GPL-2.0-or-later"}
+dependencies = [
+ "lxml",
+ "requests",
+]
+authors = [
+ {name = "Clark Williams", email = "williams@redhat.com"},
+ {name = "David Sommerseth", email = "davids@redhat.com"},
+ {name = "John Kacur", email = "jkacur@redhat.com"},
+]
+maintainers = [
+ {name = "John Kacur", email = "jkacur@redhat.com"},
+]
+
+[project.urls]
+Homepage = "https://git.kernel.org/pub/scm/utils/rteval/rteval.git"
+
+[tool.setuptools]
+packages = [
+ "rteval",
+ "rteval.modules",
+ "rteval.modules.loads",
+ "rteval.modules.measurement",
+ "rteval.sysinfo"
+]
+
+[tool.setuptools.dynamic]
+version = {attr = "rteval.version.RTEVAL_VERSION"}
+
+[tool.setuptools.package-dir]
+rteval = "rteval"
+"rteval.modules" = "rteval/modules"
+"rteval.modules.loads" = "rteval/modules/loads"
+"rteval.modules.measurement" = "rteval/modules/measurement"
+"rteval.sysinfo" = "rteval/sysinfo"
+
+[tool.setuptools.package-data]
+rteval = ["*.xsl", "rteval.conf"]
diff --git a/rteval-cmd b/rteval-cmd
index 7af179321fe2..8fd37b98b069 100755
--- a/rteval-cmd
+++ b/rteval-cmd
@@ -307,7 +307,7 @@ if __name__ == '__main__':
default_kernel_file = ModuleParameters().get('source').get('default')
if os.path.exists(tarfl):
if rtevcfg.srcdownload == default_kernel_file:
- sys.exit("Default kernel already exists, will not download")
+ sys.exit(f"Default kernel {default_kernel_file} already exists, will not download")
prompt = input("Kernel already exists, download and overwrite anyway? (y/n) ")
prompt = prompt.lower()
if prompt in ('no', 'n'):
diff --git a/setup.py b/setup.py
index c2695cb748a0..82db22989a6e 100644
--- a/setup.py
+++ b/setup.py
@@ -44,8 +44,10 @@ mangz.close()
setup(name="rteval",
version = RTEVAL_VERSION,
description = "Evaluate system performance for Realtime",
- author = "Clark Williams, David Sommerseth",
- author_email = "williams@redhat.com, davids@redhat.com",
+ author = "Clark Williams, David Sommerseth, John Kacur",
+ author_email = "williams@redhat.com, davids@redhat.com, jkacur@redhat.com",
+ maintainer = "John Kacur",
+ maintainer_email = "jkacur@redhat.com",
url = "https://git.kernel.org/pub/scm/utils/rteval/rteval.git",
license = "GPLv2",
long_description =
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 09/12] rteval: Improve argparse implementation and remove manual sys.argv parsing
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
` (7 preceding siblings ...)
2025-11-07 18:26 ` [PATCH 08/12] rteval: Add pyproject.toml for modern Python packaging John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 10/12] rteval: timerlat: Add dma_latency option with default value of 0 John Kacur
` (2 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur, Claude
This commit modernizes the argument parsing by using argparse properly
and eliminating fragile manual sys.argv manipulation.
Key improvements:
1. Use nargs='+' for -Z/--summarize and -H/--raw-histogram options
- These now properly accept one or more XML files as arguments
- Removed complex manual argument splitting logic (lines 158-174)
- Help text now correctly shows: -Z XMLFILE [XMLFILE ...]
2. Use nargs='?' with const='' for -S/--source-download option
- Simplified handling of optional kernel version argument
- Removed manual sys.argv.count() checks (line 179)
- Default value provided when flag is present without argument
3. Use parse_known_args() for early argument detection
- Config file (-f) detection now uses proper argparse
- Logging flags (-v/-D/-q) detection now uses proper argparse
- Removed all manual sys.argv indexing and counting
4. Return cmd_opts instead of cmd_args from parse_options()
- File arguments now directly accessible via cmd_opts attributes
- Clearer separation between options and file arguments
5. Improved error handling
- argparse provides better error messages for malformed arguments
- Supports both -Z file1 file2 and --summarize=file1 formats
This eliminates 40+ lines of fragile manual parsing code and makes
the argument handling more robust and maintainable.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: John Kacur <jkacur@redhat.com>
---
rteval-cmd | 93 +++++++++++++++++++++++-------------------------------
1 file changed, 39 insertions(+), 54 deletions(-)
diff --git a/rteval-cmd b/rteval-cmd
index 8fd37b98b069..00873ce1196b 100755
--- a/rteval-cmd
+++ b/rteval-cmd
@@ -124,11 +124,11 @@ def parse_options(cfg, parser, cmdargs):
action='store_true', default=rtevcfg.debugging,
help=f'turn on debug prints (default: {rtevcfg.debugging})')
parser.add_argument("-Z", '--summarize', dest='rteval___summarize',
- action='store_true', default=False,
- help='summarize an already existing XML report')
+ nargs='+', default=None, metavar='XMLFILE',
+ help='summarize one or more already existing XML reports')
parser.add_argument("-H", '--raw-histogram', dest='rteval___rawhistogram',
- action='store_true', default=False,
- help='Generate raw histogram data for an already existing XML report')
+ nargs='+', default=None, metavar='XMLFILE',
+ help='Generate raw histogram data for one or more already existing XML reports')
parser.add_argument("-f", "--inifile", dest="rteval___inifile",
type=str, default=None, metavar="FILE",
help="initialization file for configuring loads and behavior")
@@ -144,43 +144,19 @@ def parse_options(cfg, parser, cmdargs):
parser.add_argument("-V", "--version", dest="rteval___version",
action='store_true', default=False,
help='print rteval version and exit')
- parser.add_argument("-S", "--source-download", nargs="*", dest="rteval___srcdownload",
- type=str, default=None, metavar="KERNEL_VERSION",
+ parser.add_argument("-S", "--source-download", nargs="?", dest="rteval___srcdownload",
+ type=str, default=None, const='', metavar="KERNEL_VERSION",
help='download a source kernel from kernel.org and exit')
parser.add_argument("--noload", dest="rteval___noload",
action="store_true", default=False,
help="only run the measurements (don't run loads)")
-
- if not cmdargs:
- cmdargs = ["--help"]
-
- # if -Z/--summarize is specified, add the files to be summarized to cmd_args, and add -Z to cmd_opts
- cmd_args = []
- if (sys.argv.count('-Z')+sys.argv.count('--summarize')) > 0:
- try:
- ind = cmdargs.index('-Z')
- except ValueError:
- ind = cmdargs.index('--summarize')
- cmd_args = cmdargs[ind+1:]
- cmdargs = cmdargs[:ind+1]
- # if -H/--raw-histogram is specified, add the files to be summarized to cmd_args, and add -H to cmd_opts
- elif (sys.argv.count('-H')+sys.argv.count('--raw-histogram')) > 0:
- try:
- ind = cmdargs.index('-H')
- except ValueError:
- ind = cmdargs.index('--raw-histogram')
- cmd_args = cmdargs[ind+1:]
- cmdargs = cmdargs[:ind+1]
-
cmd_opts = parser.parse_args(args=cmdargs)
# if no kernel version was provided for --source-download, set version to default
- if (sys.argv.count('-S')+sys.argv.count('--source-download')) > 0:
- if cmd_opts.rteval___srcdownload == []:
+ if cmd_opts.rteval___srcdownload is not None:
+ if cmd_opts.rteval___srcdownload == '':
cmd_opts.rteval___srcdownload = ModuleParameters()["source"]["default"].replace(".tar.xz", "")
- else:
- cmd_opts.rteval___srcdownload = cmd_opts.rteval___srcdownload[0]
if cmd_opts.rteval___version:
print(f"rteval version {RTEVAL_VERSION}")
@@ -205,7 +181,7 @@ def parse_options(cfg, parser, cmdargs):
# Update the config object with the parsed arguments
cfg.UpdateFromOptionParser(cmd_opts)
- return cmd_args
+ return cmd_opts
if __name__ == '__main__':
from rteval.sysinfo import dmi
@@ -225,13 +201,14 @@ if __name__ == '__main__':
# Before really parsing options, see if we have been given a config file in the args
# and load it - just so that default values are according to the config file
- try:
- cfgfile = sys.argv[sys.argv.index('-f')+1]
- config.Load(cfgfile)
- except IndexError:
- # Missing file argument
- raise RuntimeError('The -f option requires a file name to the configuration file')
- except ValueError:
+ # Use a minimal parser to extract just the -f option
+ config_parser = argparse.ArgumentParser(add_help=False)
+ config_parser.add_argument('-f', '--inifile', dest='inifile')
+ early_args, _ = config_parser.parse_known_args()
+
+ if early_args.inifile:
+ config.Load(early_args.inifile)
+ else:
# No configuration file given, load defaults
config.Load()
@@ -247,20 +224,29 @@ if __name__ == '__main__':
'sysstat' : 'module'})
# Prepare log levels before loading modules, not to have unwanted log messages
+ # Use a minimal parser to extract logging-related flags early
rtevcfg = config.GetSection('rteval')
- if (sys.argv.count('-v')+sys.argv.count('--verbose')) > 0:
+ log_parser = argparse.ArgumentParser(add_help=False)
+ log_parser.add_argument('-v', '--verbose', action='store_true')
+ log_parser.add_argument('-D', '--debug', action='store_true')
+ log_parser.add_argument('-q', '--quiet', action='store_true')
+ log_parser.add_argument('--idle-set', action='store_true')
+ early_args, _ = log_parser.parse_known_args()
+
+ if early_args.verbose:
rtevcfg.verbose = True
- if (sys.argv.count('-D')+sys.argv.count('--debug')) > 0:
+ if early_args.debug:
rtevcfg.debugging = True
- if (sys.argv.count('-q')+sys.argv.count('--quiet')) > 0:
+ if early_args.quiet:
rtevcfg.quiet = True
+
loglev = (not rtevcfg.quiet and (Log.ERR | Log.WARN)) \
| (rtevcfg.verbose and Log.INFO) \
| (rtevcfg.debugging and Log.DEBUG)
logger.SetLogVerbosity(loglev)
# check if cpupower is being used
- if sys.argv.count('--idle-set') > 0:
+ if early_args.idle_set:
rtevcfg.update({'usingCpupower': True})
# Load modules
@@ -271,7 +257,7 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser()
loadmods.SetupModuleOptions(parser)
measuremods.SetupModuleOptions(parser)
- cmd_args = parse_options(config, parser, sys.argv[1:])
+ cmd_opts = parse_options(config, parser, sys.argv[1:])
if rtevcfg.noload:
if rtevcfg.onlyload:
@@ -371,16 +357,15 @@ if __name__ == '__main__':
logger.log(Log.DEBUG, f"workdir: {rtevcfg.workdir}")
# if --summarize was specified then just parse the XML, print it and exit
- if rtevcfg.summarize or rtevcfg.rawhistogram:
- if len(cmd_args) < 1:
- raise RuntimeError("Must specify at least one XML file with --summarize!")
-
- for x in cmd_args:
- if rtevcfg.summarize:
- summarize(x, rtevcfg.xslt_report)
- elif rtevcfg.rawhistogram:
- summarize(x, rtevcfg.xslt_histogram)
+ if cmd_opts.rteval___summarize:
+ for xmlfile in cmd_opts.rteval___summarize:
+ summarize(xmlfile, rtevcfg.xslt_report)
+ sys.exit(0)
+ # if --raw-histogram was specified then just parse the XML, print it and exit
+ if cmd_opts.rteval___rawhistogram:
+ for xmlfile in cmd_opts.rteval___rawhistogram:
+ summarize(xmlfile, rtevcfg.xslt_histogram)
sys.exit(0)
if os.getuid() != 0:
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 10/12] rteval: timerlat: Add dma_latency option with default value of 0
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
` (8 preceding siblings ...)
2025-11-07 18:26 ` [PATCH 09/12] rteval: Improve argparse implementation and remove manual sys.argv parsing John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 11/12] rteval: Add --measurement-module command-line argument John Kacur
2025-11-07 18:26 ` [PATCH 12/12] rteval: Add unit tests for --measurement-module argument John Kacur
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur, Claude
Add support for the rtla timerlat --dma-latency option to control
/dev/cpu_dma_latency settings. The option is enabled by default with
a value of 0 to help reduce latency by preventing deep CPU idle states.
Users can customize the value or disable the option entirely by setting
dma_latency to None in the configuration.
Assisted-by: Claude <noreply@anthropic.com>
Signed-off-by: John Kacur <jkacur@redhat.com>
---
rteval/modules/measurement/timerlat.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/rteval/modules/measurement/timerlat.py b/rteval/modules/measurement/timerlat.py
index 95fb7f136c25..1c7fb1607682 100644
--- a/rteval/modules/measurement/timerlat.py
+++ b/rteval/modules/measurement/timerlat.py
@@ -232,6 +232,12 @@ class Timerlat(rtevalModulePrototype):
self.__cmd.append(f'-E{self.__buckets}')
self.__cmd.append('--no-summary')
+ # Add dma-latency option if configured (default is 0)
+ # If dma_latency is explicitly set to None, don't pass the option to rtla
+ dma_latency = self.__cfg.setdefault('dma_latency', '0')
+ if dma_latency is not None and str(dma_latency).lower() != 'none':
+ self.__cmd.append(f'--dma-latency={dma_latency}')
+
if self.__cfg.stoptrace:
self.__cmd.append(f"-T{int(self.__cfg.stoptrace)}")
@@ -523,6 +529,9 @@ def ModuleParameters():
"trace": {"descr": "File to save trace to",
"default": None,
"metavar": "FILE" },
+ "dma_latency": {"descr": "Set /dev/cpu_dma_latency to USEC (set to None to disable)",
+ "default": "0",
+ "metavar": "USEC" },
}
def create(params, logger):
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 11/12] rteval: Add --measurement-module command-line argument
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
` (9 preceding siblings ...)
2025-11-07 18:26 ` [PATCH 10/12] rteval: timerlat: Add dma_latency option with default value of 0 John Kacur
@ 2025-11-07 18:26 ` John Kacur
2025-11-07 18:26 ` [PATCH 12/12] rteval: Add unit tests for --measurement-module argument John Kacur
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur, Claude
Add a new command-line argument to select between cyclictest and
timerlat measurement modules, overriding the config file setting.
The argument is part of the "Group Options for measurement modules"
in the argument parser, making it consistent with other measurement
options like --measurement-cpulist and --measurement-run-on-isolcpus.
Usage:
rteval --measurement-module cyclictest
rteval --measurement-module timerlat
Implementation details:
- Added --measurement-module to measurement argument group in
rteval/modules/__init__.py (SetupModuleOptions)
- Added early argument parsing in rteval-cmd to apply the override
before modules are loaded
- The selected module is enabled while the other is disabled
- Fixed module loading to handle None values when modules are disabled
Assisted-by: Claude <noreply@anthropic.com>
Signed-off-by: John Kacur <jkacur@redhat.com>
---
rteval-cmd | 18 ++++++++++++++++++
rteval/modules/__init__.py | 7 +++++++
rteval/modules/measurement/__init__.py | 2 +-
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/rteval-cmd b/rteval-cmd
index 00873ce1196b..855934e42192 100755
--- a/rteval-cmd
+++ b/rteval-cmd
@@ -223,6 +223,24 @@ if __name__ == '__main__':
'cyclictest' : 'module',
'sysstat' : 'module'})
+ # Check for --measurement-module argument early to override config file
+ measurement_parser = argparse.ArgumentParser(add_help=False)
+ measurement_parser.add_argument('--measurement-module', dest='measurement_module',
+ type=str, choices=['cyclictest', 'timerlat'])
+ early_args, _ = measurement_parser.parse_known_args()
+
+ if early_args.measurement_module:
+ # Override measurement config based on command-line argument
+ msrcfg = config.GetSection('measurement')
+ # Disable both cyclictest and timerlat first
+ if 'cyclictest' in msrcfg:
+ msrcfg.cyclictest = None
+ if 'timerlat' in msrcfg:
+ msrcfg.timerlat = None
+ # Enable the selected measurement module
+ setattr(msrcfg, early_args.measurement_module, 'module')
+ logger.log(Log.INFO, f"Using measurement module: {early_args.measurement_module} (from command line)")
+
# Prepare log levels before loading modules, not to have unwanted log messages
# Use a minimal parser to extract logging-related flags early
rtevcfg = config.GetSection('rteval')
diff --git a/rteval/modules/__init__.py b/rteval/modules/__init__.py
index d60877783670..7e5916c52965 100644
--- a/rteval/modules/__init__.py
+++ b/rteval/modules/__init__.py
@@ -303,6 +303,13 @@ reference from the first import"""
metavar='IDLESTATE',
default=None,
help='Idle state depth to set on cpus running measurement modules')
+ grparser.add_argument('--measurement-module',
+ dest='measurement___measurement_module',
+ type=str,
+ choices=['cyclictest', 'timerlat'],
+ metavar='MODULE',
+ default=None,
+ help='Select measurement module: cyclictest or timerlat (overrides config file)')
for (modname, mod) in list(self.__modsloaded.items()):
opts = mod.ModuleParameters()
diff --git a/rteval/modules/measurement/__init__.py b/rteval/modules/measurement/__init__.py
index 44708ce0b035..e09dca683dbf 100644
--- a/rteval/modules/measurement/__init__.py
+++ b/rteval/modules/measurement/__init__.py
@@ -24,7 +24,7 @@ class MeasurementModules(RtEvalModules):
for m in modcfg:
# hope to eventually have different kinds but module is only on
# for now (jcw)
- if m[1].lower() == 'module':
+ if m[1] and m[1].lower() == 'module':
self._LoadModule(m[0])
def SetupModuleOptions(self, parser):
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 12/12] rteval: Add unit tests for --measurement-module argument
2025-11-07 18:26 [PATCH 00/12] rteval updates John Kacur
` (10 preceding siblings ...)
2025-11-07 18:26 ` [PATCH 11/12] rteval: Add --measurement-module command-line argument John Kacur
@ 2025-11-07 18:26 ` John Kacur
11 siblings, 0 replies; 13+ messages in thread
From: John Kacur @ 2025-11-07 18:26 UTC (permalink / raw)
To: linux-rt-users; +Cc: Clark Williams, Tomas Glozar, John Kacur, Claude
Add comprehensive unit testing infrastructure for the new
--measurement-module command-line argument.
Test coverage includes:
- Default config file selection behavior
- Override with cyclictest
- Override with timerlat
- No override when argument not provided
- Invalid module name rejection
- Exclusive module selection (only one enabled)
Testing infrastructure:
- Created tests/ directory following Python conventions
- Added test_measurement_module_selection.py with 6 test cases
- Added run_tests.sh script for automated test execution
- Added Makefile targets: 'make test' and 'make unittest'
- Updated 'make help' to document test targets
All tests pass successfully using Python's unittest framework.
Usage:
make test
./run_tests.sh
python3 tests/test_measurement_module_selection.py
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: John Kacur <jkacur@redhat.com>
---
Makefile | 10 ++
run_tests.sh | 89 ++++++++++
tests/test_measurement_module_selection.py | 195 +++++++++++++++++++++
3 files changed, 294 insertions(+)
create mode 100755 run_tests.sh
create mode 100755 tests/test_measurement_module_selection.py
diff --git a/Makefile b/Makefile
index a250b18611b4..f17a434917ea 100644
--- a/Makefile
+++ b/Makefile
@@ -30,6 +30,12 @@ sysreport:
[ -d $(HERE)/run ] || mkdir run
$(PYTHON) rteval-cmd -D -v --workdir=$(HERE)/run --loaddir=$(HERE)/loadsource --duration=$(D) -i $(HERE)/rteval --sysreport
+unittest:
+ @echo "Running unit tests..."
+ ./run_tests.sh
+
+test: unittest
+
clean:
rm -f *~ rteval/*~ rteval/*.py[co] *.tar.bz2 *.tar.gz doc/*~
@@ -66,6 +72,8 @@ help:
@echo "rteval Makefile targets:"
@echo ""
@echo " runit: do a short testrun locally [default]"
+ @echo " test: run unit tests"
+ @echo " unittest: run unit tests (same as test)"
@echo " tarfile: create the source tarball"
@echo " install: install rteval locally"
@echo " clean: cleanup generated files"
@@ -82,3 +90,5 @@ tags:
.PHONY: cleantags
cleantags:
rm -f tags
+
+.PHONY: test unittest
diff --git a/run_tests.sh b/run_tests.sh
new file mode 100755
index 000000000000..c39d16feeea7
--- /dev/null
+++ b/run_tests.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Test runner for rteval unit tests
+#
+# This script runs all unit tests in the tests_progs/ directory
+# and provides a summary of results.
+#
+
+set -e
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+# Get the directory where this script is located
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "$SCRIPT_DIR"
+
+# Test results tracking
+TOTAL_TESTS=0
+PASSED_TESTS=0
+FAILED_TESTS=0
+
+echo "========================================="
+echo "Running rteval Unit Tests"
+echo "========================================="
+echo ""
+
+# Function to run a single test
+run_test() {
+ local test_file=$1
+ local test_name=$(basename "$test_file" .py)
+
+ echo -e "${YELLOW}Running: ${test_name}${NC}"
+ echo "---"
+
+ if python3 "$test_file"; then
+ echo -e "${GREEN}✓ PASSED: ${test_name}${NC}"
+ PASSED_TESTS=$((PASSED_TESTS + 1))
+ else
+ echo -e "${RED}✗ FAILED: ${test_name}${NC}"
+ FAILED_TESTS=$((FAILED_TESTS + 1))
+ fi
+
+ TOTAL_TESTS=$((TOTAL_TESTS + 1))
+ echo ""
+}
+
+# Find and run all test files in tests/
+if [ -d "tests" ]; then
+ # Run test_measurement_module_selection.py
+ if [ -f "tests/test_measurement_module_selection.py" ]; then
+ run_test "tests/test_measurement_module_selection.py"
+ fi
+
+ # Add more tests here as they are created
+ # Example:
+ # if [ -f "tests/test_another_feature.py" ]; then
+ # run_test "tests/test_another_feature.py"
+ # fi
+else
+ echo -e "${RED}Error: tests/ directory not found${NC}"
+ exit 1
+fi
+
+# Print summary
+echo "========================================="
+echo "Test Summary"
+echo "========================================="
+echo "Total tests run: $TOTAL_TESTS"
+echo -e "Passed: ${GREEN}$PASSED_TESTS${NC}"
+if [ $FAILED_TESTS -gt 0 ]; then
+ echo -e "Failed: ${RED}$FAILED_TESTS${NC}"
+else
+ echo -e "Failed: $FAILED_TESTS"
+fi
+echo ""
+
+# Exit with appropriate code
+if [ $FAILED_TESTS -eq 0 ]; then
+ echo -e "${GREEN}✓ All tests passed!${NC}"
+ exit 0
+else
+ echo -e "${RED}✗ Some tests failed${NC}"
+ exit 1
+fi
diff --git a/tests/test_measurement_module_selection.py b/tests/test_measurement_module_selection.py
new file mode 100755
index 000000000000..2216cc668297
--- /dev/null
+++ b/tests/test_measurement_module_selection.py
@@ -0,0 +1,195 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Unit test for --measurement-module command-line argument
+#
+# This test verifies that the --measurement-module argument correctly
+# overrides config file settings to select between cyclictest and timerlat.
+#
+
+import sys
+import os
+import unittest
+import argparse
+
+# Add rteval to path
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+from rteval import rtevalConfig
+from rteval.Log import Log
+
+
+class TestMeasurementModuleSelection(unittest.TestCase):
+ """Test suite for measurement module selection via command-line argument"""
+
+ def setUp(self):
+ """Set up test fixtures"""
+ self.logger = Log()
+ self.logger.SetLogVerbosity(Log.NONE)
+
+ def test_default_config_file_selection(self):
+ """Test that config file settings are loaded correctly by default"""
+ config = rtevalConfig.rtevalConfig(logger=self.logger)
+
+ # Load the rteval.conf file (which currently has timerlat enabled)
+ config_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'rteval.conf')
+ if os.path.exists(config_file):
+ config.Load(config_file)
+ msrcfg = config.GetSection('measurement')
+
+ # Based on current rteval.conf, timerlat should be set to 'module'
+ timerlat_value = getattr(msrcfg, 'timerlat', None)
+ self.assertEqual(timerlat_value, 'module',
+ "timerlat should be set to 'module' in rteval.conf")
+
+ def test_override_with_cyclictest(self):
+ """Test that --measurement-module cyclictest overrides config file"""
+ config = rtevalConfig.rtevalConfig(logger=self.logger)
+ config.AppendConfig('measurement', {
+ 'timerlat': 'module',
+ 'sysstat': 'module'
+ })
+
+ # Simulate the early argument parser
+ measurement_parser = argparse.ArgumentParser(add_help=False)
+ measurement_parser.add_argument('--measurement-module', dest='measurement_module',
+ type=str, choices=['cyclictest', 'timerlat'])
+ early_args, _ = measurement_parser.parse_known_args(['--measurement-module', 'cyclictest'])
+
+ # Apply the override logic (same as in rteval-cmd)
+ if early_args.measurement_module:
+ msrcfg = config.GetSection('measurement')
+ if 'cyclictest' in msrcfg:
+ msrcfg.cyclictest = None
+ if 'timerlat' in msrcfg:
+ msrcfg.timerlat = None
+ setattr(msrcfg, early_args.measurement_module, 'module')
+
+ # Verify the results
+ msrcfg = config.GetSection('measurement')
+ cyclictest_value = getattr(msrcfg, 'cyclictest', None)
+ timerlat_value = getattr(msrcfg, 'timerlat', None)
+
+ self.assertEqual(cyclictest_value, 'module',
+ "cyclictest should be enabled after override")
+ self.assertNotEqual(timerlat_value, 'module',
+ "timerlat should be disabled after override")
+
+ def test_override_with_timerlat(self):
+ """Test that --measurement-module timerlat overrides config file"""
+ config = rtevalConfig.rtevalConfig(logger=self.logger)
+ config.AppendConfig('measurement', {
+ 'cyclictest': 'module',
+ 'sysstat': 'module'
+ })
+
+ # Simulate the early argument parser
+ measurement_parser = argparse.ArgumentParser(add_help=False)
+ measurement_parser.add_argument('--measurement-module', dest='measurement_module',
+ type=str, choices=['cyclictest', 'timerlat'])
+ early_args, _ = measurement_parser.parse_known_args(['--measurement-module', 'timerlat'])
+
+ # Apply the override logic (same as in rteval-cmd)
+ if early_args.measurement_module:
+ msrcfg = config.GetSection('measurement')
+ if 'cyclictest' in msrcfg:
+ msrcfg.cyclictest = None
+ if 'timerlat' in msrcfg:
+ msrcfg.timerlat = None
+ setattr(msrcfg, early_args.measurement_module, 'module')
+
+ # Verify the results
+ msrcfg = config.GetSection('measurement')
+ cyclictest_value = getattr(msrcfg, 'cyclictest', None)
+ timerlat_value = getattr(msrcfg, 'timerlat', None)
+
+ self.assertEqual(timerlat_value, 'module',
+ "timerlat should be enabled after override")
+ self.assertNotEqual(cyclictest_value, 'module',
+ "cyclictest should be disabled after override")
+
+ def test_no_override_when_argument_not_provided(self):
+ """Test that config file settings remain when argument is not provided"""
+ config = rtevalConfig.rtevalConfig(logger=self.logger)
+ config.AppendConfig('measurement', {
+ 'timerlat': 'module',
+ 'sysstat': 'module'
+ })
+
+ # Simulate the early argument parser with no --measurement-module argument
+ measurement_parser = argparse.ArgumentParser(add_help=False)
+ measurement_parser.add_argument('--measurement-module', dest='measurement_module',
+ type=str, choices=['cyclictest', 'timerlat'])
+ early_args, _ = measurement_parser.parse_known_args([])
+
+ # Only apply override if argument was provided
+ if early_args.measurement_module:
+ msrcfg = config.GetSection('measurement')
+ if 'cyclictest' in msrcfg:
+ msrcfg.cyclictest = None
+ if 'timerlat' in msrcfg:
+ msrcfg.timerlat = None
+ setattr(msrcfg, early_args.measurement_module, 'module')
+
+ # Verify the results - should remain unchanged
+ msrcfg = config.GetSection('measurement')
+ timerlat_value = getattr(msrcfg, 'timerlat', None)
+
+ self.assertEqual(timerlat_value, 'module',
+ "timerlat should remain enabled when no override is provided")
+
+ def test_argparse_rejects_invalid_module(self):
+ """Test that argparse rejects invalid module names"""
+ measurement_parser = argparse.ArgumentParser(add_help=False)
+ measurement_parser.add_argument('--measurement-module', dest='measurement_module',
+ type=str, choices=['cyclictest', 'timerlat'])
+
+ # This should raise SystemExit due to invalid choice
+ with self.assertRaises(SystemExit):
+ measurement_parser.parse_args(['--measurement-module', 'invalid'])
+
+ def test_both_modules_disabled_after_override(self):
+ """Test that both modules are disabled when one is selected"""
+ config = rtevalConfig.rtevalConfig(logger=self.logger)
+ config.AppendConfig('measurement', {
+ 'cyclictest': 'module',
+ 'timerlat': 'module',
+ 'sysstat': 'module'
+ })
+
+ # Override with cyclictest
+ measurement_parser = argparse.ArgumentParser(add_help=False)
+ measurement_parser.add_argument('--measurement-module', dest='measurement_module',
+ type=str, choices=['cyclictest', 'timerlat'])
+ early_args, _ = measurement_parser.parse_known_args(['--measurement-module', 'cyclictest'])
+
+ if early_args.measurement_module:
+ msrcfg = config.GetSection('measurement')
+ if 'cyclictest' in msrcfg:
+ msrcfg.cyclictest = None
+ if 'timerlat' in msrcfg:
+ msrcfg.timerlat = None
+ setattr(msrcfg, early_args.measurement_module, 'module')
+
+ # Verify exactly one is enabled
+ msrcfg = config.GetSection('measurement')
+ cyclictest_value = getattr(msrcfg, 'cyclictest', None)
+ timerlat_value = getattr(msrcfg, 'timerlat', None)
+
+ enabled_count = sum([
+ 1 if cyclictest_value == 'module' else 0,
+ 1 if timerlat_value == 'module' else 0
+ ])
+
+ self.assertEqual(enabled_count, 1,
+ "Exactly one measurement module should be enabled")
+
+
+def main():
+ """Run the test suite"""
+ # Run tests with verbosity
+ unittest.main(verbosity=2)
+
+
+if __name__ == '__main__':
+ main()
--
2.51.1
^ permalink raw reply related [flat|nested] 13+ messages in thread