diff --git a/gating.yaml b/gating.yaml new file mode 100644 index 0000000..648918d --- /dev/null +++ b/gating.yaml @@ -0,0 +1,6 @@ +--- !Policy +product_versions: + - rhel-9 +decision_context: osci_compose_gate +rules: + - !PassingTestCaseRule {test_case_name: osci.brew-build.tier0.functional} diff --git a/rpminspect.yaml b/rpminspect.yaml new file mode 100644 index 0000000..9c2eb08 --- /dev/null +++ b/rpminspect.yaml @@ -0,0 +1,9 @@ +# Shutting off permissions due to python egg-info files + +--- +inspections: + permissions: off + +capabilities: + ignore: + - /usr/lib/python*/site-packages/rteval-*-py*.egg-info diff --git a/rteval-Add-relative-cpulists-for-measurements.patch b/rteval-Add-relative-cpulists-for-measurements.patch new file mode 100644 index 0000000..933255f --- /dev/null +++ b/rteval-Add-relative-cpulists-for-measurements.patch @@ -0,0 +1,278 @@ +From 64ce7848dfabd2056d35c8a60f3354db45e36286 Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Thu, 18 Jan 2024 10:18:10 +0100 +Subject: [PATCH 2/4] rteval: Add relative cpulists for measurements + +Instead of specifying an absolute list of CPUs to run measurements on +in --measurement-cpulist, implement an option to specify a relative list +with respect to the current cpuset of rteval. + +The relative cpulist can include CPUs both for addition and for removal, +e.g. +0,1,-7,8. + +Also move the logic for processing cpulists specified by the user as +a string into cpulists usable by rteval to a single function. + +Signed-off-by: Tomas Glozar +Signed-off-by: John Kacur +--- + rteval-cmd | 26 +++++++----- + rteval/cpulist_utils.py | 33 ++++++++++++++++ + rteval/modules/measurement/__init__.py | 9 +---- + rteval/modules/measurement/cyclictest.py | 50 +++--------------------- + rteval/systopology.py | 33 ++++++++++++++++ + 5 files changed, 90 insertions(+), 61 deletions(-) + +diff --git a/rteval-cmd b/rteval-cmd +index d224728..a5e8746 100755 +--- a/rteval-cmd ++++ b/rteval-cmd +@@ -30,7 +30,7 @@ from rteval import RtEval, rtevalConfig + from rteval.modules.loads import LoadModules + from rteval.modules.measurement import MeasurementModules + from rteval.version import RTEVAL_VERSION +-from rteval.systopology import SysTopology ++from rteval.systopology import SysTopology, parse_cpulist_from_config + from rteval.modules.loads.kcompile import ModuleParameters + import rteval.cpulist_utils as cpulist_utils + +@@ -339,26 +339,32 @@ if __name__ == '__main__': + + ldcfg = config.GetSection('loads') + msrcfg = config.GetSection('measurement') +- if ldcfg.cpulist and msrcfg.cpulist: ++ msrcfg_cpulist_present = msrcfg.cpulist != "" ++ # Parse measurement cpulist using parse_cpulist_from_config to account for run-on-isolcpus ++ # and relative cpusets ++ cpulist = parse_cpulist_from_config(msrcfg.cpulist, msrcfg.run_on_isolcpus) ++ if msrcfg_cpulist_present and not cpulist_utils.is_relative(msrcfg.cpulist) and msrcfg.run_on_isolcpus: ++ logger.log(Log.WARN, "ignoring --measurement-run-on-isolcpus, since cpulist is specified") ++ msrcfg.cpulist = collapse_cpulist(cpulist) ++ if ldcfg.cpulist: + ldcfg.cpulist = remove_offline(ldcfg.cpulist) +- msrcfg.cpulist = remove_offline(msrcfg.cpulist) + # if we only specified one set of cpus (loads or measurement) + # default the other to the inverse of the specified list +- if not ldcfg.cpulist and msrcfg.cpulist: ++ if not ldcfg.cpulist and msrcfg_cpulist_present: + tmplist = expand_cpulist(msrcfg.cpulist) + tmplist = SysTopology().invert_cpulist(tmplist) +- ldcfg.cpulist = compress_cpulist(tmplist) +- msrcfg.cpulist = remove_offline(msrcfg.cpulist) +- if not msrcfg.cpulist and ldcfg.cpulist: ++ tmplist = cpulist_utils.online_cpulist(tmplist) ++ ldcfg.cpulist = collapse_cpulist(tmplist) ++ if not msrcfg_cpulist_present and ldcfg.cpulist: + tmplist = expand_cpulist(ldcfg.cpulist) + tmplist = SysTopology().invert_cpulist(tmplist) +- msrcfg.cpulist = compress_cpulist(tmplist) +- ldcfg.cpulist = remove_offline(ldcfg.cpulist) ++ tmplist = cpulist_utils.online_cpulist(tmplist) ++ msrcfg.cpulist = collapse_cpulist(tmplist) + + if ldcfg.cpulist: + logger.log(Log.DEBUG, f"loads cpulist: {ldcfg.cpulist}") + # if --onlyload is specified msrcfg.cpulist is unused +- if msrcfg.cpulist and not rtevcfg.onlyload: ++ if msrcfg_cpulist_present and not rtevcfg.onlyload: + logger.log(Log.DEBUG, f"measurement cpulist: {msrcfg.cpulist}") + logger.log(Log.DEBUG, f"workdir: {rtevcfg.workdir}") + +diff --git a/rteval/cpulist_utils.py b/rteval/cpulist_utils.py +index 402d579..7abc45a 100644 +--- a/rteval/cpulist_utils.py ++++ b/rteval/cpulist_utils.py +@@ -126,3 +126,36 @@ def nonisolated_cpulist(cpulist): + isolated_cpulist = sysread(cpupath, "isolated") + isolated_cpulist = expand_cpulist(isolated_cpulist) + return list(set(cpulist).difference(set(isolated_cpulist))) ++ ++ ++def is_relative(cpulist): ++ return cpulist.startswith("+") or cpulist.startswith("-") ++ ++ ++def expand_relative_cpulist(cpulist): ++ """ ++ Expand a relative cpulist into a tuple of lists. ++ :param cpulist: Relative cpulist of form +1,2,3,-4,5,6 ++ :return: Tuple of two lists, one for added CPUs, one for removed CPUs ++ """ ++ added_cpus = [] ++ removed_cpus = [] ++ ++ if not cpulist: ++ return added_cpus, removed_cpus ++ ++ cpus = None ++ ++ for part in cpulist.split(','): ++ if part.startswith('+') or part.startswith('-'): ++ cpus = added_cpus if part[0] == '+' else removed_cpus ++ part = part[1:] ++ if '-' in part: ++ a, b = part.split('-') ++ a, b = int(a), int(b) ++ cpus.extend(list(range(a, b + 1))) ++ else: ++ a = int(part) ++ cpus.append(a) ++ ++ return list(set(added_cpus)), list(set(removed_cpus)) +diff --git a/rteval/modules/measurement/__init__.py b/rteval/modules/measurement/__init__.py +index 66dc9c5..11bd7b0 100644 +--- a/rteval/modules/measurement/__init__.py ++++ b/rteval/modules/measurement/__init__.py +@@ -5,7 +5,7 @@ + + import libxml2 + from rteval.modules import RtEvalModules, ModuleContainer +-from rteval.systopology import SysTopology as SysTop ++from rteval.systopology import parse_cpulist_from_config + import rteval.cpulist_utils as cpulist_utils + + class MeasurementProfile(RtEvalModules): +@@ -183,12 +183,7 @@ measurement profiles, based on their characteristics""" + rep_n = libxml2.newNode("Measurements") + cpulist = self.__cfg.GetSection("measurement").cpulist + run_on_isolcpus = self.__cfg.GetSection("measurement").run_on_isolcpus +- if cpulist: +- # Convert str to list and remove offline cpus +- cpulist = cpulist_utils.expand_cpulist(cpulist) +- cpulist = cpulist_utils.online_cpulist(cpulist) +- else: +- cpulist = SysTop().online_cpus() if run_on_isolcpus else SysTop().default_cpus() ++ cpulist = parse_cpulist_from_config(cpulist, run_on_isolcpus) + rep_n.newProp("measurecpus", cpulist_utils.collapse_cpulist(cpulist)) + + for mp in self.__measureprofiles: +diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py +index fdca257..7224225 100644 +--- a/rteval/modules/measurement/cyclictest.py ++++ b/rteval/modules/measurement/cyclictest.py +@@ -16,8 +16,7 @@ import math + import libxml2 + from rteval.Log import Log + from rteval.modules import rtevalModulePrototype +-from rteval.systopology import cpuinfo +-from rteval.systopology import SysTopology ++from rteval.systopology import cpuinfo, parse_cpulist_from_config + import rteval.cpulist_utils as cpulist_utils + + expand_cpulist = cpulist_utils.expand_cpulist +@@ -193,39 +192,9 @@ class Cyclictest(rtevalModulePrototype): + self.__priority = int(self.__cfg.setdefault('priority', 95)) + self.__buckets = int(self.__cfg.setdefault('buckets', 2000)) + self.__numcores = 0 +- self.__cpus = [] + self.__cyclicdata = {} +- self.__sparse = False +- self.__run_on_isolcpus = bool(self.__cfg.setdefault('run-on-isolcpus', False)) +- +- if self.__cfg.cpulist: +- self.__cpulist = self.__cfg.cpulist +- self.__cpus = expand_cpulist(self.__cpulist) +- # Only include online cpus +- self.__cpus = cpulist_utils.online_cpulist(self.__cpus) +- # Reset cpulist from the newly calculated self.__cpus +- self.__cpulist = cpulist_utils.collapse_cpulist(self.__cpus) +- self.__cpus = [str(c) for c in self.__cpus] +- self.__sparse = True +- if self.__run_on_isolcpus: +- self._log(Log.WARN, "ignoring --measurement-run-on-isolcpus, since cpulist is specified") +- else: +- self.__cpus = SysTopology().online_cpus_str() +- # Get the cpuset from the environment +- cpuset = os.sched_getaffinity(0) +- # Convert the elements to strings +- cpuset = [str(c) for c in cpuset] +- # Get isolated CPU list +- isolcpus = [str(c) for c in SysTopology().isolated_cpus()] +- # Only include cpus that are in the cpuset and isolated CPUs if run_on_isolcpus is enabled +- self.__cpus = [c for c in self.__cpus if c in cpuset or self.__run_on_isolcpus and c in isolcpus] +- if self.__run_on_isolcpus: +- self.__sparse = True +- self.__cpulist = cpulist_utils.collapse_cpulist([int(c) for c in self.__cpus]) +- +- # Sort the list of cpus to align with the order reported by cyclictest +- self.__cpus.sort(key=int) +- ++ self.__cpulist = self.__cfg.cpulist ++ self.__cpus = [str(c) for c in expand_cpulist(self.__cpulist)] + self.__numcores = len(self.__cpus) + + info = cpuinfo() +@@ -242,10 +211,7 @@ class Cyclictest(rtevalModulePrototype): + logfnc=self._log) + self.__cyclicdata['system'].description = (f"({self.__numcores} cores) ") + info['0']['model name'] + +- if self.__sparse: +- self._log(Log.DEBUG, f"system using {self.__numcores} cpu cores") +- else: +- self._log(Log.DEBUG, f"system has {self.__numcores} cpu cores") ++ self._log(Log.DEBUG, f"system using {self.__numcores} cpu cores") + self.__started = False + self.__cyclicoutput = None + self.__breaktraceval = None +@@ -280,12 +246,8 @@ class Cyclictest(rtevalModulePrototype): + f'-h {self.__buckets}', + f"-p{int(self.__priority)}", + ] +- if self.__sparse: +- self.__cmd.append(f'-t{self.__numcores}') +- self.__cmd.append(f'-a{self.__cpulist}') +- else: +- self.__cmd.append('-t') +- self.__cmd.append('-a') ++ self.__cmd.append(f'-t{self.__numcores}') ++ self.__cmd.append(f'-a{self.__cpulist}') + + if 'threads' in self.__cfg and self.__cfg.threads: + self.__cmd.append(f"-t{int(self.__cfg.threads)}") +diff --git a/rteval/systopology.py b/rteval/systopology.py +index 9e45762..6bcfc77 100644 +--- a/rteval/systopology.py ++++ b/rteval/systopology.py +@@ -241,6 +241,39 @@ class SysTopology: + """ return a list of online cpus in cpulist """ + return [c for c in self.online_cpus() if c in cpulist] + ++ ++def parse_cpulist_from_config(cpulist, run_on_isolcpus=False): ++ """ ++ Generates a cpulist based on --*-cpulist argument given by user ++ :param cpulist: Value of --*-cpulist argument ++ :param run_on_isolcpus: Value of --*-run-on-isolcpus argument ++ :return: Sorted list of CPUs as integers ++ """ ++ if cpulist and not cpulist_utils.is_relative(cpulist): ++ result = cpulist_utils.expand_cpulist(cpulist) ++ # Only include online cpus ++ result = cpulist_utils.online_cpulist(result) ++ else: ++ result = SysTopology().online_cpus() ++ # Get the cpuset from the environment ++ cpuset = os.sched_getaffinity(0) ++ # Get isolated CPU list ++ isolcpus = SysTopology().isolated_cpus() ++ if cpulist and cpulist_utils.is_relative(cpulist): ++ # Include cpus that are not removed in relative cpuset and are either in cpuset from affinity, ++ # isolcpus (with run_on_isolcpus enabled, or added by relative cpuset ++ added_cpus, removed_cpus = cpulist_utils.expand_relative_cpulist(cpulist) ++ result = [c for c in result ++ if (c in cpuset or ++ c in added_cpus or ++ run_on_isolcpus and c in isolcpus) and ++ c not in removed_cpus] ++ else: ++ # Only include cpus that are in the cpuset and isolated CPUs if run_on_isolcpus is enabled ++ result = [c for c in result if c in cpuset or run_on_isolcpus and c in isolcpus] ++ return result ++ ++ + if __name__ == "__main__": + + def unit_test(): +-- +2.43.0 + diff --git a/rteval-Change-the-default-kernel-for-kcompile.patch b/rteval-Change-the-default-kernel-for-kcompile.patch new file mode 100644 index 0000000..8af26cf --- /dev/null +++ b/rteval-Change-the-default-kernel-for-kcompile.patch @@ -0,0 +1,65 @@ +From c0ee73f00f6868e0ead5ace958a88a6a23db6ad3 Mon Sep 17 00:00:00 2001 +From: John Kacur +Date: Thu, 9 Nov 2023 15:43:53 -0500 +Subject: [PATCH] rteval: Change the default kernel for kcompile to linux-6.6.1 + +Change the default kernel for kcompile to linux-6.6.1 + +Signed-off-by: John Kacur +--- + Makefile | 2 +- + rteval/modules/loads/kcompile.py | 4 ++-- + rteval/rteval.conf | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/Makefile b/Makefile +index b73e8c13f3e5..14f74e087eff 100644 +--- a/Makefile ++++ b/Makefile +@@ -18,7 +18,7 @@ PREFIX := /usr + DATADIR := $(DESTDIR)/$(PREFIX)/share + LOADDIR := loadsource + +-KLOAD := $(LOADDIR)/linux-6.1.8.tar.xz ++KLOAD := $(LOADDIR)/linux-6.6.1.tar.xz + BLOAD := $(LOADDIR)/dbench-4.0.tar.gz + LOADS := $(KLOAD) $(BLOAD) + +diff --git a/rteval/modules/loads/kcompile.py b/rteval/modules/loads/kcompile.py +index 8be79ce630d5..0d025771e90e 100644 +--- a/rteval/modules/loads/kcompile.py ++++ b/rteval/modules/loads/kcompile.py +@@ -20,7 +20,7 @@ expand_cpulist = CpuList.expand_cpulist + compress_cpulist = CpuList.compress_cpulist + nonisolated_cpulist = CpuList.nonisolated_cpulist + +-DEFAULT_KERNEL_PREFIX = "linux-6.1" ++DEFAULT_KERNEL_PREFIX = "linux-6.6" + + class KBuildJob: + '''Class to manage a build job bound to a particular node''' +@@ -334,7 +334,7 @@ class Kcompile(CommandLineLoad): + + def ModuleParameters(): + return {"source": {"descr": "Source tar ball", +- "default": "linux-6.1.8.tar.xz", ++ "default": "linux-6.6.1.tar.xz", + "metavar": "TARBALL"}, + "jobspercore": {"descr": "Number of working threads per core", + "default": 2, +diff --git a/rteval/rteval.conf b/rteval/rteval.conf +index 79e28032dc6b..a4aad33e264f 100644 +--- a/rteval/rteval.conf ++++ b/rteval/rteval.conf +@@ -18,7 +18,7 @@ dbench: external + stressng: module + + [kcompile] +-source: linux-6.1.8.xz ++source: linux-6.6.1.xz + jobspercore: 2 + + [hackbench] +-- +2.41.0 + diff --git a/rteval-Convert-CpuList-class-to-a-module.patch b/rteval-Convert-CpuList-class-to-a-module.patch new file mode 100644 index 0000000..b5bbcfb --- /dev/null +++ b/rteval-Convert-CpuList-class-to-a-module.patch @@ -0,0 +1,616 @@ +From a788ea3ec976e51309f0bfae1a41cce704a77f2d Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Thu, 18 Jan 2024 10:18:09 +0100 +Subject: [PATCH 1/4] rteval: Convert CpuList class to a module + +Move out code from CpuList class in rteval.systopology into a separate +module named rteval.cpulist_utils and avoid wrapping CPU lists in +a CpuList object. + +Almost all uses of CpuList in the code either use the static methods of +the class or its constructor to filter online CPUs by running +CpuList(...).cpulist. The only exception to this are NumaNode and +SimNumaNode classes in systopology; these store a CpuList object, +however their .getcpulist() method extracts the list out. Thus, +the class is completely unnecessary. + +Note: A better name for the module would be cpulist, consistent with the +original name of the class, but this name is already used for variables +throughout the code, hence cpulist_utils is used instead. + +Signed-off-by: Tomas Glozar +Signed-off-by: John Kacur +--- + rteval-cmd | 10 +- + rteval/cpulist_utils.py | 128 ++++++++++++++++++ + rteval/modules/loads/__init__.py | 8 +- + rteval/modules/loads/hackbench.py | 9 +- + rteval/modules/loads/kcompile.py | 11 +- + rteval/modules/loads/stressng.py | 8 +- + rteval/modules/measurement/__init__.py | 8 +- + rteval/modules/measurement/cyclictest.py | 11 +- + rteval/systopology.py | 165 ++--------------------- + 9 files changed, 177 insertions(+), 181 deletions(-) + create mode 100644 rteval/cpulist_utils.py + +diff --git a/rteval-cmd b/rteval-cmd +index 7c41429..d224728 100755 +--- a/rteval-cmd ++++ b/rteval-cmd +@@ -30,11 +30,13 @@ from rteval import RtEval, rtevalConfig + from rteval.modules.loads import LoadModules + from rteval.modules.measurement import MeasurementModules + from rteval.version import RTEVAL_VERSION +-from rteval.systopology import CpuList, SysTopology ++from rteval.systopology import SysTopology + from rteval.modules.loads.kcompile import ModuleParameters ++import rteval.cpulist_utils as cpulist_utils + +-compress_cpulist = CpuList.compress_cpulist +-expand_cpulist = CpuList.expand_cpulist ++compress_cpulist = cpulist_utils.compress_cpulist ++expand_cpulist = cpulist_utils.expand_cpulist ++collapse_cpulist = cpulist_utils.collapse_cpulist + + def summarize(repfile, xslt): + """ Summarize an already existing XML report """ +@@ -211,7 +213,7 @@ def remove_offline(cpulist): + """ return cpulist in collapsed compressed form with only online cpus """ + tmplist = expand_cpulist(cpulist) + tmplist = SysTopology().online_cpulist(tmplist) +- return CpuList.collapse_cpulist(tmplist) ++ return collapse_cpulist(tmplist) + + + if __name__ == '__main__': +diff --git a/rteval/cpulist_utils.py b/rteval/cpulist_utils.py +new file mode 100644 +index 0000000..402d579 +--- /dev/null ++++ b/rteval/cpulist_utils.py +@@ -0,0 +1,128 @@ ++# -*- coding: utf-8 -*- ++# SPDX-License-Identifier: GPL-2.0-or-later ++# ++# Copyright 2016 - Clark Williams ++# Copyright 2021 - John Kacur ++# Copyright 2023 - Tomas Glozar ++# ++"""Module providing utility functions for working with CPU lists""" ++ ++import os ++ ++ ++cpupath = "/sys/devices/system/cpu" ++ ++ ++def sysread(path, obj): ++ """ Helper function for reading system files """ ++ with open(os.path.join(path, obj), "r") as fp: ++ return fp.readline().strip() ++ ++ ++def _online_file_exists(): ++ """ Check whether machine / kernel is configured with online file """ ++ # Note: some machines do not have cpu0/online so we check cpu1/online. ++ # In the case of machines with a single CPU, there is no cpu1, but ++ # that is not a problem, since a single CPU cannot be offline ++ return os.path.exists(os.path.join(cpupath, "cpu1/online")) ++ ++ ++def _isolated_file_exists(): ++ """ Check whether machine / kernel is configured with isolated file """ ++ return os.path.exists(os.path.join(cpupath, "isolated")) ++ ++ ++def collapse_cpulist(cpulist): ++ """ ++ Collapse a list of cpu numbers into a string range ++ of cpus (e.g. 0-5, 7, 9) ++ """ ++ cur_range = [None, None] ++ result = [] ++ for cpu in cpulist + [None]: ++ if cur_range[0] is None: ++ cur_range[0] = cur_range[1] = cpu ++ continue ++ if cpu is not None and cpu == cur_range[1] + 1: ++ # Extend currently processed range ++ cur_range[1] += 1 ++ else: ++ # Range processing finished, add range to string ++ result.append(f"{cur_range[0]}-{cur_range[1]}" ++ if cur_range[0] != cur_range[1] ++ else str(cur_range[0])) ++ # Reset ++ cur_range[0] = cur_range[1] = cpu ++ return ",".join(result) ++ ++ ++def compress_cpulist(cpulist): ++ """ return a string representation of cpulist """ ++ if not cpulist: ++ return "" ++ if isinstance(cpulist[0], int): ++ return ",".join(str(e) for e in cpulist) ++ return ",".join(cpulist) ++ ++ ++def expand_cpulist(cpulist): ++ """ expand a range string into an array of cpu numbers ++ don't error check against online cpus ++ """ ++ result = [] ++ ++ if not cpulist: ++ return result ++ ++ for part in cpulist.split(','): ++ if '-' in part: ++ a, b = part.split('-') ++ a, b = int(a), int(b) ++ result.extend(list(range(a, b + 1))) ++ else: ++ a = int(part) ++ result.append(a) ++ return [int(i) for i in list(set(result))] ++ ++ ++def is_online(n): ++ """ check whether cpu n is online """ ++ path = os.path.join(cpupath, f'cpu{n}') ++ ++ # Some hardware doesn't allow cpu0 to be turned off ++ if not os.path.exists(path + '/online') and n == 0: ++ return True ++ ++ return sysread(path, "online") == "1" ++ ++ ++def online_cpulist(cpulist): ++ """ Given a cpulist, return a cpulist of online cpus """ ++ # This only works if the sys online files exist ++ if not _online_file_exists(): ++ return cpulist ++ newlist = [] ++ for cpu in cpulist: ++ if not _online_file_exists() and cpu == '0': ++ newlist.append(cpu) ++ elif is_online(int(cpu)): ++ newlist.append(cpu) ++ return newlist ++ ++ ++def isolated_cpulist(cpulist): ++ """Given a cpulist, return a cpulist of isolated CPUs""" ++ if not _isolated_file_exists(): ++ return cpulist ++ isolated_cpulist = sysread(cpupath, "isolated") ++ isolated_cpulist = expand_cpulist(isolated_cpulist) ++ return list(set(isolated_cpulist) & set(cpulist)) ++ ++ ++def nonisolated_cpulist(cpulist): ++ """Given a cpulist, return a cpulist of non-isolated CPUs""" ++ if not _isolated_file_exists(): ++ return cpulist ++ isolated_cpulist = sysread(cpupath, "isolated") ++ isolated_cpulist = expand_cpulist(isolated_cpulist) ++ return list(set(cpulist).difference(set(isolated_cpulist))) +diff --git a/rteval/modules/loads/__init__.py b/rteval/modules/loads/__init__.py +index 13fba1e..0845742 100644 +--- a/rteval/modules/loads/__init__.py ++++ b/rteval/modules/loads/__init__.py +@@ -11,7 +11,8 @@ import libxml2 + from rteval.Log import Log + from rteval.rtevalConfig import rtevalCfgSection + from rteval.modules import RtEvalModules, rtevalModulePrototype +-from rteval.systopology import CpuList, SysTopology as SysTop ++from rteval.systopology import SysTopology as SysTop ++import rteval.cpulist_utils as cpulist_utils + + class LoadThread(rtevalModulePrototype): + def __init__(self, name, config, logger=None): +@@ -117,10 +118,11 @@ class LoadModules(RtEvalModules): + cpulist = self._cfg.GetSection(self._module_config).cpulist + if cpulist: + # Convert str to list and remove offline cpus +- cpulist = CpuList(cpulist).cpulist ++ cpulist = cpulist_utils.expand_cpulist(cpulist) ++ cpulist = cpulist_utils.online_cpulist(cpulist) + else: + cpulist = SysTop().default_cpus() +- rep_n.newProp("loadcpus", CpuList.collapse_cpulist(cpulist)) ++ rep_n.newProp("loadcpus", cpulist_utils.collapse_cpulist(cpulist)) + + return rep_n + +diff --git a/rteval/modules/loads/hackbench.py b/rteval/modules/loads/hackbench.py +index cfc7063..a70fdb3 100644 +--- a/rteval/modules/loads/hackbench.py ++++ b/rteval/modules/loads/hackbench.py +@@ -16,10 +16,11 @@ import errno + from signal import SIGKILL + from rteval.modules.loads import CommandLineLoad + from rteval.Log import Log +-from rteval.systopology import CpuList, SysTopology ++from rteval.systopology import SysTopology ++import rteval.cpulist_utils as cpulist_utils + +-expand_cpulist = CpuList.expand_cpulist +-isolated_cpulist = CpuList.isolated_cpulist ++expand_cpulist = cpulist_utils.expand_cpulist ++isolated_cpulist = cpulist_utils.isolated_cpulist + + class Hackbench(CommandLineLoad): + def __init__(self, config, logger): +@@ -61,7 +62,7 @@ class Hackbench(CommandLineLoad): + self.cpus[n] = [c for c in self.cpus[n] if c in expand_cpulist(self.cpulist)] + # if a cpulist was not specified, exclude isolated cpus + else: +- self.cpus[n] = CpuList.nonisolated_cpulist(self.cpus[n]) ++ self.cpus[n] = cpulist_utils.nonisolated_cpulist(self.cpus[n]) + + # track largest number of cpus used on a node + node_biggest = len(self.cpus[n]) +diff --git a/rteval/modules/loads/kcompile.py b/rteval/modules/loads/kcompile.py +index 0d02577..b606f7a 100644 +--- a/rteval/modules/loads/kcompile.py ++++ b/rteval/modules/loads/kcompile.py +@@ -14,11 +14,12 @@ import subprocess + from rteval.modules import rtevalRuntimeError + from rteval.modules.loads import CommandLineLoad + from rteval.Log import Log +-from rteval.systopology import CpuList, SysTopology ++from rteval.systopology import SysTopology ++import rteval.cpulist_utils as cpulist_utils + +-expand_cpulist = CpuList.expand_cpulist +-compress_cpulist = CpuList.compress_cpulist +-nonisolated_cpulist = CpuList.nonisolated_cpulist ++expand_cpulist = cpulist_utils.expand_cpulist ++compress_cpulist = cpulist_utils.compress_cpulist ++nonisolated_cpulist = cpulist_utils.nonisolated_cpulist + + DEFAULT_KERNEL_PREFIX = "linux-6.6" + +@@ -38,7 +39,7 @@ class KBuildJob: + os.mkdir(self.objdir) + + # Exclude isolated CPUs if cpulist not set +- cpus_available = len(nonisolated_cpulist(self.node.cpus.cpulist)) ++ cpus_available = len(nonisolated_cpulist(self.node.cpus)) + + if os.path.exists('/usr/bin/numactl') and not cpulist: + # Use numactl +diff --git a/rteval/modules/loads/stressng.py b/rteval/modules/loads/stressng.py +index 7c9e63f..cbcf6b7 100644 +--- a/rteval/modules/loads/stressng.py ++++ b/rteval/modules/loads/stressng.py +@@ -7,10 +7,10 @@ import subprocess + import signal + from rteval.modules.loads import CommandLineLoad + from rteval.Log import Log +-from rteval.systopology import CpuList, SysTopology ++from rteval.systopology import SysTopology ++import rteval.cpulist_utils as cpulist_utils + +-expand_cpulist = CpuList.expand_cpulist +-nonisolated_cpulist = CpuList.nonisolated_cpulist ++expand_cpulist = cpulist_utils.expand_cpulist + + class Stressng(CommandLineLoad): + " This class creates a load module that runs stress-ng " +@@ -73,7 +73,7 @@ class Stressng(CommandLineLoad): + cpus[n] = [c for c in cpus[n] if c in expand_cpulist(self.cpulist)] + # if a cpulist was not specified, exclude isolated cpus + else: +- cpus[n] = CpuList.nonisolated_cpulist(cpus[n]) ++ cpus[n] = cpulist_utils.nonisolated_cpulist(cpus[n]) + + + # remove nodes with no cpus available for running +diff --git a/rteval/modules/measurement/__init__.py b/rteval/modules/measurement/__init__.py +index 41b8022..66dc9c5 100644 +--- a/rteval/modules/measurement/__init__.py ++++ b/rteval/modules/measurement/__init__.py +@@ -5,7 +5,8 @@ + + import libxml2 + from rteval.modules import RtEvalModules, ModuleContainer +-from rteval.systopology import CpuList, SysTopology as SysTop ++from rteval.systopology import SysTopology as SysTop ++import rteval.cpulist_utils as cpulist_utils + + class MeasurementProfile(RtEvalModules): + """Keeps and controls all the measurement modules with the same measurement profile""" +@@ -184,10 +185,11 @@ measurement profiles, based on their characteristics""" + run_on_isolcpus = self.__cfg.GetSection("measurement").run_on_isolcpus + if cpulist: + # Convert str to list and remove offline cpus +- cpulist = CpuList(cpulist).cpulist ++ cpulist = cpulist_utils.expand_cpulist(cpulist) ++ cpulist = cpulist_utils.online_cpulist(cpulist) + else: + cpulist = SysTop().online_cpus() if run_on_isolcpus else SysTop().default_cpus() +- rep_n.newProp("measurecpus", CpuList.collapse_cpulist(cpulist)) ++ rep_n.newProp("measurecpus", cpulist_utils.collapse_cpulist(cpulist)) + + for mp in self.__measureprofiles: + mprep_n = mp.MakeReport() +diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py +index 1b14e7e..fdca257 100644 +--- a/rteval/modules/measurement/cyclictest.py ++++ b/rteval/modules/measurement/cyclictest.py +@@ -17,9 +17,10 @@ import libxml2 + from rteval.Log import Log + from rteval.modules import rtevalModulePrototype + from rteval.systopology import cpuinfo +-from rteval.systopology import CpuList, SysTopology ++from rteval.systopology import SysTopology ++import rteval.cpulist_utils as cpulist_utils + +-expand_cpulist = CpuList.expand_cpulist ++expand_cpulist = cpulist_utils.expand_cpulist + + class RunData: + '''class to keep instance data from a cyclictest run''' +@@ -201,9 +202,9 @@ class Cyclictest(rtevalModulePrototype): + self.__cpulist = self.__cfg.cpulist + self.__cpus = expand_cpulist(self.__cpulist) + # Only include online cpus +- self.__cpus = CpuList(self.__cpus).cpulist ++ self.__cpus = cpulist_utils.online_cpulist(self.__cpus) + # Reset cpulist from the newly calculated self.__cpus +- self.__cpulist = CpuList.collapse_cpulist(self.__cpus) ++ self.__cpulist = cpulist_utils.collapse_cpulist(self.__cpus) + self.__cpus = [str(c) for c in self.__cpus] + self.__sparse = True + if self.__run_on_isolcpus: +@@ -220,7 +221,7 @@ class Cyclictest(rtevalModulePrototype): + self.__cpus = [c for c in self.__cpus if c in cpuset or self.__run_on_isolcpus and c in isolcpus] + if self.__run_on_isolcpus: + self.__sparse = True +- self.__cpulist = CpuList.collapse_cpulist([int(c) for c in self.__cpus]) ++ self.__cpulist = cpulist_utils.collapse_cpulist([int(c) for c in self.__cpus]) + + # Sort the list of cpus to align with the order reported by cyclictest + self.__cpus.sort(key=int) +diff --git a/rteval/systopology.py b/rteval/systopology.py +index bed5192..9e45762 100644 +--- a/rteval/systopology.py ++++ b/rteval/systopology.py +@@ -9,12 +9,8 @@ + import os + import os.path + import glob +- +- +-def sysread(path, obj): +- """ Helper function for reading system files """ +- with open(os.path.join(path, obj), "r") as fp: +- return fp.readline().strip() ++import rteval.cpulist_utils as cpulist_utils ++from rteval.cpulist_utils import sysread + + def cpuinfo(): + ''' return a dictionary of cpu keys with various cpu information ''' +@@ -56,145 +52,6 @@ def cpuinfo(): + return info + + +-# +-# class to provide access to a list of cpus +-# +- +-class CpuList: +- "Object that represents a group of system cpus" +- +- cpupath = '/sys/devices/system/cpu' +- +- def __init__(self, cpulist): +- if isinstance(cpulist, list): +- self.cpulist = cpulist +- elif isinstance(cpulist, str): +- self.cpulist = self.expand_cpulist(cpulist) +- self.cpulist = self.online_cpulist(self.cpulist) +- self.cpulist.sort() +- +- def __str__(self): +- return self.collapse_cpulist(self.cpulist) +- +- def __contains__(self, cpu): +- return cpu in self.cpulist +- +- def __len__(self): +- return len(self.cpulist) +- +- def getcpulist(self): +- """ return the list of cpus tracked """ +- return self.cpulist +- +- @staticmethod +- def online_file_exists(): +- """ Check whether machine / kernel is configured with online file """ +- # Note: some machines do not have cpu0/online so we check cpu1/online. +- # In the case of machines with a single CPU, there is no cpu1, but +- # that is not a problem, since a single CPU cannot be offline +- return os.path.exists(os.path.join(CpuList.cpupath, "cpu1/online")) +- +- @staticmethod +- def isolated_file_exists(): +- """ Check whether machine / kernel is configured with isolated file """ +- return os.path.exists(os.path.join(CpuList.cpupath, "isolated")) +- +- @staticmethod +- def collapse_cpulist(cpulist): +- """ +- Collapse a list of cpu numbers into a string range +- of cpus (e.g. 0-5, 7, 9) +- """ +- cur_range = [None, None] +- result = [] +- for cpu in cpulist + [None]: +- if cur_range[0] is None: +- cur_range[0] = cur_range[1] = cpu +- continue +- if cpu is not None and cpu == cur_range[1] + 1: +- # Extend currently processed range +- cur_range[1] += 1 +- else: +- # Range processing finished, add range to string +- result.append(f"{cur_range[0]}-{cur_range[1]}" +- if cur_range[0] != cur_range[1] +- else str(cur_range[0])) +- # Reset +- cur_range[0] = cur_range[1] = cpu +- return ",".join(result) +- +- @staticmethod +- def compress_cpulist(cpulist): +- """ return a string representation of cpulist """ +- if not cpulist: +- return "" +- if isinstance(cpulist[0], int): +- return ",".join(str(e) for e in cpulist) +- return ",".join(cpulist) +- +- @staticmethod +- def expand_cpulist(cpulist): +- """ expand a range string into an array of cpu numbers +- don't error check against online cpus +- """ +- result = [] +- +- if not cpulist: +- return result +- +- for part in cpulist.split(','): +- if '-' in part: +- a, b = part.split('-') +- a, b = int(a), int(b) +- result.extend(list(range(a, b + 1))) +- else: +- a = int(part) +- result.append(a) +- return [int(i) for i in list(set(result))] +- +- @staticmethod +- def is_online(n): +- """ check whether cpu n is online """ +- path = os.path.join(CpuList.cpupath, f'cpu{n}') +- +- # Some hardware doesn't allow cpu0 to be turned off +- if not os.path.exists(path + '/online') and n == 0: +- return True +- +- return sysread(path, "online") == "1" +- +- @staticmethod +- def online_cpulist(cpulist): +- """ Given a cpulist, return a cpulist of online cpus """ +- # This only works if the sys online files exist +- if not CpuList.online_file_exists(): +- return cpulist +- newlist = [] +- for cpu in cpulist: +- if not CpuList.online_file_exists() and cpu == '0': +- newlist.append(cpu) +- elif CpuList.is_online(int(cpu)): +- newlist.append(cpu) +- return newlist +- +- @staticmethod +- def isolated_cpulist(cpulist): +- """Given a cpulist, return a cpulist of isolated CPUs""" +- if not CpuList.isolated_file_exists(): +- return cpulist +- isolated_cpulist = sysread(CpuList.cpupath, "isolated") +- isolated_cpulist = CpuList.expand_cpulist(isolated_cpulist) +- return list(set(isolated_cpulist) & set(cpulist)) +- +- @staticmethod +- def nonisolated_cpulist(cpulist): +- """Given a cpulist, return a cpulist of non-isolated CPUs""" +- if not CpuList.isolated_file_exists(): +- return cpulist +- isolated_cpulist = sysread(CpuList.cpupath, "isolated") +- isolated_cpulist = CpuList.expand_cpulist(isolated_cpulist) +- return list(set(cpulist).difference(set(isolated_cpulist))) +- + # + # class to abstract access to NUMA nodes in /sys filesystem + # +@@ -208,7 +65,8 @@ class NumaNode: + """ + self.path = path + self.nodeid = int(os.path.basename(path)[4:].strip()) +- self.cpus = CpuList(sysread(self.path, "cpulist")) ++ self.cpus = cpulist_utils.expand_cpulist(sysread(self.path, "cpulist")) ++ self.cpus = cpulist_utils.online_cpulist(self.cpus) + self.getmeminfo() + + def __contains__(self, cpu): +@@ -240,11 +98,11 @@ class NumaNode: + + def getcpustr(self): + """ return list of cpus for this node as a string """ +- return str(self.cpus) ++ return cpulist_utils.collapse_cpulist(self.cpus) + + def getcpulist(self): + """ return list of cpus for this node """ +- return self.cpus.getcpulist() ++ return self.cpus + + class SimNumaNode(NumaNode): + """class representing a simulated NUMA node. +@@ -257,7 +115,8 @@ class SimNumaNode(NumaNode): + + def __init__(self): + self.nodeid = 0 +- self.cpus = CpuList(sysread(SimNumaNode.cpupath, "possible")) ++ self.cpus = cpulist_utils.expand_cpulist(sysread(SimNumaNode.cpupath, "possible")) ++ self.cpus = cpulist_utils.online_cpulist(self.cpus) + self.getmeminfo() + + def getmeminfo(self): +@@ -339,7 +198,7 @@ class SysTopology: + """ return a list of integers of all online cpus """ + cpulist = [] + for n in self.nodes: +- cpulist += self.getcpus(n) ++ cpulist += cpulist_utils.online_cpulist(self.getcpus(n)) + cpulist.sort() + return cpulist + +@@ -347,7 +206,7 @@ class SysTopology: + """ return a list of integers of all isolated cpus """ + cpulist = [] + for n in self.nodes: +- cpulist += CpuList.isolated_cpulist(self.getcpus(n)) ++ cpulist += cpulist_utils.isolated_cpulist(self.getcpus(n)) + cpulist.sort() + return cpulist + +@@ -355,7 +214,7 @@ class SysTopology: + """ return a list of integers of all default schedulable cpus, i.e. online non-isolated cpus """ + cpulist = [] + for n in self.nodes: +- cpulist += CpuList.nonisolated_cpulist(self.getcpus(n)) ++ cpulist += cpulist_utils.nonisolated_cpulist(self.getcpus(n)) + cpulist.sort() + return cpulist + +@@ -403,7 +262,7 @@ if __name__ == "__main__": + + onlcpus = s.online_cpus() + print(f'onlcpus = {onlcpus}') +- onlcpus = CpuList.collapse_cpulist(onlcpus) ++ onlcpus = cpulist_utils.collapse_cpulist(onlcpus) + print(f'onlcpus = {onlcpus}') + + onlcpus_str = s.online_cpus_str() +-- +2.43.0 + diff --git a/rteval-Disable-use-of-python-dmidecode.patch b/rteval-Disable-use-of-python-dmidecode.patch new file mode 100644 index 0000000..ceed1b5 --- /dev/null +++ b/rteval-Disable-use-of-python-dmidecode.patch @@ -0,0 +1,87 @@ +From d142f0d23d8df1cede3573c3d6cfbf16535b3475 Mon Sep 17 00:00:00 2001 +From: John Kacur +Date: Wed, 20 Dec 2023 17:55:21 -0500 +Subject: [PATCH 2/2] rteval: Disable use of python-dmidecode + +python-dmidecode is not being maintained upstream. +For now just disable it's use in rteval since it is useful but not +essential information for running rteval + +In the future look at generating this info directly using dmidecode. + +Signed-off-by: John Kacur +--- + README | 6 ------ + doc/installing.txt | 9 --------- + rteval/sysinfo/dmi.py | 4 ++-- + 3 files changed, 2 insertions(+), 17 deletions(-) + +diff --git a/README b/README +index a5cf98344a46..b352d7f66ad2 100644 +--- a/README ++++ b/README +@@ -19,15 +19,9 @@ Rteval requires the following packages to run: + Python >= 3.0 + http://www.python.org/download/ + +-python-ethtool +- git://git.kernel.org/pub/scm/linux/kernel/git/acme/python-ethtool.git +- + python-lxml + http://lxml.de/ + +-python-dmidecode +- http://www.ohloh.net/p/python-dmidecode +- + libxml2-python + http://xmlsoft.org/ + +diff --git a/doc/installing.txt b/doc/installing.txt +index ff2d43cb9481..227249bbc9ed 100644 +--- a/doc/installing.txt ++++ b/doc/installing.txt +@@ -1,18 +1,10 @@ + The rteval utility requires some external software libraries to run + properly. These are: + +-python-ethtool +- A python library to query network interfaces +- git://git.kernel.org/pub/scm/linux/kernel/git/acme/python-ethtool.git +- + python-lxml + A python library to parse XML files and XSLT stylesheets + http://lxml.de/ + +-python-dmidecode +- A python library used to access DMI table information +- http://www.autonomy.net.au/display/pydmi/Home +- + libxml2-python + A python library to parse XML files + http://xmlsoft.org/ +@@ -22,7 +14,6 @@ rt-tests + git://git.kernel.org/pub/scm/utils/rt-tests/rt-tests.git + + +-$ sudo yum install python-{dmidecode,ethtool) + $ git clone \ + git://git.kernel.org/pub/scm/utils/rt-tests/rt-tests.git + $ cd rt-tests && sudo make prefix=/usr install +diff --git a/rteval/sysinfo/dmi.py b/rteval/sysinfo/dmi.py +index e8285d263fe6..c01a0eef1435 100644 +--- a/rteval/sysinfo/dmi.py ++++ b/rteval/sysinfo/dmi.py +@@ -15,8 +15,8 @@ from rteval import xmlout + from rteval import rtevalConfig + + try: +- import dmidecode +- dmidecode_avail = True ++ # import dmidecode ++ dmidecode_avail = False + except ModuleNotFoundError: + dmidecode_avail = False + +-- +2.42.0 + diff --git a/rteval-Implement-initial-dmidecode-support.patch b/rteval-Implement-initial-dmidecode-support.patch new file mode 100644 index 0000000..4ed3fe8 --- /dev/null +++ b/rteval-Implement-initial-dmidecode-support.patch @@ -0,0 +1,299 @@ +From 43c45ba7dae488c16beb359834b3a71ebf6068d6 Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Mon, 4 Mar 2024 11:26:03 +0100 +Subject: [PATCH] rteval: Implement initial dmidecode support + +Previously rteval used python-dmidecode to gather DMI data from a +system. Since python-dmidecode is without a maintainer, its support was +removed in d142f0d2 ("rteval: Disable use of python-dmidecode"). + +Add get_dmidecode_xml() function into rteval/sysinfo/dmi.py that does +simple parsing of dmidecode command-line tool output without any +structure changes and include it into the rteval report. + +Notes: +- ProcessWarnings() in rteval.sysinfo.dmi was reworked into a class + method of DMIinfo and to use the class's __log field as logger. It + now also does not ignore warnings that appear when running rteval as + non-root, since that is no longer supported. Additionally, + a duplicate call in rteval-cmd was removed. +- rteval/rteval_dmi.xsl XSLT template was left untouched and is + currectly not used. In a future commit, it is expected to be rewritten + to transform the XML format outputted by get_dmidecode_xml() into the + same format that was used with python-dmidecode. + +Signed-off-by: Tomas Glozar +Signed-off-by: John Kacur +--- + rteval-cmd | 2 - + rteval/sysinfo/__init__.py | 2 +- + rteval/sysinfo/dmi.py | 178 ++++++++++++++++++++++++------------- + 3 files changed, 118 insertions(+), 64 deletions(-) + +diff --git a/rteval-cmd b/rteval-cmd +index a5e8746..018a414 100755 +--- a/rteval-cmd ++++ b/rteval-cmd +@@ -268,8 +268,6 @@ if __name__ == '__main__': + | (rtevcfg.debugging and Log.DEBUG) + logger.SetLogVerbosity(loglev) + +- dmi.ProcessWarnings(logger=logger) +- + # Load modules + loadmods = LoadModules(config, logger=logger) + measuremods = MeasurementModules(config, logger=logger) +diff --git a/rteval/sysinfo/__init__.py b/rteval/sysinfo/__init__.py +index d3f9efb..09af52e 100644 +--- a/rteval/sysinfo/__init__.py ++++ b/rteval/sysinfo/__init__.py +@@ -30,7 +30,7 @@ class SystemInfo(KernelInfo, SystemServices, dmi.DMIinfo, CPUtopology, + NetworkInfo.__init__(self, logger=logger) + + # Parse initial DMI decoding errors +- dmi.ProcessWarnings(logger=logger) ++ self.ProcessWarnings() + + # Parse CPU info + CPUtopology._parse(self) +diff --git a/rteval/sysinfo/dmi.py b/rteval/sysinfo/dmi.py +index c01a0ee..f1aab9f 100644 +--- a/rteval/sysinfo/dmi.py ++++ b/rteval/sysinfo/dmi.py +@@ -3,6 +3,7 @@ + # Copyright 2009 - 2013 Clark Williams + # Copyright 2009 - 2013 David Sommerseth + # Copyright 2022 John Kacur ++# Copyright 2024 Tomas Glozar + # + """ dmi.py class to wrap DMI Table Information """ + +@@ -10,65 +11,125 @@ import sys + import os + import libxml2 + import lxml.etree ++import shutil ++import re ++from subprocess import Popen, PIPE, SubprocessError + from rteval.Log import Log + from rteval import xmlout + from rteval import rtevalConfig + +-try: +- # import dmidecode +- dmidecode_avail = False +-except ModuleNotFoundError: +- dmidecode_avail = False +- +-def set_dmidecode_avail(val): +- """ Used to set global variable dmidecode_avail from a function """ +- global dmidecode_avail +- dmidecode_avail = val +- +-def ProcessWarnings(logger=None): +- """ Process Warnings from dmidecode """ +- +- if not dmidecode_avail: +- return +- +- if not hasattr(dmidecode, 'get_warnings'): +- return +- +- warnings = dmidecode.get_warnings() +- if warnings is None: +- return +- +- ignore1 = '/dev/mem: Permission denied' +- ignore2 = 'No SMBIOS nor DMI entry point found, sorry.' +- ignore3 = 'Failed to open memory buffer (/dev/mem): Permission denied' +- ignore = (ignore1, ignore2, ignore3) +- for warnline in warnings.split('\n'): +- # Ignore these warnings, as they are "valid" if not running as root +- if warnline in ignore: +- continue + +- # All other warnings will be printed +- if len(warnline) > 0: +- logger.log(Log.DEBUG, f"** DMI WARNING ** {warnline}") +- set_dmidecode_avail(False) ++def get_dmidecode_xml(dmidecode_executable): ++ """ ++ Transform human-readable dmidecode output into machine-processable XML format ++ :param dmidecode_executable: Path to dmidecode tool executable ++ :return: Tuple of values with resulting XML and dmidecode warnings ++ """ ++ proc = Popen(dmidecode_executable, text=True, stdout=PIPE, stderr=PIPE) ++ outs, errs = proc.communicate() ++ parts = outs.split("\n\n") ++ if len(parts) < 2: ++ raise RuntimeError("Parsing dmidecode output failed") ++ header = parts[0] ++ handles = parts[1:] ++ root = lxml.etree.Element("dmidecode") ++ # Parse dmidecode output header ++ # Note: Only supports SMBIOS data currently ++ regex = re.compile(r"# dmidecode (\d+\.\d+)\n" ++ r"Getting SMBIOS data from sysfs\.\n" ++ r"SMBIOS ((?:\d+\.)+\d+) present\.\n" ++ r"(?:(\d+) structures occupying (\d+) bytes\.\n)?" ++ r"Table at (0x[0-9A-Fa-f]+)\.", re.MULTILINE) ++ match = re.match(regex, header) ++ if match is None: ++ raise RuntimeError("Parsing dmidecode output failed") ++ root.attrib["dmidecodeversion"] = match.group(1) ++ root.attrib["smbiosversion"] = match.group(2) ++ if match.group(3) is not None: ++ root.attrib["structures"] = match.group(3) ++ if match.group(4) is not None: ++ root.attrib["size"] = match.group(4) ++ root.attrib["address"] = match.group(5) ++ ++ # Generate element per handle in dmidecode output ++ for handle_text in handles: ++ if not handle_text: ++ # Empty line ++ continue + +- dmidecode.clear_warnings() ++ handle = lxml.etree.Element("Handle") ++ lines = handle_text.splitlines() ++ # Parse handle header ++ if len(lines) < 2: ++ raise RuntimeError("Parsing dmidecode handle failed") ++ header, name, content = lines[0], lines[1], lines[2:] ++ match = re.match(r"Handle (0x[0-9A-Fa-f]{4}), " ++ r"DMI type (\d+), (\d+) bytes", header) ++ if match is None: ++ raise RuntimeError("Parsing dmidecode handle failed") ++ handle.attrib["address"] = match.group(1) ++ handle.attrib["type"] = match.group(2) ++ handle.attrib["bytes"] = match.group(3) ++ handle.attrib["name"] = name ++ ++ # Parse all fields in handle and create an element for each ++ list_field = None ++ for index, line in enumerate(content): ++ line = content[index] ++ if line.rfind("\t") > 0: ++ # We are inside a list field, add value to it ++ value = lxml.etree.Element("Value") ++ value.text = line.strip() ++ list_field.append(value) ++ continue ++ line = line.lstrip().split(":", 1) ++ if len(line) != 2: ++ raise RuntimeError("Parsing dmidecode field failed") ++ if not line[1] or (index + 1 < len(content) and ++ content[index + 1].rfind("\t") > 0): ++ # No characters after : or next line is inside list field ++ # means a list field ++ # Note: there are list fields which specify a number of ++ # items, for example "Installable Languages", so merely ++ # checking for no characters after : is not enough ++ list_field = lxml.etree.Element("List") ++ list_field.attrib["Name"] = line[0].strip() ++ handle.append(list_field) ++ else: ++ # Regular field ++ field = lxml.etree.Element("Field") ++ field.attrib["Name"] = line[0].strip() ++ field.text = line[1].strip() ++ handle.append(field) ++ ++ root.append(handle) ++ ++ return root, errs + + + class DMIinfo: +- '''class used to obtain DMI info via python-dmidecode''' ++ '''class used to obtain DMI info via dmidecode''' + + def __init__(self, logger=None): + self.__version = '0.6' + self._log = logger + +- if not dmidecode_avail: +- logger.log(Log.DEBUG, "DMI info unavailable, ignoring DMI tables") ++ dmidecode_executable = shutil.which("dmidecode") ++ if dmidecode_executable is None: ++ logger.log(Log.DEBUG, "DMI info unavailable," ++ " ignoring DMI tables") + self.__fake = True + return + + self.__fake = False +- self.__dmixml = dmidecode.dmidecodeXML() ++ try: ++ self.__dmixml, self.__warnings = get_dmidecode_xml( ++ dmidecode_executable) ++ except (RuntimeError, OSError, SubprocessError) as error: ++ logger.log(Log.DEBUG, "DMI info unavailable: {};" ++ " ignoring DMI tables".format(str(error))) ++ self.__fake = True ++ return + + self.__xsltparser = self.__load_xslt('rteval_dmi.xsl') + +@@ -88,30 +149,25 @@ class DMIinfo: + + raise RuntimeError(f'Could not locate XSLT template for DMI data ({fname})') + ++ def ProcessWarnings(self): ++ """Prints out warnings from dmidecode into log if there were any""" ++ if self.__fake or self._log is None: ++ return ++ for warnline in self.__warnings.split('\n'): ++ if len(warnline) > 0: ++ self._log.log(Log.DEBUG, f"** DMI WARNING ** {warnline}") ++ + def MakeReport(self): + """ Add DMI information to final report """ +- rep_n = libxml2.newNode("DMIinfo") +- rep_n.newProp("version", self.__version) + if self.__fake: ++ rep_n = libxml2.newNode("DMIinfo") ++ rep_n.newProp("version", self.__version) + rep_n.addContent("No DMI tables available") + rep_n.newProp("not_available", "1") +- else: +- self.__dmixml.SetResultType(dmidecode.DMIXML_DOC) +- try: +- dmiqry = xmlout.convert_libxml2_to_lxml_doc(self.__dmixml.QuerySection('all')) +- except Exception as ex1: +- self._log.log(Log.DEBUG, f'** EXCEPTION {str(ex1)}, will query BIOS only') +- try: +- # If we can't query 'all', at least query 'bios' +- dmiqry = xmlout.convert_libxml2_to_lxml_doc(self.__dmixml.QuerySection('bios')) +- except Exception as ex2: +- rep_n.addContent("No DMI tables available") +- rep_n.newProp("not_available", "1") +- self._log.log(Log.DEBUG, f'** EXCEPTION {str(ex2)}, dmi info will not be reported') +- return rep_n +- resdoc = self.__xsltparser(dmiqry) +- dmi_n = xmlout.convert_lxml_to_libxml2_nodes(resdoc.getroot()) +- rep_n.addChild(dmi_n) ++ return rep_n ++ rep_n = xmlout.convert_lxml_to_libxml2_nodes(self.__dmixml) ++ rep_n.setName("DMIinfo") ++ rep_n.newProp("version", self.__version) + return rep_n + + def unit_test(rootdir): +@@ -130,12 +186,12 @@ def unit_test(rootdir): + log = Log() + log.SetLogVerbosity(Log.DEBUG|Log.INFO) + +- ProcessWarnings(logger=log) + if os.getuid() != 0: + print("** ERROR ** Must be root to run this unit_test()") + return 1 + + d = DMIinfo(logger=log) ++ d.ProcessWarnings() + dx = d.MakeReport() + x = libxml2.newDoc("1.0") + x.setRootElement(dx) +-- +2.44.0 + diff --git a/rteval-Makefile-More-rpm-cleanups.patch b/rteval-Makefile-More-rpm-cleanups.patch new file mode 100644 index 0000000..abca54c --- /dev/null +++ b/rteval-Makefile-More-rpm-cleanups.patch @@ -0,0 +1,64 @@ +From 2d2e85c459d240926c99b1961bbef090aa80a1fc Mon Sep 17 00:00:00 2001 +From: John Kacur +Date: Wed, 20 Dec 2023 17:22:22 -0500 +Subject: [PATCH 1/2] rteval: Makefile: More rpm cleanups + +Afer having removed the upstream specfile, there were still a few +references to rpms in the Makefile. These are not necessary because any +work with rpms can be done with modern rpm tools and are typically done +by distributions + +Also test whether directory 'run' exists since it may have been removed +by make realclean, and create it if it does not + +Signed-off-by: John Kacur +--- + Makefile | 11 +++-------- + 1 file changed, 3 insertions(+), 8 deletions(-) + +diff --git a/Makefile b/Makefile +index ee4cca555b95..b8bed643f760 100644 +--- a/Makefile ++++ b/Makefile +@@ -31,6 +31,7 @@ load: + $(PYTHON) rteval-cmd --onlyload -D -L -v --workdir=./run --loaddir=$(HERE)/loadsource -f $(HERE)/rteval/rteval.conf -i $(HERE)/rteval + + sysreport: ++ [ -d $(HERE)/run ] || mkdir run + $(PYTHON) rteval-cmd -D -v --workdir=$(HERE)/run --loaddir=$(HERE)/loadsource --duration=$(D) -i $(HERE)/rteval --sysreport + + clean: +@@ -39,7 +40,7 @@ clean: + + realclean: clean + [ -f $(XMLRPCDIR)/Makefile ] && make -C $(XMLRPCDIR) maintainer-clean || echo -n +- rm -rf run rpm ++ rm -rf run + + install: install_loads install_rteval + +@@ -73,13 +74,6 @@ rteval-xmlrpc-$(XMLRPCVER).tar.gz : + make distcheck + cp $(XMLRPCDIR)/rteval-xmlrpc-$(XMLRPCVER).tar.gz $(HERE)/ + +-rpm_prep: +- rm -rf rpm +- mkdir -p rpm/{BUILD,RPMS,SRPMS,SOURCES,SPECS} +- +-rpms rpm: rpm_prep rtevalrpm loadrpm +- +- + help: + @echo "" + @echo "rteval Makefile targets:" +@@ -88,6 +82,7 @@ help: + @echo " tarfile: create the source tarball" + @echo " install: install rteval locally" + @echo " clean: cleanup generated files" ++ @echo " realclean: Same as clean plus directory run" + @echo " sysreport: do a short testrun and generate sysreport data" + @echo " tags: generate a ctags file" + @echo " cleantags: remove the ctags file" +-- +2.42.0 + diff --git a/rteval-Minor-improvements-to-CpuList-class.patch b/rteval-Minor-improvements-to-CpuList-class.patch new file mode 100644 index 0000000..44430e0 --- /dev/null +++ b/rteval-Minor-improvements-to-CpuList-class.patch @@ -0,0 +1,85 @@ +From 768daa63ec8e2299da53afe081e7304b37fc91cf Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Wed, 29 Nov 2023 10:34:55 +0100 +Subject: [PATCH 2/9] rteval: Minor improvements to CpuList class + +- Remove unnecessary if-else from online_file_exists +- Use cpupath in online_file_exists +- In is_online, remove check for n in cpuset and make it static +- Mark also the remaining methods static since they do not rely on +any fields of the class + +Signed-off-by: Tomas Glozar +- Removed incorrect line from commit message +Signed-off-by: John Kacur +--- + rteval/systopology.py | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/rteval/systopology.py b/rteval/systopology.py +index ea8e242..60ac8e8 100644 +--- a/rteval/systopology.py ++++ b/rteval/systopology.py +@@ -82,12 +82,17 @@ class CpuList: + def __len__(self): + return len(self.cpulist) + ++ def getcpulist(self): ++ """ return the list of cpus tracked """ ++ return self.cpulist ++ + @staticmethod + def online_file_exists(): + """ Check whether machine / kernel is configured with online file """ +- if os.path.exists('/sys/devices/system/cpu/cpu1/online'): +- return True +- return False ++ # Note: some machines do not have cpu0/online so we check cpu1/online. ++ # In the case of machines with a single CPU, there is no cpu1, but ++ # that is not a problem, since a single CPU cannot be offline ++ return os.path.exists(os.path.join(CpuList.cpupath, "cpu1/online")) + + @staticmethod + def isolated_file_exists(): +@@ -147,14 +152,9 @@ class CpuList: + result.append(a) + return [int(i) for i in list(set(result))] + +- def getcpulist(self): +- """ return the list of cpus tracked """ +- return self.cpulist +- +- def is_online(self, n): ++ @staticmethod ++ def is_online(n): + """ check whether cpu n is online """ +- if n not in self.cpulist: +- raise RuntimeError(f"invalid cpu number {n}") + path = os.path.join(CpuList.cpupath, f'cpu{n}') + + # Some hardware doesn't allow cpu0 to be turned off +@@ -163,16 +163,17 @@ class CpuList: + + return sysread(path, "online") == "1" + +- def online_cpulist(self, cpulist): ++ @staticmethod ++ def online_cpulist(cpulist): + """ Given a cpulist, return a cpulist of online cpus """ + # This only works if the sys online files exist +- if not self.online_file_exists(): ++ if not CpuList.online_file_exists(): + return cpulist + newlist = [] + for cpu in cpulist: +- if not self.online_file_exists() and cpu == '0': ++ if not CpuList.online_file_exists() and cpu == '0': + newlist.append(cpu) +- elif self.is_online(int(cpu)): ++ elif CpuList.is_online(int(cpu)): + newlist.append(cpu) + return newlist + +-- +2.43.0 + diff --git a/rteval-Refactor-collapse_cpulist-in-systopology.patch b/rteval-Refactor-collapse_cpulist-in-systopology.patch new file mode 100644 index 0000000..cb43c85 --- /dev/null +++ b/rteval-Refactor-collapse_cpulist-in-systopology.patch @@ -0,0 +1,225 @@ +From 4b4f50900e38a931ed9585c6cf200b74c0120a20 Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Wed, 29 Nov 2023 10:34:54 +0100 +Subject: [PATCH 1/9] rteval: Refactor collapse_cpulist in systopology + +Instead of having duplicate code in two functions, one top-level and +one member function of CpuList, have only one static function in +CpuList. + +Additionally re-write the implementation to use a more straight forward +one-pass algorithm. + +Signed-off-by: Tomas Glozar +Signed-off-by: John Kacur +--- + rteval-cmd | 4 +- + rteval/modules/loads/__init__.py | 4 +- + rteval/modules/measurement/__init__.py | 4 +- + rteval/modules/measurement/cyclictest.py | 6 +-- + rteval/systopology.py | 68 ++++++++---------------- + 5 files changed, 30 insertions(+), 56 deletions(-) + +diff --git a/rteval-cmd b/rteval-cmd +index 6f613a3..7c41429 100755 +--- a/rteval-cmd ++++ b/rteval-cmd +@@ -30,7 +30,7 @@ from rteval import RtEval, rtevalConfig + from rteval.modules.loads import LoadModules + from rteval.modules.measurement import MeasurementModules + from rteval.version import RTEVAL_VERSION +-from rteval.systopology import CpuList, SysTopology, collapse_cpulist ++from rteval.systopology import CpuList, SysTopology + from rteval.modules.loads.kcompile import ModuleParameters + + compress_cpulist = CpuList.compress_cpulist +@@ -211,7 +211,7 @@ 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) ++ return CpuList.collapse_cpulist(tmplist) + + + if __name__ == '__main__': +diff --git a/rteval/modules/loads/__init__.py b/rteval/modules/loads/__init__.py +index aca0c9f..13fba1e 100644 +--- a/rteval/modules/loads/__init__.py ++++ b/rteval/modules/loads/__init__.py +@@ -11,7 +11,7 @@ import libxml2 + from rteval.Log import Log + from rteval.rtevalConfig import rtevalCfgSection + from rteval.modules import RtEvalModules, rtevalModulePrototype +-from rteval.systopology import collapse_cpulist, CpuList, SysTopology as SysTop ++from rteval.systopology import CpuList, SysTopology as SysTop + + class LoadThread(rtevalModulePrototype): + def __init__(self, name, config, logger=None): +@@ -120,7 +120,7 @@ class LoadModules(RtEvalModules): + cpulist = CpuList(cpulist).cpulist + else: + cpulist = SysTop().default_cpus() +- rep_n.newProp("loadcpus", collapse_cpulist(cpulist)) ++ rep_n.newProp("loadcpus", CpuList.collapse_cpulist(cpulist)) + + return rep_n + +diff --git a/rteval/modules/measurement/__init__.py b/rteval/modules/measurement/__init__.py +index 2a0556b..41b8022 100644 +--- a/rteval/modules/measurement/__init__.py ++++ b/rteval/modules/measurement/__init__.py +@@ -5,7 +5,7 @@ + + import libxml2 + from rteval.modules import RtEvalModules, ModuleContainer +-from rteval.systopology import collapse_cpulist, CpuList, SysTopology as SysTop ++from rteval.systopology import CpuList, SysTopology as SysTop + + class MeasurementProfile(RtEvalModules): + """Keeps and controls all the measurement modules with the same measurement profile""" +@@ -187,7 +187,7 @@ measurement profiles, based on their characteristics""" + cpulist = CpuList(cpulist).cpulist + else: + cpulist = SysTop().online_cpus() if run_on_isolcpus else SysTop().default_cpus() +- rep_n.newProp("measurecpus", collapse_cpulist(cpulist)) ++ rep_n.newProp("measurecpus", CpuList.collapse_cpulist(cpulist)) + + for mp in self.__measureprofiles: + mprep_n = mp.MakeReport() +diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py +index 0af1d31..1b14e7e 100644 +--- a/rteval/modules/measurement/cyclictest.py ++++ b/rteval/modules/measurement/cyclictest.py +@@ -17,7 +17,7 @@ import libxml2 + from rteval.Log import Log + from rteval.modules import rtevalModulePrototype + from rteval.systopology import cpuinfo +-from rteval.systopology import CpuList, SysTopology, collapse_cpulist ++from rteval.systopology import CpuList, SysTopology + + expand_cpulist = CpuList.expand_cpulist + +@@ -203,7 +203,7 @@ class Cyclictest(rtevalModulePrototype): + # Only include online cpus + self.__cpus = CpuList(self.__cpus).cpulist + # Reset cpulist from the newly calculated self.__cpus +- self.__cpulist = collapse_cpulist(self.__cpus) ++ self.__cpulist = CpuList.collapse_cpulist(self.__cpus) + self.__cpus = [str(c) for c in self.__cpus] + self.__sparse = True + if self.__run_on_isolcpus: +@@ -220,7 +220,7 @@ class Cyclictest(rtevalModulePrototype): + self.__cpus = [c for c in self.__cpus if c in cpuset or self.__run_on_isolcpus and c in isolcpus] + if self.__run_on_isolcpus: + self.__sparse = True +- self.__cpulist = collapse_cpulist(self.__cpus) ++ self.__cpulist = CpuList.collapse_cpulist([int(c) for c in self.__cpus]) + + # Sort the list of cpus to align with the order reported by cyclictest + self.__cpus.sort(key=int) +diff --git a/rteval/systopology.py b/rteval/systopology.py +index 62ad355..ea8e242 100644 +--- a/rteval/systopology.py ++++ b/rteval/systopology.py +@@ -10,25 +10,6 @@ import os + import os.path + import glob + +-# Utility version of collapse_cpulist that doesn't require a CpuList object +-def collapse_cpulist(cpulist): +- """ Collapse a list of cpu numbers into a string range +- of cpus (e.g. 0-5, 7, 9) """ +- if len(cpulist) == 0: +- return "" +- idx = CpuList.longest_sequence(cpulist) +- if idx == 0: +- seq = str(cpulist[0]) +- else: +- if idx == 1: +- seq = f"{cpulist[0]},{cpulist[idx]}" +- else: +- seq = f"{cpulist[0]}-{cpulist[idx]}" +- +- rest = collapse_cpulist(cpulist[idx+1:]) +- if rest == "": +- return seq +- return ",".join((seq, rest)) + + def sysread(path, obj): + """ Helper function for reading system files """ +@@ -93,7 +74,7 @@ class CpuList: + self.cpulist.sort() + + def __str__(self): +- return self.__collapse_cpulist(self.cpulist) ++ return self.collapse_cpulist(self.cpulist) + + def __contains__(self, cpu): + return cpu in self.cpulist +@@ -114,35 +95,28 @@ class CpuList: + return os.path.exists(os.path.join(CpuList.cpupath, "isolated")) + + @staticmethod +- def longest_sequence(cpulist): +- """ return index of last element of a sequence that steps by one """ +- lim = len(cpulist) +- for idx, _ in enumerate(cpulist): +- if idx+1 == lim: +- break +- if int(cpulist[idx+1]) != (int(cpulist[idx])+1): +- return idx +- return lim - 1 +- +- def __collapse_cpulist(self, cpulist): +- """ Collapse a list of cpu numbers into a string range ++ def collapse_cpulist(cpulist): ++ """ ++ Collapse a list of cpu numbers into a string range + of cpus (e.g. 0-5, 7, 9) + """ +- if len(cpulist) == 0: +- return "" +- idx = self.longest_sequence(cpulist) +- if idx == 0: +- seq = str(cpulist[0]) +- else: +- if idx == 1: +- seq = f"{cpulist[0]},{cpulist[idx]}" ++ cur_range = [None, None] ++ result = [] ++ for cpu in cpulist + [None]: ++ if cur_range[0] is None: ++ cur_range[0] = cur_range[1] = cpu ++ continue ++ if cpu is not None and cpu == cur_range[1] + 1: ++ # Extend currently processed range ++ cur_range[1] += 1 + else: +- seq = f"{cpulist[0]}-{cpulist[idx]}" +- +- rest = self.__collapse_cpulist(cpulist[idx+1:]) +- if rest == "": +- return seq +- return ",".join((seq, rest)) ++ # Range processing finished, add range to string ++ result.append(f"{cur_range[0]}-{cur_range[1]}" ++ if cur_range[0] != cur_range[1] ++ else str(cur_range[0])) ++ # Reset ++ cur_range[0] = cur_range[1] = cpu ++ return ",".join(result) + + @staticmethod + def compress_cpulist(cpulist): +@@ -428,7 +402,7 @@ if __name__ == "__main__": + + onlcpus = s.online_cpus() + print(f'onlcpus = {onlcpus}') +- onlcpus = collapse_cpulist(onlcpus) ++ onlcpus = CpuList.collapse_cpulist(onlcpus) + print(f'onlcpus = {onlcpus}') + + onlcpus_str = s.online_cpus_str() +-- +2.43.0 + diff --git a/rteval-Remove-upstream-spec-file.patch b/rteval-Remove-upstream-spec-file.patch new file mode 100644 index 0000000..d35f4cc --- /dev/null +++ b/rteval-Remove-upstream-spec-file.patch @@ -0,0 +1,566 @@ +From de8e25ff3a30dbdbba6fb1b68ea0921dff55cd91 Mon Sep 17 00:00:00 2001 +From: John Kacur +Date: Mon, 13 Nov 2023 14:32:19 -0500 +Subject: [PATCH] rteval: Remove upstream spec file + +Specfiles should be maintained by distributions and not in the upstream +code. In practice they are not maintained upstream except for version +numbers, so just remove the specfile. + +This also moves a lot of functionality around rpms in the Makefile, but +this functionality exists in tools such as rpmbuild and friends anyway. + +Signed-off-by: John Kacur +--- + Makefile | 37 ---- + rteval.spec | 484 ---------------------------------------------------- + 2 files changed, 521 deletions(-) + delete mode 100644 rteval.spec + +diff --git a/Makefile b/Makefile +index 14f74e087eff..ee4cca555b95 100644 +--- a/Makefile ++++ b/Makefile +@@ -79,49 +79,12 @@ rpm_prep: + + rpms rpm: rpm_prep rtevalrpm loadrpm + +-rtevalrpm: rteval-$(VERSION).tar.bz2 +- cp $^ rpm/SOURCES +- cp rteval.spec rpm/SPECS +- rpmbuild -ba --define "_topdir $(HERE)/rpm" rpm/SPECS/rteval.spec +- +-rtevalsrpm: rteval-$(VERSION).tar.bz2 +- cp $^ rpm/SOURCES +- cp rteval.spec rpm/SPECS +- rpmbuild -bs --define "_topdir $(HERE)/rpm" rpm/SPECS/rteval.spec +- +- +-xmlrpcrpm: rteval-xmlrpc-$(XMLRPCVER).tar.gz +- cp rteval-xmlrpc-$(XMLRPCVER).tar.gz rpm/SOURCES/ +- cp server/rteval-parser.spec rpm/SPECS/ +- rpmbuild -ba --define "_topdir $(HERE)/rpm" rpm/SPECS/rteval-parser.spec +- +-xmlsrpm: rteval-xmlrpc-$(XMLRPCVER).tar.gz +- cp rteval-xmlrpc-$(XMLRPCVER).tar.gz rpm/SOURCES/ +- cp server/rteval-parser.spec rpm/SPECS/ +- rpmbuild -bs --define "_topdir $(HERE)/rpm" rpm/SPECS/rteval-parser.spec +- +-loadrpm: +- rm -rf rpm-loads +- mkdir -p rpm-loads/{BUILD,RPMS,SRPMS,SOURCES,SPECS} +- cp rteval-loads.spec rpm-loads/SPECS +- cp $(LOADS) rpm-loads/SOURCES +- rpmbuild -ba --define "_topdir $(HERE)/rpm-loads" rpm-loads/SPECS/rteval-loads.spec +- +-rpmlint: rpms +- @echo "===============" +- @echo "running rpmlint" +- rpmlint -v $(shell find ./rpm -type f -name "*.rpm") \ +- $(shell find ./rpm-loads -type f -name "*.rpm") \ +- $(shell find ./rpm/SPECS -type f -name "rteval*.spec") \ +- $(shell find ./rpm-loads/SPECS -type f -name "rteval*.spec" ) + + help: + @echo "" + @echo "rteval Makefile targets:" + @echo "" + @echo " runit: do a short testrun locally [default]" +- @echo " rpm: run rpmbuild for all rpms" +- @echo " rpmlint: run rpmlint against all rpms/srpms/specfiles" + @echo " tarfile: create the source tarball" + @echo " install: install rteval locally" + @echo " clean: cleanup generated files" +diff --git a/rteval.spec b/rteval.spec +deleted file mode 100644 +index b5842f0d8206..000000000000 +--- a/rteval.spec ++++ /dev/null +@@ -1,484 +0,0 @@ +-%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} +-%{!?python_ver: %define python_ver %(%{__python} -c "import sys ; print sys.version[:3]")} +- +-Name: rteval +-Version: 3.7 +-Release: 1%{?dist} +-Summary: Utility to evaluate system suitability for RT Linux +- +-Group: Development/Tools +-License: GPLv2 +-URL: http://git.kernel.org/?p=linux/kernel/git/clrkwllms/rteval.git +-Source0: rteval-%{version}.tar.bz2 +-BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +- +-BuildRequires: python3-devel +-Requires: platform-python +-Requires: python3-ethtool python3-lxml +-Requires: python3-dmidecode >= 3.10 +-Requires: rt-tests >= 0.97 +-Requires: rteval-loads >= 1.4 +-Requires: rteval-common => %{version}-%{release} +-Requires: sysstat +-Requires: bzip2 +-Requires: kernel-headers +-Requires: sos +-BuildArch: noarch +-Obsoletes: rteval <= 1.7 +-Requires: numactl +- +-%description +-The rteval script is a utility for measuring various aspects of +-realtime behavior on a system under load. The script unpacks the +-kernel source, and then goes into a loop, running hackbench and +-compiling a kernel tree. During that loop the cyclictest program +-is run to measure event response time. After the run time completes, +-a statistical analysis of the event response times is done and printed +-to the screen. +- +- +-%package common +-Summary: Common rteval files +-BuildArch: noarch +- +-%description common +-Common files used by rteval, rteval-xmlrpc and rteval-parser +- +-%prep +-%setup -q +- +-# version sanity check (make sure specfile and rteval.py match) +-cp rteval/version.py rtevalversion.py +-srcver=$(%{__python} -c "from rtevalversion import RTEVAL_VERSION; print RTEVAL_VERSION") +-rm -rf rtevalversion.py +-if [ $srcver != %{version} ]; then +- printf "\n***\n*** rteval spec file version do not match the rteval/rteval.py version\n***\n\n" +- exit -1 +-fi +- +-%build +-%{__python} setup.py build +- +-%install +-%{__python} setup.py install --root=$RPM_BUILD_ROOT +- +-%clean +-rm -rf $RPM_BUILD_ROOT +- +-%files common +-%doc COPYING +-%dir %{_datadir}/%{name} +-%{python_sitelib}/rteval/rtevalclient.py* +-%{python_sitelib}/rteval/rtevalConfig.py* +-%{python_sitelib}/rteval/rtevalXMLRPC.py* +-%{python_sitelib}/rteval/version.py* +-%{python_sitelib}/rteval/Log.py* +-%{python_sitelib}/rteval/misc.py* +-%{python_sitelib}/rteval/systopology.py* +- +-%files +-%defattr(-,root,root,-) +-%if "%{python_ver}" >= "2.5" +-%{python_sitelib}/*.egg-info +-%endif +- +-%doc COPYING README doc/rteval.txt +-%{_mandir}/man8/rteval.8.gz +-%config(noreplace) %{_sysconfdir}/rteval.conf +-%{_datadir}/%{name}/rteval_*.xsl +-%{python_sitelib}/rteval/__init__.py* +-%{python_sitelib}/rteval/rtevalMailer.py* +-%{python_sitelib}/rteval/rtevalReport.py* +-%{python_sitelib}/rteval/xmlout.py* +-%{python_sitelib}/rteval/modules +-%{python_sitelib}/rteval/sysinfo +-/usr/bin/rteval +- +-%changelog +-* Thu Mar 16 2017 Clark Williams - 2.14-1 +-- removed leftover import of systopology from sysinfo +- +-* Wed Mar 15 2017 Clark Williams - 2.13-2 +-- Updated specfile to correct version and bz [1382155] +- +-* Tue Sep 20 2016 Clark Williams - 2.12-1 +-- handle empty environment variables SUDO_USER and USER [1312057] +- +-* Tue Aug 30 2016 Clark Williams - 2.11-1 +-- make sure we return non-zero for early exit from tests +- +-* Wed Aug 3 2016 Clark Williams - 2.10-1 +-- bumped version for RHEL 7.3 release +- +-* Mon May 9 2016 Clark Williams - 2.9.1 +-- default cpulist for modules if only one specified [1333831] +- +-* Tue Apr 26 2016 Clark Williams - 2.8.1 +-- add the --version option to print the rteval version +-- made the --cyclictest-breaktrace option work properly [1209986] +- +-* Fri Apr 1 2016 Clark Williams - 2.7.1 +-- treat SIGINT and SIGTERM as valid end-of-run events [1278757] +-- added cpulist options to man page +- +-* Thu Feb 11 2016 Clark Williams - 2.6.1 +-- update to make --loads-cpulist and --measurement-cpulist work [1306437] +- +-* Thu Dec 10 2015 Clark Williams - 2.5-1 +-- stop using old numactl --cpubind argument +- +-* Wed Dec 9 2015 Clark Williams - 2.4.2 +-- added Require of package numactl +- +-* Tue Nov 17 2015 Clark Williams - 2.4.1 +-- rework hackbench load to not generate cross-node traffic [1282826] +- +-* Wed Aug 12 2015 Clark Williams - 2.3-1 +-- comment out HWLatDetect module from default config [1245699] +- +-* Wed Jun 10 2015 Clark Williams - 2.2-1 +-- add --loads-cpulist and --measurement-cpulist to allow cpu placement [1230401] +- +-* Thu Apr 23 2015 Luis Claudio R. Goncalves - 2.1-8 +-- load default configs when no config file is specified (Jiri kastner) [1212452] +- +-* Wed Jan 14 2015 Clark Williams - 2.1-7 +-- added requires of bzip2 to specfile [1151567] +- +-* Thu Jan 8 2015 Clark Williams - 2.1-6 +-- cleaned up product documentation [1173315] +- +-* Mon Nov 10 2014 Luis Claudio R. Goncalves - 2.1-5 +-- rebuild for RHEL-7.1 (1151567) +- +-* Thu Mar 27 2014 Clark Williams - 2.1-4 +-- cherry-picked old commit to deal with installdir problem +- +-* Wed Mar 26 2014 Clark Williams - 2.1-3 +-- added sysstat requires to specfile +- +-* Tue Mar 12 2013 David Sommerseth - 2.1-2 +-- Migrated from libxslt-python to python-lxml +- +-* Fri Jan 18 2013 David Sommerseth - 2.1-1 +-- Made some log lines clearer +-- cyclictest: Added --cyclictest-breaktrace feature +-- cyclictest: Removed --cyclictest-distance option +-- cyclictest: Use a tempfile buffer for cyclictest's stdout data +-- cyclictest: Report if breaktrace was triggered +-- cyclictest: Make the unit test work again +-- cyclictest: Only log and show statistic data when samples are collected +-- Copyright updates +- +-* Thu Jan 17 2013 David Sommerseth - 2.0.1-1 +-- Fix up type casting in the core module code +-- hwlatdetect: Add some more useful debug info +-- Reworked the run logic for modules - allow them to flag they won't run +-- Fixed a few log messages in load modules +-- Add a 30 seconds sleep before unleashing the measurement threads +- +-* Thu Jan 10 2013 David Sommerseth - 2.0-3 +-- Separate out RTEVAL_VERSION into rteval.version, to avoid +- massive BuildRequirements +- +-* Fri Dec 21 2012 David Sommerseth - 2.0-2 +-- Split out common files into rteval-common +- +-* Fri Dec 21 2012 David Sommerseth - 2.0-1 +-- Updated to rteval v2.0 and reworked spec file to use setup.py directly +- +-* Tue Oct 23 2012 Clark Williams - 1.36-1 +-- deal with system not having dmidecode python module +-- make sure to cast priority parameter to int +-- from Raphaƫl Beamonte : +- - Rewrite of the get_kthreads method to make it cross-distribution +- - Adds getcmdpath method to use which to locate the used commands +- - Rewrite of the get_services method to make it cross-distribution +- +-* Mon Apr 2 2012 Clark Williams - 1.35-1 +-- fix thinko where SIGINT and SIGTERM handlers were commented out +- +-* Thu Jan 12 2012 Clark Williams - 1.34-1 +-- fix missing config merge in rteval.py to pass parameters +- down to cyclictest +-- modify hackbench to use helper function to start process +- +-* Sat May 14 2011 Clark Williams - 1.33-1 +-- modify hackbench cutoff to be 0.75GB/core +- +-* Mon Aug 23 2010 Clark Williams - 1.32-1 +-- update docs +-- refactor some RTEval methods to utility functions +-- modify hackbench.py not to run under low memory conditions +-- clean up XML generation to deal with new hackbench code +-- clean up XSL code to deal with new XML 'run' attribute +-- from David Sommerseth : +- - improve CPU socket counting logic +- - delay log directory creation until actually needed +-- from Gowrishankar : +- - check if the core id really exists (multithreading fix) +- +-* Mon Jul 26 2010 Clark Williams - 1.31-1 +-- from David Sommerseth : +- - Updated hackbench implementation to avoid overusing resources +- - Don't show NUMA node information if it's missing in the summary.xml +- - Show CPU cores properly +- +-* Wed Jul 21 2010 Clark Williams - 1.30-1 +-- added code to hackbench to try to detect and ease memory pressure +- +-* Fri Jul 16 2010 Clark Williams - 1.29-1 +-- fixed incorrect type value in kcompile.py +- +-* Fri Jul 16 2010 Clark Williams - 1.28-1 +-- added logic to loads to adjust number of jobs based on ratio +- of memory per core +- +-* Wed Jul 14 2010 Clark Williams - 1.27-1 +-- modified hackbench to go back to using threads rather than +- processes for units of work +-- added memory size, number of numa nodes and run duration to the +- parameter dictionary passed to all loads and cyclictest +- +-* Tue Jul 13 2010 Clark Williams - 1.26-1 +-- modified hackbench parameters to reduce memory consumption +- +-* Mon Jul 12 2010 Clark Williams - 1.25-1 +-- fixed cyclictest bug that caused everything to be uniprocessor +-- updated source copyrights to 2010 +- +-* Fri Jul 9 2010 Clark Williams - 1.24-1 +-- modified hackbench arguments and added new parameters for +- hackbench in rteval.conf +- +-* Thu Jul 8 2010 Clark Williams - 1.23-1 +-- version bump to deal with out-of-sync cvs issue +- +-* Thu Jul 8 2010 Clark Williams - 1.22-1 +-- merged David Sommerseth changes to use +- hackbench from rt-tests packages rather than carry local copy +-- converted all loads and cyclictest to pass __init__ parameters +- in a dictionary rather than as discrete parameters +-- added logging for load output +- +-* Tue Apr 13 2010 Clark Williams - 1.21-1 +-- from Luis Claudio Goncalves : +- - remove unecessary wait() call in cyclictest.py +- - close /dev/null after using it +- - call subprocess.wait() when needed +- - remove delayloop code in hackbench.py +-- from David Sommerseth : +- - add SIGINT handler +- - handle non-root user case +- - process DMI warnings before command line arguments +- - added --annotate feature to rteval +- - updates to xmlrpc code +- +-* Tue Apr 6 2010 Clark Williams - 1.20-1 +-- code fixes from Luis Claudio Goncalves +-- from David Sommerseth : +- - xmlrpc server updates +- - cputopology.py for recording topology in xml +- - added NUMA node recording for run data +- - rpmlint fixes +-- added start of rteval whitepaper in docs dir +- +-* Tue Mar 16 2010 Clark Williams - 1.19-1 +-- add ability for --summarize to read tarfiles +-- from David Sommerseth +- - gather info about loaded kernel modules for XML file +- - added child tracking to hackbench to prevent zombies +- +-* Tue Feb 16 2010 Clark Williams - 1.18-1 +-- fix usage of python 2.6 features on RHEL5 (python 2.4) +- +-* Tue Feb 16 2010 Clark Williams - 1.17-1 +-- added logic to filter non-printables from service status output +- so that we have legal XML output +-- added logic to hackbench.py to cleanup properly at the end +- of the test +- +-* Thu Feb 11 2010 Clark Williams - 1.16-1 +-- fix errors in show_remaining_time() introduced because +- time values are floats rather than ints +- +-* Thu Feb 11 2010 Clark Williams - 1.15-1 +-- added logic to use --numa and --smp options of new cyclictest +-- added countdown report for time remaining in a run +- +-* Tue Feb 9 2010 Clark Williams - 1.14-1 +-- David Sommerseth : +- merged XMLReport() changes for hwcert suite +- +-* Tue Dec 22 2009 Clark Williams - 1.13-1 +-- added cyclictest default initializers +-- added sanity checks to statistics reduction code +-- updated release checklist to include origin push +-- updated Makefile clean and help targets +-- davids updates (mainly for v7 integration): +- - Add explicit sys.path directory to the python sitelib+ +- '/rteval' +- - Send program arguments via RtEval() constructor +- - Added more DMI data into the summary.xml report +- - Fixed issue with not including all devices in the +- OnBoardDeviceInfo tag +- +-* Thu Dec 3 2009 David Sommerseth - 1.12-2 +-- fixed Makefile and specfile to include and install the +- rteval/rteval_histogram_raw.py source file for gaining +- raw access to histogram data +-- Removed xmlrpc package during merge against master_ipv4 branch +- +-* Wed Nov 25 2009 Clark Williams - 1.12-1 +-- fix incorrect reporting of measurement thread priorities +- +-* Mon Nov 16 2009 Clark Williams - 1.11-5 +-- ensure that no double-slashes ("//") appear in the symlink +- path for /usr/bin/rteval (problem with rpmdiff) +- +-* Tue Nov 10 2009 Clark Williams - 1.11-4 +-- changed symlink back to install and tracked by %%files +- +-* Mon Nov 9 2009 Clark Williams - 1.11-3 +-- changed symlink generation from %%post to %%posttrans +- +-* Mon Nov 9 2009 Clark Williams - 1.11-2 +-- fixed incorrect dependency for libxslt +- +-* Fri Nov 6 2009 Clark Williams - 1.11-1 +-- added base OS info to XML file and XSL report +-- created new package rteval-loads for the load source code +- +-* Wed Nov 4 2009 Clark Williams - 1.10-1 +-- added config file section for cyclictest and two settable +- parameters, buckets and interval +- +-* Thu Oct 29 2009 Clark Williams - 1.9-1 +-- merged davids updates: +- -H option (raw histogram data) +- cleaned up xsl files +- fixed cpu sorting +- +-* Mon Oct 26 2009 David Sommerseth - 1.8-3 +-- Fixed rpmlint complaints +- +-* Mon Oct 26 2009 David Sommerseth - 1.8-2 +-- Added xmlrpc package, containing the XML-RPC mod_python modules +- +-* Tue Oct 20 2009 Clark Williams - 1.8-1 +-- split kcompile and hackbench into sub-packages +-- reworked Makefile (and specfile) install/uninstall logic +-- fixed sysreport incorrect plugin option +-- catch failure when running on root-squashed NFS +- +-* Tue Oct 13 2009 Clark Williams - 1.7-1 +-- added kthread status to xml file +-- merged davids changes for option processing and additions +- to xml summary +- +-* Tue Oct 13 2009 Clark Williams - 1.6-1 +-- changed stat calculation to loop less +-- added methods to grab service and kthread status +- +-* Mon Oct 12 2009 Clark Williams - 1.5-1 +-- changed cyclictest to use less memory when doing statisics +- calculations +-- updated debug output to use module name prefixes +-- changed option processing to only process config file once +- +-* Fri Oct 9 2009 Clark Williams - 1.4-1 +-- changed cyclictest to use histogram rather than sample array +-- calcuated statistics directly from histogram +-- changed sample interval to 100us +-- added -a (affinity) argument to force cpu affinity for +- measurement threads +- +-* Thu Sep 24 2009 David Sommerseth - 1.3-3 +-- Cleaned up the spec file and made rpmlint happy +- +-* Wed Sep 23 2009 David Sommerseth - 1.3-2 +-- Removed version number from /usr/share/rteval path +- +-* Tue Sep 22 2009 Clark Williams - 1.3-1 +-- changes from davids: +- * changed report code to sort by processor id +- * added report submission retry logic +- * added emailer class +- +-* Fri Sep 18 2009 Clark Williams - 1.2-1 +-- added config file handling for modifying load behavior and +- setting defaults +-- added units in report per IBM request +- +-* Wed Aug 26 2009 Clark Williams - 1.1-2 +-- missed a version change in rteval/rteval.py +- +-* Wed Aug 26 2009 Clark Williams - 1.1-1 +-- modified cyclictest.py to start cyclictest threads with a +- 'distance' of zero, meaning they all have the same measurement +- interval +- +-* Tue Aug 25 2009 Clark Williams - 1.0-1 +-- merged davids XMLRPC fixes +-- fixed --workdir option +-- verion bump to 1.0 +- +-* Thu Aug 13 2009 Clark Williams - 0.9-2 +-- fixed problem with incorrect version in rteval.py +- +-* Tue Aug 4 2009 Clark Williams - 0.9-1 +-- merged dsommers XMLRPC and database changes +-- Specify minimum python-dmidecode version, which got native XML support +-- Added rteval_dmi.xsl +-- Fixed permission issues in /usr/share/rteval-x.xx +- +-* Wed Jul 22 2009 Clark Williams - 0.8-1 +-- added code to capture clocksource info +-- added code to copy dmesg info to report directory +-- added code to display clocksource info in report +-- added --summarize option to display summary of existing report +-- added helpfile target to Makefile +- +-* Thu Mar 26 2009 Clark Williams - 0.7-1 +-- added require for python-schedutils to specfile +-- added default for cyclictest output file +-- added help parameter to option parser data +-- renamed xml output file to summary.xml +-- added routine to create tarfile of result files +- +-* Wed Mar 18 2009 Clark Williams - 0.6-6 +-- added code to handle binary data coming from DMI tables +- +-* Wed Mar 18 2009 Clark Williams - 0.6-5 +-- fixed logic for locating XSL template (williams) +-- fixed another stupid typo in specfile (williams) +- +-* Wed Mar 18 2009 Clark Williams - 0.6-4 +-- fixed specfile to install rteval_text.xsl in /usr/share directory +- +-* Wed Mar 18 2009 Clark Williams - 0.6-3 +-- added Requires for libxslt-python (williams) +-- fixed race condition in xmlout constructor/destructor (williams) +- +-* Wed Mar 18 2009 Clark Williams - 0.6-2 +-- added Requires for libxslt (williams) +-- fixed stupid typo in rteval/rteval.py (williams) +- +-* Wed Mar 18 2009 Clark Williams - 0.6-1 +-- added xml output logic (williams, dsommers) +-- added xlst template for report generator (dsommers) +-- added dmi/smbios output to report (williams) +-- added __del__ method to hackbench to cleanup after run (williams) +-- modified to always keep run data (williams) +- +-* Fri Feb 20 2009 Clark Williams - 0.5-1 +-- fixed tab/space mix problem +-- added report path line to report +- +-* Fri Feb 20 2009 Clark Williams - 0.4-1 +-- reworked report output +-- handle keyboard interrupt better +-- removed duration mismatch between rteval and cyclictest +- +-* Mon Feb 2 2009 Clark Williams - 0.3-1 +-- initial checkin +-- +2.41.0 + diff --git a/rteval.spec b/rteval.spec index c2a7462..96a52c1 100644 --- a/rteval.spec +++ b/rteval.spec @@ -1,6 +1,6 @@ Name: rteval Version: 3.7 -Release: 3%{?dist} +Release: 4%{?dist} Summary: Utility to evaluate system suitability for RT Linux Group: Development/Tools @@ -13,7 +13,6 @@ BuildRequires: python3-devel BuildRequires: python3-setuptools Requires: python3-lxml Requires: python3-libxml2 -Requires: python3-dmidecode >= 3.10 Requires: realtime-tests Requires: rteval-loads >= 1.6-5 Requires: sysstat @@ -32,8 +31,15 @@ Requires: dwarves BuildArch: noarch # Patches -Patch1: 0001-rteval-Change-the-default-kernel-for-kcompile-to-lin.patch -Patch2: 0002-rteval-Remove-upstream-spec-file.patch +Patch1: rteval-Change-the-default-kernel-for-kcompile.patch +Patch2: rteval-Remove-upstream-spec-file.patch +Patch3: rteval-Makefile-More-rpm-cleanups.patch +Patch4: rteval-Disable-use-of-python-dmidecode.patch +Patch5: rteval-Refactor-collapse_cpulist-in-systopology.patch +Patch6: rteval-Minor-improvements-to-CpuList-class.patch +Patch7: rteval-Convert-CpuList-class-to-a-module.patch +Patch8: rteval-Add-relative-cpulists-for-measurements.patch + %description The rteval script is a utility for measuring various aspects of @@ -48,6 +54,12 @@ to the screen. %setup -q %patch 1 -p1 %patch 2 -p1 +%patch 3 -p1 +%patch 4 -p1 +%patch 5 -p1 +%patch 6 -p1 +%patch 7 -p1 +%patch 8 -p1 %build %{__python3} setup.py build @@ -68,6 +80,18 @@ to the screen. %{_bindir}/rteval %changelog +* Fri Mar 22 2024 John Kacur - 3.7-4 +- Remove python3-dmidecode as a Requires +- Add relative cpulists for measurements +- Convert CpuList class to a module +- Disable use of python-dmidecode +- Makefile: More rpm cleanups +- systopology: Fix incorrect test to invert a cpulist +- Minor improvements to CpuList class +- Refactor collapse_cpulist in systopology +- Added the tests dir +Resolves: RHEL-30162 + * Fri Jan 26 2024 Fedora Release Engineering - 3.7-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh new file mode 100644 index 0000000..9a2c9df --- /dev/null +++ b/tests/scripts/run_tests.sh @@ -0,0 +1,20 @@ +#!/usr/bin/bash + +# make sure we have rteval installed +if rpm -q --quiet rteval; then + : +else + sudo dnf install -y rteval + if [[ $? != 0 ]]; then + echo "install of rteval failed!" + exit 1 + fi +fi + +rteval --help + +if [[ $? != 0 ]]; then + exit 2 +fi + +exit 0 diff --git a/tests/tests.yml b/tests/tests.yml new file mode 100644 index 0000000..f2e1e05 --- /dev/null +++ b/tests/tests.yml @@ -0,0 +1,16 @@ +- hosts: localhost + roles: + - role: standard-test-basic + tags: + - classic + tests: + - simple: + dir: scripts + run: ./run_tests.sh + - short_test: + dir: . + run: rteval --duration=2m + timeout: 10m + required_packages: + - make + - rteval