diff --git a/0001-Updated-rteval-man-page.patch b/Updated-rteval-man-page.patch similarity index 100% rename from 0001-Updated-rteval-man-page.patch rename to Updated-rteval-man-page.patch diff --git a/rteval-Fix-aNone-being-passed-to-cyclictest.patch b/rteval-Fix-aNone-being-passed-to-cyclictest.patch new file mode 100644 index 0000000..263145f --- /dev/null +++ b/rteval-Fix-aNone-being-passed-to-cyclictest.patch @@ -0,0 +1,45 @@ +From 17a39863e5f89fbaf51dc158bc7e27f46676d46d Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Tue, 11 Jun 2024 16:45:53 +0200 +Subject: [PATCH] rteval: Fix -aNone being passed to cyclictest + +When rteval is called via the command line, cpulists for both +measurements and loads default to an empty string and are further +processed by parse_cpulist_from_config. However, this is not true when +rteval is used as a module: in that case, neither the default +command-line value is used nor parse_cpulist_from_config is run, leading +to None being set to the config property of measurements which is +explicitely passed down to the corresponding cyclictest config property. + +After 64ce7848dfab ("rteval: Add relative cpulists for measurements"), +where the check for None was removed from the cyclictest module, rteval +passes "-aNone" to cyclictest when being used as a module. + +Call parse_cpulist_from_config with an empty string to get the default +cpulist to pass to cyclictest module if cpulist is empty in +the measurement config. + +Fixes: 64ce7848dfab ("rteval: Add relative cpulists for measurements") +Signed-off-by: Tomas Glozar +Signed-off-by: John Kacur +--- + rteval/modules/measurement/__init__.py | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/rteval/modules/measurement/__init__.py b/rteval/modules/measurement/__init__.py +index 11bd7b0fce69..43c0fda30ce1 100644 +--- a/rteval/modules/measurement/__init__.py ++++ b/rteval/modules/measurement/__init__.py +@@ -148,6 +148,9 @@ measurement profiles, based on their characteristics""" + modcfg = self.__cfg.GetSection("measurement") + cpulist = modcfg.cpulist + run_on_isolcpus = modcfg.run_on_isolcpus ++ if cpulist is None: ++ # Get default cpulist value ++ cpulist = cpulist_utils.collapse_cpulist(parse_cpulist_from_config("", run_on_isolcpus)) + + for (modname, modtype) in modcfg: + if isinstance(modtype, str) and modtype.lower() == 'module': # Only 'module' will be supported (ds) +-- +2.45.2 + diff --git a/rteval-Fix-sysreport-traceback-when-utility-sos-not-.patch b/rteval-Fix-sysreport-traceback-when-utility-sos-not-.patch new file mode 100644 index 0000000..a13ba7c --- /dev/null +++ b/rteval-Fix-sysreport-traceback-when-utility-sos-not-.patch @@ -0,0 +1,43 @@ +From 2a0b5833be4f55dbbc00f1835a4ace554e498137 Mon Sep 17 00:00:00 2001 +From: John Kacur +Date: Fri, 21 Jun 2024 13:20:26 -0400 +Subject: [PATCH 3/4] rteval: Fix sysreport traceback when utility sos not + found + +When rteval is run with +-s, --sysreport run sysreport to collect system data (default: False) + +and sos, sosreport or sysreport cannot be found then rteval exits with +an error. + +Fix this by adding /usr/bin to the places to search for this program. + +Signed-off-by: John Kacur +--- + rteval/sysinfo/osinfo.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/rteval/sysinfo/osinfo.py b/rteval/sysinfo/osinfo.py +index 3d6d5f8daa96..3bdbcc81e068 100644 +--- a/rteval/sysinfo/osinfo.py ++++ b/rteval/sysinfo/osinfo.py +@@ -45,10 +45,16 @@ class OSInfo: + def run_sysreport(self, repdir): + if os.path.exists('/usr/sbin/sos'): + exe = '/usr/sbin/sos report' ++ elif os.path.exists('/usr/bin/sos'): ++ exe = '/usr/bin/sos report' + elif os.path.exists('/usr/sbin/sosreport'): + exe = '/usr/sbin/sosreport' ++ elif os.path.exists('/usr/bin/sosreport'): ++ exe = '/usr/bin/sosreport' + elif os.path.exists('/usr/sbin/sysreport'): + exe = '/usr/sbin/sysreport' ++ elif os.path.exists('/usr/bin/sysreport'): ++ exe = '/usr/bin/sysreport' + else: + raise RuntimeError("Can't find sos/sosreport/sysreport") + +-- +2.45.2 + diff --git a/0002-rteval-sysstat-Convert-base64-data-to-text-before-wr.patch b/rteval-sysstat-Convert-base64-data-to-text-before-wr.patch similarity index 97% rename from 0002-rteval-sysstat-Convert-base64-data-to-text-before-wr.patch rename to rteval-sysstat-Convert-base64-data-to-text-before-wr.patch index 266e660..4a306d4 100644 --- a/0002-rteval-sysstat-Convert-base64-data-to-text-before-wr.patch +++ b/rteval-sysstat-Convert-base64-data-to-text-before-wr.patch @@ -1,7 +1,7 @@ From ca90d5aa7ae2ff6dac124c710fceadae028b5f4a Mon Sep 17 00:00:00 2001 From: Crystal Wood Date: Thu, 20 Jun 2024 21:18:05 -0500 -Subject: [PATCH 2/3] rteval: sysstat: Convert base64 data to text before +Subject: [PATCH 2/4] rteval: sysstat: Convert base64 data to text before wrapping As of Python 3, b64encode() returns data, not a string, causing this: diff --git a/0001-rteval-timerlat-Add-timerlat-tracing-to-rteval.patch b/rteval-timerlat-Add-timerlat-tracing-to-rteval.patch similarity index 99% rename from 0001-rteval-timerlat-Add-timerlat-tracing-to-rteval.patch rename to rteval-timerlat-Add-timerlat-tracing-to-rteval.patch index 097d329..831a850 100644 --- a/0001-rteval-timerlat-Add-timerlat-tracing-to-rteval.patch +++ b/rteval-timerlat-Add-timerlat-tracing-to-rteval.patch @@ -1,7 +1,7 @@ From 5909521e06ed92ea60ffb247b25ca86c232f71bf Mon Sep 17 00:00:00 2001 From: John Kacur Date: Thu, 20 Jun 2024 21:34:24 -0400 -Subject: [PATCH 1/3] rteval: timerlat: Add timerlat tracing to rteval +Subject: [PATCH 1/4] rteval: timerlat: Add timerlat tracing to rteval This patch adds tracing from timerlat to rteval. diff --git a/rteval-timerlat-tracing-clean-up.patch b/rteval-timerlat-tracing-clean-up.patch new file mode 100644 index 0000000..ede50e6 --- /dev/null +++ b/rteval-timerlat-tracing-clean-up.patch @@ -0,0 +1,217 @@ +From 4fd5b77dc47f039551050925e442db9a17be5e75 Mon Sep 17 00:00:00 2001 +From: John Kacur +Date: Mon, 24 Jun 2024 08:45:54 -0400 +Subject: [PATCH 4/4] rteval: timerlat tracing clean-up + +The patch cleans-up the code that stores the timerlat summary into xml, +after a trace is stopped. It does so by grouping similar kinds of output +together for parsing. It also reduces the amount of debug output. + +Signed-off-by: John Kacur +--- + rteval/modules/measurement/timerlat.py | 142 ++++++++++--------------- + rteval/rteval_text.xsl | 10 +- + 2 files changed, 62 insertions(+), 90 deletions(-) + +diff --git a/rteval/modules/measurement/timerlat.py b/rteval/modules/measurement/timerlat.py +index 45adec1b33e1..dc6226ccc991 100644 +--- a/rteval/modules/measurement/timerlat.py ++++ b/rteval/modules/measurement/timerlat.py +@@ -273,12 +273,14 @@ class Timerlat(rtevalModulePrototype): + os.kill(self.__timerlat_process.pid, signal.SIGINT) + time.sleep(2) + ++ ++ # Parse histogram output ++ self.__timerlat_out.seek(0) ++ + blocking_thread_detected = False + softirq_interference_detected = False + irq_interference_detected = False + +- # Parse histogram output +- self.__timerlat_out.seek(0) + for line in self.__timerlat_out: + line = bytes.decode(line) + +@@ -291,6 +293,8 @@ class Timerlat(rtevalModulePrototype): + self.__posttrace += line + line = line.strip() + fields = line.split() ++ if not line: ++ continue + if line.startswith("##") and fields[1] == "CPU": + self.stcpu = int(fields[2]) + self._log(Log.DEBUG, f"self.stcpu = {self.stcpu}") +@@ -304,91 +308,59 @@ class Timerlat(rtevalModulePrototype): + softirq_interference_detected = False + irq_interference_detected = False + continue +- if line.startswith("Thread latency:"): +- thread_latency_percent = fields[-1].strip('()%') +- self._log(Log.DEBUG, f"thread_latency_percent = {thread_latency_percent}") +- thread_latency = fields[-3] +- self._log(Log.DEBUG, f"thread_latency = {thread_latency}") +- self.__stdata[self.stcpu]["Thread_latency"] = (thread_latency, thread_latency_percent) +- elif line.startswith("Previous IRQ interference"): +- self._log(Log.DEBUG, f'Previous_IRQ_interference = {fields[-2]}') +- self.__stdata[self.stcpu]["Previous_IRQ_interference"] = fields[-2] +- elif line.startswith("IRQ handler delay:"): +- irq_handler_delay_percent = fields[-2].strip('(') +- irq_handler_delay = fields[-4] +- # Do we have (exit from idle)? +- if fields[3] == '(exit': +- field_name = "IRQ_handler_delay_exit_from_idle" +- else: +- field_name = "IRQ_handler_delay" +- self._log(Log.DEBUG, f"{field_name} = {irq_handler_delay}") +- self._log(Log.DEBUG, f"{field_name}_percent = {irq_handler_delay_percent}") +- self.__stdata[self.stcpu][field_name] = (irq_handler_delay, irq_handler_delay_percent) +- elif line.startswith("IRQ latency:"): +- self._log(Log.DEBUG, f"irq_latency = {fields[-2]}") +- self.__stdata[self.stcpu]["IRQ_latency"] = fields[-2] +- elif line.startswith("Timerlat IRQ duration"): +- timerlat_irq_duration_percent = fields[-2].strip('(') +- self._log(Log.DEBUG, f"timerlat_irq_duration_percent = {timerlat_irq_duration_percent}") +- timerlat_irq_duration = fields[-4] +- self._log(Log.DEBUG, f"timerlat_irq_duration = {timerlat_irq_duration}") +- self.__stdata[self.stcpu]["Timerlat_IRQ_duration"] = (timerlat_irq_duration, timerlat_irq_duration_percent) +- elif line.startswith("Blocking thread:"): +- blocking_thread_percent = fields[-2].strip('(') +- self._log(Log.DEBUG, f"blocking_thread_percent = {blocking_thread_percent}") +- blocking_thread = fields[-4] +- self._log(Log.DEBUG, f"blocking_thread = {blocking_thread}") +- self.__stdata[self.stcpu]["Blocking_Thread"] = (blocking_thread, blocking_thread_percent) +- blocking_thread_detected = True +- irq_interference_detected = False +- softirq_interference_detected = False +- elif line.startswith("IRQ interference"): +- irq_interference_percent = fields[-2].strip('(') +- self._log(Log.DEBUG, f"irq_interference_percent = {irq_interference_percent}") +- irq_interference = fields[-4] +- self._log(Log.DEBUG, f"irq_interference = {irq_interference}") +- self.__stdata[self.stcpu]["IRQ_interference"] = (irq_interference, irq_interference_percent) +- blocking_thread_detected = False +- irq_interference_detected = True +- softirq_interference_detected = False +- elif line.startswith("Softirq interference"): +- softirq_interference_percent = fields[-2].strip('(') +- self._log(Log.DEBUG, f"softirq_interference_percent = {softirq_interference_percent}") +- softirq_interference = fields[-4] +- self._log(Log.DEBUG, f"softirq_interference = {softirq_interference}") +- self.__stdata[self.stcpu]["Softirq_interference"] = (softirq_interference, softirq_interference_percent) +- blocking_thread_detected = False +- irq_interference_detected = False +- softirq_interference_detected = True +- elif blocking_thread_detected: +- self._log(Log.DEBUG, f'line={line}') +- blocking_thread = " ".join(fields[0:-2]) +- self._log(Log.DEBUG, f"blocking_thread = {blocking_thread}") +- blocking_threadus = fields[-2] +- self._log(Log.DEBUG, f"blocking_threadus = {blocking_threadus}") +- self.__stdata[self.stcpu].setdefault("blocking_thread", []) +- self.__stdata[self.stcpu]["blocking_thread"] += [(blocking_thread, blocking_threadus)] +- elif softirq_interference_detected: +- softirq = " ".join(fields[0:-2]) +- softirq_latency = fields[-2] +- self._log(Log.DEBUG, f'softirq = {softirq}') +- self._log(Log.DEBUG, f'softirq_latency = {softirq_latency}') +- self.__stdata[self.stcpu].setdefault("softirq_interference", []) +- self.__stdata[self.stcpu]["softirq_interference"] += [(softirq, softirq_latency)] +- elif irq_interference_detected: +- irq_interference_name = " ".join(fields[0:-2]) +- irq_interference_latency = fields[-2] +- self._log(Log.DEBUG, f'irq_interference = {irq_interference_name}, latency = {irq_interference_latency}') +- self.__stdata[self.stcpu].setdefault("irq_interference", []) +- self.__stdata[self.stcpu]["irq_interference"] += [(irq_interference_name, irq_interference_latency)] +- elif line.startswith("Max timerlat IRQ latency"): +- self._log(Log.DEBUG, f"line={line}") +- max_timerlat_irq_latency = fields[-5] +- self._log(Log.DEBUG, f"max_timerlat_irq_latency = {max_timerlat_irq_latency}") ++ ++ # work around rtla not printing ':' after all names ++ if line.startswith('Softirq interference'): ++ name = 'Softirq_interference' ++ elif line.startswith('IRQ interference'): ++ name = 'IRQ_interference' ++ else: ++ name = ''.join(line.split(':')[0]).replace(' ', '_') ++ self._log(Log.DEBUG, f"name={name}") ++ ++ if name in ['Thread_latency']: ++ latency = fields[-3] ++ percent = fields[-1].strip('()%') ++ self._log(Log.DEBUG, f'{name} = ({latency}, {percent})') ++ self.__stdata[self.stcpu][name] = (latency, percent) ++ continue ++ if name in ['Timerlat_IRQ_duration', 'IRQ_handler_delay', 'Blocking_thread', 'IRQ_interference', 'Softirq_interference']: ++ latency = fields[-4] ++ percent = fields[-2].strip('(') ++ if name == 'IRQ_handler_delay' and fields[3] == '(exit': ++ name = 'IRQ_handler_delay_exit_from_idle' ++ self._log(Log.DEBUG, f'{name} = ({latency}, {percent})') ++ self.__stdata[self.stcpu][name] = (latency, percent) ++ detected = {'Blocking_thread' : (True, False, False), ++ 'IRQ_interference' : (False, True, False), ++ 'Softirq_interference' : (False, False, True) } ++ if name in ('Blocking_thread', 'IRQ_interference', 'Softirq_interference'): ++ blocking_thread_detected, irq_interference_detected, softirq_interference_detected = detected.get(name) ++ continue ++ if name in ["IRQ_latency", "Previous_IRQ_interference"]: ++ latency = fields[-2] ++ self._log(Log.DEBUG, f'{name} = {fields[-2]}') ++ self.__stdata[self.stcpu][name] = fields[-2] ++ continue ++ if blocking_thread_detected or softirq_interference_detected or irq_interference_detected: ++ if blocking_thread_detected: ++ field_name = "blocking_thread" ++ elif softirq_interference_detected: ++ field_name = "softirq_interference" ++ elif irq_interference_detected: ++ field_name = "irq_interference" ++ thread = " ".join(fields[0:-2]) ++ latency = fields[-2] ++ self._log(Log.DEBUG, f"{field_name} += [({thread}, {latency})]") ++ self.__stdata[self.stcpu].setdefault(field_name, []) ++ self.__stdata[self.stcpu][field_name] += [(thread, latency)] ++ continue ++ if name == "Max_timerlat_IRQ_latency_from_idle": ++ latency = fields[-5] + max_timerlat_cpu = int(fields[-1]) +- self._log(Log.DEBUG, f"max_timerlat_cpu = {max_timerlat_cpu}") ++ self._log(Log.DEBUG, f'self.__stdata[{max_timerlat_cpu}][{name}] = {latency}') + self.__stdata.setdefault(max_timerlat_cpu, {}) +- self.__stdata[max_timerlat_cpu]["Max_timerlat_IRQ_latency_from_idle"] = max_timerlat_irq_latency ++ self.__stdata[max_timerlat_cpu][name] = latency + else: + self._log(Log.DEBUG, f'line = {line}') + continue +diff --git a/rteval/rteval_text.xsl b/rteval/rteval_text.xsl +index 70777354efa5..7ca0ae3a4c66 100644 +--- a/rteval/rteval_text.xsl ++++ b/rteval/rteval_text.xsl +@@ -546,16 +546,16 @@ + ) + + +- ++ + Blocking thread: + +- ++ + +- ++ + ( +- ++ + +- ++ + ) + + +-- +2.45.2 + diff --git a/rteval.spec b/rteval.spec index ed89600..7b92439 100644 --- a/rteval.spec +++ b/rteval.spec @@ -1,6 +1,6 @@ Name: rteval Version: 3.8 -Release: 5%{?dist} +Release: 6%{?dist} Summary: Utility to evaluate system suitability for RT Linux Group: Development/Tools @@ -34,9 +34,12 @@ Requires: rtla BuildArch: noarch # Patches -Patch1: 0001-Updated-rteval-man-page.patch -Patch2: 0001-rteval-timerlat-Add-timerlat-tracing-to-rteval.patch -Patch3: 0002-rteval-sysstat-Convert-base64-data-to-text-before-wr.patch +Patch1: Updated-rteval-man-page.patch +Patch2: rteval-Fix-aNone-being-passed-to-cyclictest.patch +Patch3: rteval-timerlat-Add-timerlat-tracing-to-rteval.patch +Patch4: rteval-sysstat-Convert-base64-data-to-text-before-wr.patch +Patch5: rteval-Fix-sysreport-traceback-when-utility-sos-not-.patch +Patch6: rteval-timerlat-tracing-clean-up.patch %description The rteval script is a utility for measuring various aspects of @@ -52,6 +55,9 @@ to the screen. %patch1 -p1 %patch2 -p1 %patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 %build %{__python3} setup.py build @@ -72,6 +78,12 @@ to the screen. %{_bindir}/rteval %changelog +* Mon Jun 24 2024 John Kacur - 3.8-6 +- Rename some patches +- Add timerlat tracing +- Add Fix-aNone being pass to cyclictest +Resolves: RHEL-35462 + * Fri Jun 21 2024 Anubhav Shelat - 3.8-5 - Add options for timerlat tracing in rteval, and store timerlat summary in rteval summary xml file. - Convert base64 data to text before wrapping.