Add timerlat as a measurement module
Add timerlat as a measurement module Resolves: RHEL-28058 Signed-off-by: John Kacur <jkacur@redhat.com>
This commit is contained in:
parent
d83dcd1eee
commit
3db03791e8
@ -1,6 +1,6 @@
|
||||
--- !Policy
|
||||
product_versions:
|
||||
- rhel-9
|
||||
- rhel-10
|
||||
decision_context: osci_compose_gate
|
||||
rules:
|
||||
- !PassingTestCaseRule {test_case_name: osci.brew-build.tier0.functional}
|
||||
|
70
rteval-Add-relative-cpulists-for-loads.patch
Normal file
70
rteval-Add-relative-cpulists-for-loads.patch
Normal file
@ -0,0 +1,70 @@
|
||||
From 636701e66cb98b979948b7a47320809c734e2a9e Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Glozar <tglozar@redhat.com>
|
||||
Date: Thu, 4 Apr 2024 10:14:38 +0200
|
||||
Subject: [PATCH] rteval: Add relative cpulists for loads
|
||||
|
||||
Relative cpulists were added for measurements in 64ce7848 ("rteval: Add
|
||||
relative cpulists for measurements"). It was observed since that this
|
||||
feature would also be useful for load cpulists, for example when the
|
||||
measurements are performed externally and rteval is only used to run
|
||||
loads.
|
||||
|
||||
Add support for relative cpulists also for loads. This works the same
|
||||
way as for measurements using parse_cpulist_from_config, only difference
|
||||
is there is no --loads-run-on-isolcpus option. That is, --loads-cpulist
|
||||
now also takes lists with addition (+) and removal (-) of CPUs against
|
||||
the default list, e.g. +0,1,-7,8.
|
||||
|
||||
Signed-off-by: Tomas Glozar <tglozar@redhat.com>
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval-cmd | 17 ++++++++++-------
|
||||
1 file changed, 10 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/rteval-cmd b/rteval-cmd
|
||||
index ed13af3..c72bc61 100755
|
||||
--- a/rteval-cmd
|
||||
+++ b/rteval-cmd
|
||||
@@ -331,29 +331,32 @@ if __name__ == '__main__':
|
||||
|
||||
ldcfg = config.GetSection('loads')
|
||||
msrcfg = config.GetSection('measurement')
|
||||
+ # Remember if cpulists were explicitly set by the user before running
|
||||
+ # parse_cpulist_from_config, which generates default value for them
|
||||
msrcfg_cpulist_present = msrcfg.cpulist != ""
|
||||
- # Parse measurement cpulist using parse_cpulist_from_config to account for run-on-isolcpus
|
||||
- # and relative cpusets
|
||||
+ ldcfg_cpulist_present = ldcfg.cpulist != ""
|
||||
+ # Parse cpulists 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)
|
||||
+ cpulist = parse_cpulist_from_config(ldcfg.cpulist)
|
||||
+ ldcfg.cpulist = collapse_cpulist(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_present:
|
||||
+ if not ldcfg_cpulist_present and msrcfg_cpulist_present:
|
||||
tmplist = expand_cpulist(msrcfg.cpulist)
|
||||
tmplist = SysTopology().invert_cpulist(tmplist)
|
||||
tmplist = cpulist_utils.online_cpulist(tmplist)
|
||||
ldcfg.cpulist = collapse_cpulist(tmplist)
|
||||
- if not msrcfg_cpulist_present and ldcfg.cpulist:
|
||||
+ if not msrcfg_cpulist_present and ldcfg_cpulist_present:
|
||||
tmplist = expand_cpulist(ldcfg.cpulist)
|
||||
tmplist = SysTopology().invert_cpulist(tmplist)
|
||||
tmplist = cpulist_utils.online_cpulist(tmplist)
|
||||
msrcfg.cpulist = collapse_cpulist(tmplist)
|
||||
|
||||
- if ldcfg.cpulist:
|
||||
+ if ldcfg_cpulist_present:
|
||||
logger.log(Log.DEBUG, f"loads cpulist: {ldcfg.cpulist}")
|
||||
# if --onlyload is specified msrcfg.cpulist is unused
|
||||
if msrcfg_cpulist_present and not rtevcfg.onlyload:
|
||||
--
|
||||
2.44.0
|
||||
|
189
rteval-Add-rtla-timerlat-as-a-measurement-module.patch
Normal file
189
rteval-Add-rtla-timerlat-as-a-measurement-module.patch
Normal file
@ -0,0 +1,189 @@
|
||||
From 393c95ee69ec98816a26aa0583f6b1cac4acb5e7 Mon Sep 17 00:00:00 2001
|
||||
From: John Kacur <jkacur@redhat.com>
|
||||
Date: Fri, 12 Apr 2024 14:40:37 -0400
|
||||
Subject: [PATCH 02/13] rteval: Add rtla timerlat as a measurement module
|
||||
|
||||
This is the first step to adding timerlat as a measurement module
|
||||
With this change you can run timerlat as a standalone rteval file like
|
||||
this (as root)
|
||||
|
||||
python rteval/modules/measurement/timerlat.py
|
||||
|
||||
You can also modify your rteval.conf to list timerlat in the
|
||||
[measurement] section, for example like this
|
||||
|
||||
[measurement]
|
||||
cyclictest: module
|
||||
timerlat: module
|
||||
|
||||
and then both measurement moduels will be run from rteval, for example
|
||||
|
||||
rteval -D -d5m --measurement-cpulist=1-5
|
||||
|
||||
Will run rteval with Debug info, for 5m and cyclictest and timerlat will
|
||||
run on cpus 1-5 and load modules will run on the other available cpus.
|
||||
|
||||
Currently MakeReport just prints to standard out the same information
|
||||
that timerlat outputs, in otherwords, there is no processing into xml
|
||||
yet. Also, there is no way to invoke tracing at the time, but that will
|
||||
be added soon!
|
||||
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval-cmd | 1 +
|
||||
rteval/modules/measurement/timerlat.py | 131 +++++++++++++++++++++++++
|
||||
2 files changed, 132 insertions(+)
|
||||
create mode 100644 rteval/modules/measurement/timerlat.py
|
||||
|
||||
diff --git a/rteval-cmd b/rteval-cmd
|
||||
index c72bc614ad78..5cb6d7a44523 100755
|
||||
--- a/rteval-cmd
|
||||
+++ b/rteval-cmd
|
||||
@@ -247,6 +247,7 @@ if __name__ == '__main__':
|
||||
if not config.HasSection('measurement'):
|
||||
config.AppendConfig('measurement', {
|
||||
'cyclictest' : 'module',
|
||||
+ 'timerlat' : 'module',
|
||||
'sysstat' : 'module'})
|
||||
|
||||
# Prepare log levels before loading modules, not to have unwanted log messages
|
||||
diff --git a/rteval/modules/measurement/timerlat.py b/rteval/modules/measurement/timerlat.py
|
||||
new file mode 100644
|
||||
index 000000000000..d4e78de8d2a2
|
||||
--- /dev/null
|
||||
+++ b/rteval/modules/measurement/timerlat.py
|
||||
@@ -0,0 +1,131 @@
|
||||
+# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+#
|
||||
+# Copyright 2024 John Kacur <jkacur@redhat.com>
|
||||
+#
|
||||
+""" timerlat.py - objectd to manage rtla timerlat """
|
||||
+import os
|
||||
+import subprocess
|
||||
+import signal
|
||||
+import time
|
||||
+import tempfile
|
||||
+import libxml2
|
||||
+from rteval.Log import Log
|
||||
+from rteval.modules import rtevalModulePrototype
|
||||
+from rteval.systopology import cpuinfo, SysTopology
|
||||
+from rteval.cpulist_utils import expand_cpulist, collapse_cpulist
|
||||
+
|
||||
+class Timerlat(rtevalModulePrototype):
|
||||
+ """ measurement modules for rteval """
|
||||
+ def __init__(self, config, logger=None):
|
||||
+ rtevalModulePrototype.__init__(self, 'measurement', 'timerlat', logger)
|
||||
+
|
||||
+ self.__cfg = config
|
||||
+
|
||||
+ self.__numanodes = int(self.__cfg.setdefault('numanodes', 0))
|
||||
+ self.__priority = int(self.__cfg.setdefault('priority', 95))
|
||||
+
|
||||
+ self.__cpulist = self.__cfg.setdefault('cpulist', "")
|
||||
+ self.__cpus = [str(c) for c in expand_cpulist(self.__cpulist)]
|
||||
+ self.__numcores = len(self.__cpus)
|
||||
+
|
||||
+ self.__timerlat_out = None
|
||||
+ self.__timerlat_err = None
|
||||
+ self.__started = False
|
||||
+ self._log(Log.DEBUG, f"system using {self.__numcores} cpu cores")
|
||||
+
|
||||
+
|
||||
+ def _WorkloadSetup(self):
|
||||
+ self.__timerlat_process = None
|
||||
+
|
||||
+ def _WorkloadBuild(self):
|
||||
+ self._setReady()
|
||||
+
|
||||
+ def _WorkloadPrepare(self):
|
||||
+ self.__cmd = ['rtla', 'timerlat', 'hist', '-P', f'f:{int(self.__priority)}', '-u']
|
||||
+ self.__cmd.append(f'-c{self.__cpulist}')
|
||||
+ self._log(Log.DEBUG, f'self.__cmd = {self.__cmd}')
|
||||
+ self.__timerlat_out = tempfile.SpooledTemporaryFile(mode='w+b')
|
||||
+ self.__timerlat_err = tempfile.SpooledTemporaryFile(mode='w+b')
|
||||
+
|
||||
+ def _WorkloadTask(self):
|
||||
+ if self.__started:
|
||||
+ return
|
||||
+
|
||||
+ self._log(Log.DEBUG, f'starting with cmd: {" ".join(self.__cmd)}')
|
||||
+
|
||||
+ self.__timerlat_out.seek(0)
|
||||
+ self.__timerlat_err.seek(0)
|
||||
+ try:
|
||||
+ self.__timerlat_process = subprocess.Popen(self.__cmd,
|
||||
+ stdout=self.__timerlat_out,
|
||||
+ stderr=self.__timerlat_err,
|
||||
+ stdin=None)
|
||||
+ self.__started = True
|
||||
+ except OSError:
|
||||
+ self.__started = False
|
||||
+
|
||||
+ def WorkloadAlive(self):
|
||||
+ if self.__started:
|
||||
+ return self.__timerlat_process.poll() is None
|
||||
+ return False
|
||||
+
|
||||
+ def _WorkloadCleanup(self):
|
||||
+ if not self.__started:
|
||||
+ return
|
||||
+ while self.__timerlat_process.poll() is None:
|
||||
+ self._log(Log.DEBUG, "Sending SIGINT")
|
||||
+ os.kill(self.__timerlat_process.pid, signal.SIGINT)
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ self._setFinished()
|
||||
+ self.__started = False
|
||||
+
|
||||
+ def MakeReport(self):
|
||||
+ self.__timerlat_out.seek(0)
|
||||
+ for line in self.__timerlat_out:
|
||||
+ line = bytes.decode(line)
|
||||
+ print(line)
|
||||
+ self.__timerlat_out.close()
|
||||
+
|
||||
+
|
||||
+def ModuleInfo():
|
||||
+ """ Required measurement module information """
|
||||
+ return {"parallel": True,
|
||||
+ "loads": True}
|
||||
+
|
||||
+def ModuleParameters():
|
||||
+ """ default parameters """
|
||||
+ return {"priority": {"descr": "Run rtla timerlat with this priority",
|
||||
+ "default": 95,
|
||||
+ "metavar": "PRIO" }
|
||||
+ }
|
||||
+
|
||||
+def create(params, logger):
|
||||
+ """ Instantiate a Timerlat measurement module object"""
|
||||
+ return Timerlat(params, logger)
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ from rteval.rtevalConfig import rtevalConfig
|
||||
+
|
||||
+ l = Log()
|
||||
+ l.SetLogVerbosity(Log.INFO|Log.DEBUG|Log.ERR|Log.WARN)
|
||||
+
|
||||
+ cfg = rtevalConfig({}, logger=l)
|
||||
+ prms = {}
|
||||
+ modprms = ModuleParameters()
|
||||
+ for c, p in list(modprms.items()):
|
||||
+ prms[c] = p['default']
|
||||
+ cfg.AppendConfig('timerlat', prms)
|
||||
+
|
||||
+ cfg_tl = cfg.GetSection('timerlat')
|
||||
+ cfg_tl.cpulist = collapse_cpulist(SysTopology().online_cpus())
|
||||
+
|
||||
+ RUNTIME = 10
|
||||
+
|
||||
+ tl = Timerlat(cfg_tl, l)
|
||||
+ tl._WorkloadSetup()
|
||||
+ tl._WorkloadPrepare()
|
||||
+ tl._WorkloadTask()
|
||||
+ time.sleep(RUNTIME)
|
||||
+ tl._WorkloadCleanup()
|
||||
+ tl.MakeReport()
|
||||
--
|
||||
2.44.0
|
||||
|
178
rteval-Add-summary-reporting-for-timerlat.patch
Normal file
178
rteval-Add-summary-reporting-for-timerlat.patch
Normal file
@ -0,0 +1,178 @@
|
||||
From 20fbcc1547d77a77a3e333bac28b7f28632d8707 Mon Sep 17 00:00:00 2001
|
||||
From: John Kacur <jkacur@redhat.com>
|
||||
Date: Fri, 26 Apr 2024 14:45:48 -0400
|
||||
Subject: [PATCH 08/13] rteval: Add summary reporting for timerlat
|
||||
|
||||
This adds an rteval section to the xsl file and generates a timerlat
|
||||
report at the end of a run. To use it edit your rteval.conf file
|
||||
to comment out cyclictest and uncomment timerlat
|
||||
|
||||
Another interesting thing you can do is uncomment both of them and
|
||||
get a report from both in one run.
|
||||
|
||||
You can also use this with the summary report, for example
|
||||
|
||||
rteval -Z rteval-20240426-3/summary.xml
|
||||
|
||||
will work with timerlat now. (or both cyclictest and timerlat at the
|
||||
same time.)
|
||||
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval.conf | 1 +
|
||||
rteval/rteval_text.xsl | 103 +++++++++++++++++++++++++++++++++++++++--
|
||||
2 files changed, 100 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/rteval.conf b/rteval.conf
|
||||
index 4c32fcf4d842..601410b51c28 100644
|
||||
--- a/rteval.conf
|
||||
+++ b/rteval.conf
|
||||
@@ -7,6 +7,7 @@ report_interval: 600
|
||||
|
||||
[measurement]
|
||||
cyclictest: module
|
||||
+# timerlat: module
|
||||
|
||||
[loads]
|
||||
kcompile: module
|
||||
diff --git a/rteval/rteval_text.xsl b/rteval/rteval_text.xsl
|
||||
index f526526d4d49..1e9c0f0d26c5 100644
|
||||
--- a/rteval/rteval_text.xsl
|
||||
+++ b/rteval/rteval_text.xsl
|
||||
@@ -201,11 +201,11 @@
|
||||
<!-- -->
|
||||
<!-- select="cyclictest|new_foo_section|another_section" -->
|
||||
<!-- -->
|
||||
- <xsl:apply-templates select="cyclictest|hwlatdetect[@format='1.0']|sysstat"/>
|
||||
+ <xsl:apply-templates select="cyclictest|timerlat|hwlatdetect[@format='1.0']|sysstat"/>
|
||||
<xsl:text> </xsl:text>
|
||||
</xsl:template>
|
||||
|
||||
- <!-- Format the cyclic test section of the report -->
|
||||
+ <!-- Format the cyclictest section of the report -->
|
||||
<xsl:template match="/rteval/Measurements/Profile/cyclictest">
|
||||
<xsl:text> Latency test </xsl:text>
|
||||
|
||||
@@ -237,7 +237,7 @@
|
||||
</xsl:template>
|
||||
|
||||
|
||||
- <!-- Format the CPU core section in the cyclict test part -->
|
||||
+ <!-- Format the CPU core section in the cyclictest part -->
|
||||
<xsl:template match="/rteval/Measurements/Profile/cyclictest/core">
|
||||
<xsl:text> CPU core </xsl:text>
|
||||
<xsl:value-of select="@id"/>
|
||||
@@ -300,6 +300,101 @@
|
||||
<xsl:text> </xsl:text>
|
||||
</xsl:template>
|
||||
|
||||
+ <!-- Format the timerlat section of the report -->
|
||||
+ <xsl:template match="/rteval/Measurements/Profile/timerlat">
|
||||
+ <xsl:text> Latency test </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Started: </xsl:text>
|
||||
+ <xsl:value-of select="timestamps/runloop_start"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Stopped: </xsl:text>
|
||||
+ <xsl:value-of select="timestamps/runloop_stop"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Command: </xsl:text>
|
||||
+ <xsl:value-of select="@command_line"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:apply-templates select="abort_report"/>
|
||||
+
|
||||
+ <xsl:text> System: </xsl:text>
|
||||
+ <xsl:value-of select="system/@description"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Statistics: </xsl:text>
|
||||
+ <xsl:apply-templates select="system/statistics"/>
|
||||
+
|
||||
+ <!-- Add CPU core info and stats-->
|
||||
+ <xsl:apply-templates select="core">
|
||||
+ <xsl:sort select="@id" data-type="number"/>
|
||||
+ </xsl:apply-templates>
|
||||
+ </xsl:template>
|
||||
+
|
||||
+
|
||||
+ <!-- Format the CPU core section in the timerlat part -->
|
||||
+ <xsl:template match="/rteval/Measurements/Profile/timerlat/core">
|
||||
+ <xsl:text> CPU core </xsl:text>
|
||||
+ <xsl:value-of select="@id"/>
|
||||
+ <xsl:text> Priority: </xsl:text>
|
||||
+ <xsl:value-of select="@priority"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+ <xsl:text> Statistics: </xsl:text>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+ <xsl:apply-templates select="statistics"/>
|
||||
+ </xsl:template>
|
||||
+
|
||||
+
|
||||
+ <!-- Generic formatting of statistics information -->
|
||||
+ <xsl:template match="/rteval/Measurements/Profile/timerlat/*/statistics">
|
||||
+ <xsl:text> Samples: </xsl:text>
|
||||
+ <xsl:value-of select="samples"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:if test="samples > 0">
|
||||
+ <xsl:text> Mean: </xsl:text>
|
||||
+ <xsl:value-of select="mean"/>
|
||||
+ <xsl:value-of select="mean/@unit"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Median: </xsl:text>
|
||||
+ <xsl:value-of select="median"/>
|
||||
+ <xsl:value-of select="median/@unit"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Mode: </xsl:text>
|
||||
+ <xsl:value-of select="mode"/>
|
||||
+ <xsl:value-of select="mode/@unit"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Range: </xsl:text>
|
||||
+ <xsl:value-of select="range"/>
|
||||
+ <xsl:value-of select="range/@unit"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Min: </xsl:text>
|
||||
+ <xsl:value-of select="minimum"/>
|
||||
+ <xsl:value-of select="minimum/@unit"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Max: </xsl:text>
|
||||
+ <xsl:value-of select="maximum"/>
|
||||
+ <xsl:value-of select="maximum/@unit"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Mean Absolute Dev: </xsl:text>
|
||||
+ <xsl:value-of select="mean_absolute_deviation"/>
|
||||
+ <xsl:value-of select="mean_absolute_deviation/@unit"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+
|
||||
+ <xsl:text> Std.dev: </xsl:text>
|
||||
+ <xsl:value-of select="standard_deviation"/>
|
||||
+ <xsl:value-of select="standard_deviation/@unit"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+ </xsl:if>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+ </xsl:template>
|
||||
+
|
||||
|
||||
<!-- Format the hwlatdetect test section of the report -->
|
||||
<xsl:template match="/rteval/Measurements/Profile/hwlatdetect[@format='1.0' and not(@aborted)]">
|
||||
@@ -340,7 +435,7 @@
|
||||
<xsl:text>us </xsl:text>
|
||||
</xsl:template>
|
||||
|
||||
- <!-- Format the cyclic test section of the report -->
|
||||
+ <!-- Format the cyclictest section of the report -->
|
||||
<xsl:template match="/rteval/Measurements/Profile/sysstat">
|
||||
<xsl:text> sysstat measurements </xsl:text>
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
88
rteval-Cyclictest.py-Make-standalone-file-work-again.patch
Normal file
88
rteval-Cyclictest.py-Make-standalone-file-work-again.patch
Normal file
@ -0,0 +1,88 @@
|
||||
From ea72c99ead85d1fcf156b9a3825a547ba7f4d0f8 Mon Sep 17 00:00:00 2001
|
||||
From: John Kacur <jkacur@redhat.com>
|
||||
Date: Fri, 12 Apr 2024 14:34:11 -0400
|
||||
Subject: [PATCH 01/13] rteval: Cyclictest.py: Make standalone file work again
|
||||
|
||||
Make standalone Cyclictest.py work again for testing purposes
|
||||
- remove unused parse_cpulist_from_config
|
||||
- Instead of "import as", use "from" and the requested functionality
|
||||
- Obtain the default buckets from the ModuleParameters to use if a
|
||||
number is not otherwise provided
|
||||
- set the cpulist to "" if not otherwise provided
|
||||
- add a few docstrings to functions
|
||||
- obtain a default cpulist from online_cpus for the standalone test
|
||||
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval/modules/measurement/cyclictest.py | 19 ++++++++-----------
|
||||
1 file changed, 8 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py
|
||||
index 722422589d62..671426e13c4d 100644
|
||||
--- a/rteval/modules/measurement/cyclictest.py
|
||||
+++ b/rteval/modules/measurement/cyclictest.py
|
||||
@@ -16,10 +16,8 @@ import math
|
||||
import libxml2
|
||||
from rteval.Log import Log
|
||||
from rteval.modules import rtevalModulePrototype
|
||||
-from rteval.systopology import cpuinfo, parse_cpulist_from_config
|
||||
-import rteval.cpulist_utils as cpulist_utils
|
||||
-
|
||||
-expand_cpulist = cpulist_utils.expand_cpulist
|
||||
+from rteval.systopology import cpuinfo, SysTopology
|
||||
+from rteval.cpulist_utils import expand_cpulist, collapse_cpulist
|
||||
|
||||
class RunData:
|
||||
'''class to keep instance data from a cyclictest run'''
|
||||
@@ -190,10 +188,11 @@ class Cyclictest(rtevalModulePrototype):
|
||||
# Create a RunData object per CPU core
|
||||
self.__numanodes = int(self.__cfg.setdefault('numanodes', 0))
|
||||
self.__priority = int(self.__cfg.setdefault('priority', 95))
|
||||
- self.__buckets = int(self.__cfg.setdefault('buckets', 2000))
|
||||
+ default_buckets = ModuleParameters()["buckets"]["default"]
|
||||
+ self.__buckets = int(self.__cfg.setdefault('buckets', default_buckets))
|
||||
self.__numcores = 0
|
||||
self.__cyclicdata = {}
|
||||
- self.__cpulist = self.__cfg.cpulist
|
||||
+ self.__cpulist = self.__cfg.setdefault('cpulist', "")
|
||||
self.__cpus = [str(c) for c in expand_cpulist(self.__cpulist)]
|
||||
self.__numcores = len(self.__cpus)
|
||||
|
||||
@@ -393,14 +392,13 @@ class Cyclictest(rtevalModulePrototype):
|
||||
return rep_n
|
||||
|
||||
|
||||
-
|
||||
def ModuleInfo():
|
||||
return {"parallel": True,
|
||||
"loads": True}
|
||||
|
||||
|
||||
-
|
||||
def ModuleParameters():
|
||||
+ """ default parameters """
|
||||
return {"interval": {"descr": "Base interval of the threads in microseconds",
|
||||
"default": 100,
|
||||
"metavar": "INTV_US"},
|
||||
@@ -421,6 +419,7 @@ def ModuleParameters():
|
||||
|
||||
|
||||
def create(params, logger):
|
||||
+ """ Instantiate a Cyclictest measurement module object """
|
||||
return Cyclictest(params, logger)
|
||||
|
||||
|
||||
@@ -438,9 +437,7 @@ if __name__ == '__main__':
|
||||
cfg.AppendConfig('cyclictest', prms)
|
||||
|
||||
cfg_ct = cfg.GetSection('cyclictest')
|
||||
- cfg_ct.reportdir = "."
|
||||
- cfg_ct.buckets = 200
|
||||
- # cfg_ct.breaktrace = 30
|
||||
+ cfg_ct.cpulist = collapse_cpulist(SysTopology().online_cpus())
|
||||
|
||||
runtime = 10
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
69
rteval-Generate-raw-histogram-data-for-an-existing-t.patch
Normal file
69
rteval-Generate-raw-histogram-data-for-an-existing-t.patch
Normal file
@ -0,0 +1,69 @@
|
||||
From 843ff713340ab701aa8859ca8b3d8a776608175c Mon Sep 17 00:00:00 2001
|
||||
From: John Kacur <jkacur@redhat.com>
|
||||
Date: Mon, 29 Apr 2024 10:47:41 -0400
|
||||
Subject: [PATCH 09/13] rteval: Generate raw histogram data for an existing
|
||||
timerlat report
|
||||
|
||||
Generate raw histogram data for an existing timerlat report.
|
||||
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval/rteval_histogram_raw.xsl | 29 ++++++++++++++++++++++++++++-
|
||||
1 file changed, 28 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/rteval/rteval_histogram_raw.xsl b/rteval/rteval_histogram_raw.xsl
|
||||
index 038419bf03b7..00b2be34f305 100644
|
||||
--- a/rteval/rteval_histogram_raw.xsl
|
||||
+++ b/rteval/rteval_histogram_raw.xsl
|
||||
@@ -12,15 +12,26 @@
|
||||
|
||||
<!-- Extract overall system histogram data -->
|
||||
<xsl:apply-templates select="Measurements/Profile/cyclictest/system/histogram/bucket">
|
||||
- <xsl:with-param name="label" select="'system'"/>
|
||||
+ <xsl:with-param name="label" select="'system'"/>
|
||||
<xsl:sort select="Measurements/Profile/cyclictest/core/histogram/bucket/@index" data-type="number"/>
|
||||
</xsl:apply-templates>
|
||||
|
||||
+ <xsl:apply-templates select="Measurements/Profile/timerlat/system/histogram/bucket">
|
||||
+ <xsl:with-param name="label" select="'system'"/>
|
||||
+ <xsl:sort select="Measurements/Profile/timerlat/core/histogram/bucket/@index" data-type="number"/>
|
||||
+ </xsl:apply-templates>
|
||||
+
|
||||
<!-- Extract per cpu core histogram data -->
|
||||
<xsl:apply-templates select="Measurements/Profile/cyclictest/core/histogram/bucket">
|
||||
<xsl:sort select="Measurements/Profile/cyclictest/core/@id" data-type="number"/>
|
||||
<xsl:sort select="Measurements/Profile/cyclictest/core/histogram/bucket/@index" data-type="number"/>
|
||||
</xsl:apply-templates>
|
||||
+
|
||||
+ <xsl:apply-templates select="Measurements/Profile/timerlat/core/histogram/bucket">
|
||||
+ <xsl:sort select="Measurements/Profile/timerlat/core/@id" data-type="number"/>
|
||||
+ <xsl:sort select="Measurements/Profile/timerlat/core/histogram/bucket/@index" data-type="number"/>
|
||||
+ </xsl:apply-templates>
|
||||
+
|
||||
</xsl:template>
|
||||
<!-- -->
|
||||
<!-- End of main report framework -->
|
||||
@@ -43,4 +54,20 @@
|
||||
<xsl:text> </xsl:text>
|
||||
</xsl:template>
|
||||
|
||||
+ <xsl:template match="/rteval/Measurements/Profile/timerlat/*/histogram/bucket">
|
||||
+ <xsl:param name="label"/>
|
||||
+ <xsl:choose>
|
||||
+ <!-- If we don't have a id tag in what should be a 'core' tag, use the given label -->
|
||||
+ <xsl:when test="../../@id"><xsl:value-of select="../../@id"/></xsl:when>
|
||||
+ <xsl:otherwise><xsl:value-of select="$label"/></xsl:otherwise>
|
||||
+ </xsl:choose>
|
||||
+ <xsl:text>	</xsl:text>
|
||||
+
|
||||
+ <xsl:value-of select="@index"/>
|
||||
+ <xsl:text>	</xsl:text>
|
||||
+
|
||||
+ <xsl:value-of select="@value"/>
|
||||
+ <xsl:text> </xsl:text>
|
||||
+ </xsl:template>
|
||||
+
|
||||
</xsl:stylesheet>
|
||||
--
|
||||
2.44.0
|
||||
|
60
rteval-Make-output-work-with-new-dmiinfo-format.patch
Normal file
60
rteval-Make-output-work-with-new-dmiinfo-format.patch
Normal file
@ -0,0 +1,60 @@
|
||||
From 701ab9f5d50fb0e1308ad8ea163d49525d3d90af Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Glozar <tglozar@redhat.com>
|
||||
Date: Mon, 29 Apr 2024 10:47:20 +0200
|
||||
Subject: [PATCH 10/13] rteval: Make output work with new dmiinfo format
|
||||
|
||||
Commit 43c45ba7 ("rteval: Implement initial dmidecode support")
|
||||
re-introduced DMI information into rteval XML summary, but in a
|
||||
different format, which is not correctly picked up in rteval_text.xsl
|
||||
for formatting BIOS and vendor information into rteval text output.
|
||||
|
||||
This lead to the information not being displayed correctly:
|
||||
Model: -
|
||||
BIOS version: (ver: , rev :, release date: )
|
||||
|
||||
Modify the relevant section of rteval_text.xsl to make it understand the
|
||||
new format. Also, adjust the incorrect spacing around "rev".
|
||||
|
||||
New output (in QEMU VM):
|
||||
Model: QEMU - Standard PC (Q35 + ICH9, 2009)
|
||||
BIOS version: SeaBIOS (ver: 1.16.3-1.fc39, rev: 0.0, release date: 04/01/2014)
|
||||
|
||||
Signed-off-by: Tomas Glozar <tglozar@redhat.com>
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval/rteval_text.xsl | 14 +++++++-------
|
||||
1 file changed, 7 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/rteval/rteval_text.xsl b/rteval/rteval_text.xsl
|
||||
index 1e9c0f0d26c5..2f03bda0bb55 100644
|
||||
--- a/rteval/rteval_text.xsl
|
||||
+++ b/rteval/rteval_text.xsl
|
||||
@@ -39,18 +39,18 @@
|
||||
<xsl:text> </xsl:text>
|
||||
|
||||
<xsl:text> Model: </xsl:text>
|
||||
- <xsl:value-of select="SystemInfo/DMIinfo/HardwareInfo/GeneralInfo/Manufacturer|HardwareInfo/GeneralInfo/ProductName"/>
|
||||
- <xsl:text> - </xsl:text><xsl:value-of select="SystemInfo/DMIinfo/HardwareInfo/GeneralInfo/ProductName|HardwareInfo/GeneralInfo/ProductName"/>
|
||||
+ <xsl:value-of select="SystemInfo/DMIinfo/Handle[@name='System Information']/Field[@Name='Manufacturer']"/>
|
||||
+ <xsl:text> - </xsl:text><xsl:value-of select="SystemInfo/DMIinfo/Handle[@name='System Information']/Field[@Name='Product Name']"/>
|
||||
<xsl:text> </xsl:text>
|
||||
|
||||
<xsl:text> BIOS version: </xsl:text>
|
||||
- <xsl:value-of select="SystemInfo/DMIinfo/HardwareInfo/BIOS|HardwareInfo/BIOS"/>
|
||||
+ <xsl:value-of select="SystemInfo/DMIinfo/Handle[@name='BIOS Information']/Field[@Name='Vendor']"/>
|
||||
<xsl:text> (ver: </xsl:text>
|
||||
- <xsl:value-of select="SystemInfo/DMIinfo/HardwareInfo/BIOS/@Version|HardwareInfo/BIOS/@Version"/>
|
||||
- <xsl:text>, rev :</xsl:text>
|
||||
- <xsl:value-of select="SystemInfo/DMIinfo/HardwareInfo/BIOS/@BIOSrevision|HardwareInfo/BIOS/@BIOSrevision"/>
|
||||
+ <xsl:value-of select="SystemInfo/DMIinfo/Handle[@name='BIOS Information']/Field[@Name='Version']"/>
|
||||
+ <xsl:text>, rev: </xsl:text>
|
||||
+ <xsl:value-of select="SystemInfo/DMIinfo/Handle[@name='BIOS Information']/Field[@Name='BIOS Revision']"/>
|
||||
<xsl:text>, release date: </xsl:text>
|
||||
- <xsl:value-of select="SystemInfo/DMIinfo/HardwareInfo/BIOS/@ReleaseDate|HardwareInfo/BIOS/@ReleaseDate"/>
|
||||
+ <xsl:value-of select="SystemInfo/DMIinfo/Handle[@name='BIOS Information']/Field[@Name='Release Date']"/>
|
||||
<xsl:text>)</xsl:text>
|
||||
<xsl:text> </xsl:text>
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
45
rteval-Use-get-instead-of-setdefault-for-calculating.patch
Normal file
45
rteval-Use-get-instead-of-setdefault-for-calculating.patch
Normal file
@ -0,0 +1,45 @@
|
||||
From a5f3289e2ee6577cbbb78661bea58b821942ba56 Mon Sep 17 00:00:00 2001
|
||||
From: John Kacur <jkacur@redhat.com>
|
||||
Date: Thu, 2 May 2024 13:09:01 -0400
|
||||
Subject: [PATCH 12/13] rteval: Use get instead of setdefault for calculating
|
||||
range
|
||||
|
||||
As Crystal Wood <crwood@redhat.com> points out, there is no
|
||||
need to setdefault when calculating the range, just use get
|
||||
if there is no value.
|
||||
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval/modules/measurement/cyclictest.py | 2 +-
|
||||
rteval/modules/measurement/timerlat.py | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py
|
||||
index 925065367eaf..3301e1b45e11 100644
|
||||
--- a/rteval/modules/measurement/cyclictest.py
|
||||
+++ b/rteval/modules/measurement/cyclictest.py
|
||||
@@ -115,7 +115,7 @@ class RunData:
|
||||
low = i
|
||||
break
|
||||
high = keys[-1]
|
||||
- while high and self.__samples.setdefault(high, 0) == 0:
|
||||
+ while high and self.__samples.get(high, 0) == 0:
|
||||
high -= 1
|
||||
self.__range = high - low
|
||||
|
||||
diff --git a/rteval/modules/measurement/timerlat.py b/rteval/modules/measurement/timerlat.py
|
||||
index 9fa931043e40..e8345fab1ad7 100644
|
||||
--- a/rteval/modules/measurement/timerlat.py
|
||||
+++ b/rteval/modules/measurement/timerlat.py
|
||||
@@ -106,7 +106,7 @@ class TLRunData:
|
||||
low = i
|
||||
break
|
||||
high = keys[-1]
|
||||
- while high and self.__samples.setdefault(high, 0) == 0:
|
||||
+ while high and self.__samples.get(high, 0) == 0:
|
||||
high -= 1
|
||||
self.__range = high - low
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
45
rteval-cyclictest.py-Fix-the-description-in-the-xml-.patch
Normal file
45
rteval-cyclictest.py-Fix-the-description-in-the-xml-.patch
Normal file
@ -0,0 +1,45 @@
|
||||
From 0a431278cc681270b54efea4eebce9571c237624 Mon Sep 17 00:00:00 2001
|
||||
From: John Kacur <jkacur@redhat.com>
|
||||
Date: Thu, 18 Apr 2024 12:39:33 -0400
|
||||
Subject: [PATCH 03/13] rteval: cyclictest.py: Fix the description in the xml
|
||||
report
|
||||
|
||||
If the __type is 'system' we should add the description to the xml
|
||||
The output should be something like this
|
||||
|
||||
<system description="(12 cores) Intel(R) Core(TM) i7-9850H CPU @ 2.60GHz">
|
||||
|
||||
Before this fix it was
|
||||
|
||||
<system description="">
|
||||
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval/modules/measurement/cyclictest.py | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py
|
||||
index 671426e13c4d..48b86a03cbfc 100644
|
||||
--- a/rteval/modules/measurement/cyclictest.py
|
||||
+++ b/rteval/modules/measurement/cyclictest.py
|
||||
@@ -25,7 +25,7 @@ class RunData:
|
||||
self.__id = coreid
|
||||
self.__type = datatype
|
||||
self.__priority = int(priority)
|
||||
- self.__description = ''
|
||||
+ self.description = ''
|
||||
# histogram of data
|
||||
self.__samples = {}
|
||||
self.__numsamples = 0
|
||||
@@ -130,7 +130,7 @@ class RunData:
|
||||
def MakeReport(self):
|
||||
rep_n = libxml2.newNode(self.__type)
|
||||
if self.__type == 'system':
|
||||
- rep_n.newProp('description', self.__description)
|
||||
+ rep_n.newProp('description', self.description)
|
||||
else:
|
||||
rep_n.newProp('id', str(self.__id))
|
||||
rep_n.newProp('priority', str(self.__priority))
|
||||
--
|
||||
2.44.0
|
||||
|
60
rteval-cyclictest.py-Fix-the-median-calculation.patch
Normal file
60
rteval-cyclictest.py-Fix-the-median-calculation.patch
Normal file
@ -0,0 +1,60 @@
|
||||
From 071bd1be404f1b5c71f8b2f2d9fa1a85bd485ce9 Mon Sep 17 00:00:00 2001
|
||||
From: John Kacur <jkacur@redhat.com>
|
||||
Date: Thu, 25 Apr 2024 13:48:05 -0400
|
||||
Subject: [PATCH 05/13] rteval: cyclictest.py: Fix the median calculation
|
||||
|
||||
Fix the calculation of the median in rteval for cyclictest
|
||||
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval/modules/measurement/cyclictest.py | 20 ++++++++++++++------
|
||||
1 file changed, 14 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py
|
||||
index 1ba8b8b2323c..cef73abd1b4b 100644
|
||||
--- a/rteval/modules/measurement/cyclictest.py
|
||||
+++ b/rteval/modules/measurement/cyclictest.py
|
||||
@@ -80,26 +80,34 @@ class RunData:
|
||||
return
|
||||
|
||||
self._log(Log.INFO, f"reducing {self.__id}")
|
||||
- total = 0
|
||||
+ total = 0 # total number of samples
|
||||
+ total_us = 0
|
||||
keys = list(self.__samples.keys())
|
||||
keys.sort()
|
||||
|
||||
- mid = self.__numsamples / 2
|
||||
+ # if numsamples is odd, then + 1 gives us the actual mid
|
||||
+ # if numsamples is even, we avg mid and mid + 1, so we actually
|
||||
+ # want to know mid + 1 since we will combine it with mid and
|
||||
+ # the lastkey if the last key is at the end of a previous bucket
|
||||
+ mid = int(self.__numsamples / 2) + 1
|
||||
|
||||
# mean, mode, and median
|
||||
occurances = 0
|
||||
lastkey = -1
|
||||
for i in keys:
|
||||
- if mid > total and mid <= (total + self.__samples[i]):
|
||||
- if self.__numsamples & 1 and mid == total+1:
|
||||
+ if mid > total and mid <= total + self.__samples[i]:
|
||||
+ # Test if numsamples is even and if mid+1 is the next bucket
|
||||
+ if self.__numsamples & 1 != 0 and mid == total+1:
|
||||
self.__median = (lastkey + i) / 2
|
||||
else:
|
||||
self.__median = i
|
||||
- total += (i * self.__samples[i])
|
||||
+ lastkey = i
|
||||
+ total += self.__samples[i]
|
||||
+ total_us += (i * self.__samples[i])
|
||||
if self.__samples[i] > occurances:
|
||||
occurances = self.__samples[i]
|
||||
self.__mode = i
|
||||
- self.__mean = float(total) / float(self.__numsamples)
|
||||
+ self.__mean = float(total_us) / float(self.__numsamples)
|
||||
|
||||
# range
|
||||
for i in keys:
|
||||
--
|
||||
2.44.0
|
||||
|
32
rteval-cyclictest.py-Remove-unused-method-sample.patch
Normal file
32
rteval-cyclictest.py-Remove-unused-method-sample.patch
Normal file
@ -0,0 +1,32 @@
|
||||
From 6a185e50bb9b0903e034a2361456b5742e60c47b Mon Sep 17 00:00:00 2001
|
||||
From: John Kacur <jkacur@redhat.com>
|
||||
Date: Fri, 19 Apr 2024 12:17:31 -0400
|
||||
Subject: [PATCH 04/13] rteval: cyclictest.py: Remove unused method sample
|
||||
|
||||
Remove the unused method sample() in class RunData
|
||||
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval/modules/measurement/cyclictest.py | 6 ------
|
||||
1 file changed, 6 deletions(-)
|
||||
|
||||
diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py
|
||||
index 48b86a03cbfc..1ba8b8b2323c 100644
|
||||
--- a/rteval/modules/measurement/cyclictest.py
|
||||
+++ b/rteval/modules/measurement/cyclictest.py
|
||||
@@ -61,12 +61,6 @@ class RunData:
|
||||
if value < self.__min:
|
||||
self.__min = value
|
||||
|
||||
- def sample(self, value):
|
||||
- self.__samples[value] += self.__samples.setdefault(value, 0) + 1
|
||||
- self.update_max(value)
|
||||
- self.update_min(value)
|
||||
- self.__numsamples += 1
|
||||
-
|
||||
def bucket(self, index, value):
|
||||
self.__samples[index] = self.__samples.setdefault(index, 0) + value
|
||||
if value:
|
||||
--
|
||||
2.44.0
|
||||
|
35
rteval-cyclictest.py-reduce-Fix-exception-with-missi.patch
Normal file
35
rteval-cyclictest.py-reduce-Fix-exception-with-missi.patch
Normal file
@ -0,0 +1,35 @@
|
||||
From eb344bffd1525bbb8cd95fb4f4501e467cdc21ce Mon Sep 17 00:00:00 2001
|
||||
From: Crystal Wood <crwood@redhat.com>
|
||||
Date: Wed, 1 May 2024 15:09:22 -0500
|
||||
Subject: [PATCH 11/13] rteval: cyclictest.py: reduce: Fix exception with
|
||||
missing samples
|
||||
|
||||
If cyclictest omits a line of zeroes rather than printing it,
|
||||
__samples[high] will not exist (rather than be zero). Handle this
|
||||
in preparation for cyclictest doing this.
|
||||
|
||||
Note that get() seems more suitable than setdefault(), but the latter
|
||||
is used in order to match the code in timerlat.py.
|
||||
|
||||
Signed-off-by: Crystal Wood <crwood@redhat.com>
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval/modules/measurement/cyclictest.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py
|
||||
index cef73abd1b4b..925065367eaf 100644
|
||||
--- a/rteval/modules/measurement/cyclictest.py
|
||||
+++ b/rteval/modules/measurement/cyclictest.py
|
||||
@@ -115,7 +115,7 @@ class RunData:
|
||||
low = i
|
||||
break
|
||||
high = keys[-1]
|
||||
- while high and self.__samples[high] == 0:
|
||||
+ while high and self.__samples.setdefault(high, 0) == 0:
|
||||
high -= 1
|
||||
self.__range = high - low
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
358
rteval-timerlat.py-Add-statistics-and-generate-xml.patch
Normal file
358
rteval-timerlat.py-Add-statistics-and-generate-xml.patch
Normal file
@ -0,0 +1,358 @@
|
||||
From 53248cbad7b0b353e31a18b40181f38da0a721aa Mon Sep 17 00:00:00 2001
|
||||
From: John Kacur <jkacur@redhat.com>
|
||||
Date: Thu, 25 Apr 2024 13:59:27 -0400
|
||||
Subject: [PATCH 06/13] rteval: timerlat.py Add statistics and generate xml
|
||||
|
||||
This adds calculating statitics such as samples (count), min, max, mean,
|
||||
median, mode, range, absolute and standard deviations to xml
|
||||
|
||||
Currently it combines the kernel thread, IRQs and userspace threads into
|
||||
one statistic like it does with cyclictest, but in the future we can
|
||||
separate this out too.
|
||||
|
||||
To see this functioning from git, do this,
|
||||
|
||||
su -c 'python rteval/modules/measurement/timerlat.py '
|
||||
|
||||
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||||
---
|
||||
rteval/modules/measurement/timerlat.py | 266 ++++++++++++++++++++++++-
|
||||
1 file changed, 259 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/rteval/modules/measurement/timerlat.py b/rteval/modules/measurement/timerlat.py
|
||||
index d4e78de8d2a2..9fa931043e40 100644
|
||||
--- a/rteval/modules/measurement/timerlat.py
|
||||
+++ b/rteval/modules/measurement/timerlat.py
|
||||
@@ -8,12 +8,170 @@ import subprocess
|
||||
import signal
|
||||
import time
|
||||
import tempfile
|
||||
+import math
|
||||
+import sys
|
||||
import libxml2
|
||||
from rteval.Log import Log
|
||||
from rteval.modules import rtevalModulePrototype
|
||||
from rteval.systopology import cpuinfo, SysTopology
|
||||
from rteval.cpulist_utils import expand_cpulist, collapse_cpulist
|
||||
|
||||
+class TLRunData:
|
||||
+ ''' class to store instance data from a timerlat run '''
|
||||
+ def __init__(self, coreid, datatype, priority, logfnc):
|
||||
+ self.__id = coreid
|
||||
+ self.__type = datatype
|
||||
+ self.__priority = int(priority)
|
||||
+ self.description = ''
|
||||
+ self._log = logfnc
|
||||
+ self.duration = ''
|
||||
+ # histogram data, irqs, kernel threads and user threads per core
|
||||
+ self.irqs = {}
|
||||
+ self.thrs = {}
|
||||
+ self.usrs = {}
|
||||
+ self.__samples = {}
|
||||
+ self.__numsamples = 0
|
||||
+ self.min = 100000000
|
||||
+ self.max = 0
|
||||
+ self.__stddev = 0.0
|
||||
+ self.__mean = 0.0
|
||||
+ self.__mode = 0.0
|
||||
+ self.__median = 0.0
|
||||
+ self.__range = 0.0
|
||||
+
|
||||
+ def update_max(self, value):
|
||||
+ """ highest bucket with a value """
|
||||
+ if value > self.max:
|
||||
+ self.max = value
|
||||
+
|
||||
+ def update_min(self, value):
|
||||
+ """ lowest bucket with a value """
|
||||
+ if value < self.min:
|
||||
+ self.min = value
|
||||
+
|
||||
+ def bucket(self, index, val1, val2, val3):
|
||||
+ """ Store results index=bucket number, val1=IRQ, val2=thr, val3=usr """
|
||||
+ values = val1 + val2 + val3
|
||||
+ self.__samples[index] = self.__samples.setdefault(index, 0) + values
|
||||
+ self.irqs[index] = val1
|
||||
+ self.thrs[index] = val2
|
||||
+ self.usrs[index] = val3
|
||||
+ if values:
|
||||
+ self.update_max(index)
|
||||
+ self.update_min(index)
|
||||
+ self.__numsamples += values
|
||||
+
|
||||
+ def reduce(self):
|
||||
+ """ Calculate statistics """
|
||||
+ # Check to see if we have any samples. If there are 1 or 0, return
|
||||
+ if self.__numsamples <= 1:
|
||||
+ self._log(Log.DEBUG, f"skipping {self.__id} ({self.__numsamples} sampples)")
|
||||
+ self.__mad = 0
|
||||
+ self.__stddev = 0
|
||||
+ return
|
||||
+
|
||||
+ self._log(Log.INFO, f"reducing {self.__id}")
|
||||
+ total = 0 # total number of samples
|
||||
+ total_us = 0
|
||||
+ keys = list(self.__samples.keys())
|
||||
+ keys.sort()
|
||||
+
|
||||
+ # if numsamples is odd, then + 1 gives us the actual mid
|
||||
+ # if numsamples is even, we avg mid and mid + 1, so we actually
|
||||
+ # want to know mid + 1 since we will combine it with mid and
|
||||
+ # the lastkey if the last key is at the end of a previous bucket
|
||||
+ mid = int(self.__numsamples / 2) + 1
|
||||
+
|
||||
+ # mean, mode and median
|
||||
+ occurances = 0
|
||||
+ lastkey = -1
|
||||
+ for i in keys:
|
||||
+ if mid > total and mid <= total + self.__samples[i]:
|
||||
+ # Test if numsamples is even and if mid+1 is the next bucket
|
||||
+ if self.__numsamples & 1 != 0 and mid == total+1:
|
||||
+ self.__median = (lastkey + i) / 2
|
||||
+ else:
|
||||
+ self.__median = i
|
||||
+ lastkey = i
|
||||
+ total += self.__samples[i]
|
||||
+ total_us += i * self.__samples[i]
|
||||
+ if self.__samples[i] > occurances:
|
||||
+ occurances = self.__samples[i]
|
||||
+ self.__mode = i
|
||||
+ self.__mean = float(total_us) / float(self.__numsamples)
|
||||
+
|
||||
+ # range
|
||||
+ for i in keys:
|
||||
+ if self.__samples[i]:
|
||||
+ low = i
|
||||
+ break
|
||||
+ high = keys[-1]
|
||||
+ while high and self.__samples.setdefault(high, 0) == 0:
|
||||
+ high -= 1
|
||||
+ self.__range = high - low
|
||||
+
|
||||
+ # Mean Absolute Deviation and Standard Deviation
|
||||
+ madsum = 0
|
||||
+ varsum = 0
|
||||
+ for i in keys:
|
||||
+ madsum += float(abs(float(i) - self.__mean) * self.__samples[i])
|
||||
+ varsum += float(((float(i) - self.__mean) ** 2) * self.__samples[i])
|
||||
+ self.__mad = madsum / self.__numsamples
|
||||
+ self.__stddev = math.sqrt(varsum / (self.__numsamples - 1))
|
||||
+
|
||||
+
|
||||
+ def MakeReport(self):
|
||||
+ rep_n = libxml2.newNode(self.__type)
|
||||
+
|
||||
+ if self.__type == 'system':
|
||||
+ rep_n.newProp('description', self.description)
|
||||
+ else:
|
||||
+ rep_n.newProp('id', str(self.__id))
|
||||
+ rep_n.newProp('priority', str(self.__priority))
|
||||
+
|
||||
+ stat_n = rep_n.newChild(None, 'statistics', None)
|
||||
+ stat_n.newTextChild(None, 'samples', str(self.__numsamples))
|
||||
+
|
||||
+ if self.__numsamples > 0:
|
||||
+ n = stat_n.newTextChild(None, 'minimum', str(self.min))
|
||||
+ n.newProp('unit', 'us')
|
||||
+
|
||||
+ n = stat_n.newTextChild(None, 'maximum', str(self.max))
|
||||
+ n.newProp('unit', 'us')
|
||||
+
|
||||
+ n = stat_n.newTextChild(None, 'median', str(self.__median))
|
||||
+ n.newProp('unit', 'us')
|
||||
+
|
||||
+ n = stat_n.newTextChild(None, 'mode', str(self.__mode))
|
||||
+ n.newProp('unit', 'us')
|
||||
+
|
||||
+ n = stat_n.newTextChild(None, 'range', str(self.__range))
|
||||
+ n.newProp('unit', 'us')
|
||||
+
|
||||
+ n = stat_n.newTextChild(None, 'mean', str(self.__mean))
|
||||
+ n.newProp('unit', 'us')
|
||||
+
|
||||
+ n = stat_n.newTextChild(None, 'mean_absolute_deviation', str(self.__mad))
|
||||
+ n.newProp('unit', 'us')
|
||||
+
|
||||
+ n = stat_n.newTextChild(None, 'standard_deviation', str(self.__stddev))
|
||||
+ n.newProp('unit', 'us')
|
||||
+
|
||||
+ hist_n = rep_n.newChild(None, 'histogram', None)
|
||||
+ hist_n.newProp('nbuckets', str(len(self.__samples)))
|
||||
+
|
||||
+ keys = list(self.__samples.keys())
|
||||
+ keys.sort()
|
||||
+ for k in keys:
|
||||
+ if self.__samples[k] == 0:
|
||||
+ # Don't report buckets without any samples
|
||||
+ continue
|
||||
+ b_n = hist_n.newChild(None, 'bucket', None)
|
||||
+ b_n.newProp('index', str(k))
|
||||
+ b_n.newProp('value', str(self.__samples[k]))
|
||||
+
|
||||
+ return rep_n
|
||||
+
|
||||
class Timerlat(rtevalModulePrototype):
|
||||
""" measurement modules for rteval """
|
||||
def __init__(self, config, logger=None):
|
||||
@@ -23,6 +181,8 @@ class Timerlat(rtevalModulePrototype):
|
||||
|
||||
self.__numanodes = int(self.__cfg.setdefault('numanodes', 0))
|
||||
self.__priority = int(self.__cfg.setdefault('priority', 95))
|
||||
+ default_buckets = ModuleParameters()["buckets"]["default"]
|
||||
+ self.__buckets = int(self.__cfg.setdefault('buckets', default_buckets))
|
||||
|
||||
self.__cpulist = self.__cfg.setdefault('cpulist', "")
|
||||
self.__cpus = [str(c) for c in expand_cpulist(self.__cpulist)]
|
||||
@@ -31,6 +191,20 @@ class Timerlat(rtevalModulePrototype):
|
||||
self.__timerlat_out = None
|
||||
self.__timerlat_err = None
|
||||
self.__started = False
|
||||
+
|
||||
+ # Create a TLRunData object for each core we'll measure
|
||||
+ info = cpuinfo()
|
||||
+ self.__timerlatdata = {}
|
||||
+ for core in self.__cpus:
|
||||
+ self.__timerlatdata[core] = TLRunData(core, 'core', self.__priority,
|
||||
+ logfnc=self._log)
|
||||
+ self.__timerlatdata[core].description = info[core]['model name']
|
||||
+
|
||||
+ # Create a TLRunData object for the overall system
|
||||
+ self.__timerlatdata['system'] = TLRunData('system', 'system',
|
||||
+ self.__priority,
|
||||
+ logfnc=self._log)
|
||||
+ self.__timerlatdata['system'].description = (f"({self.__numcores} cores) ") + info['0']['model name']
|
||||
self._log(Log.DEBUG, f"system using {self.__numcores} cpu cores")
|
||||
|
||||
|
||||
@@ -43,6 +217,7 @@ class Timerlat(rtevalModulePrototype):
|
||||
def _WorkloadPrepare(self):
|
||||
self.__cmd = ['rtla', 'timerlat', 'hist', '-P', f'f:{int(self.__priority)}', '-u']
|
||||
self.__cmd.append(f'-c{self.__cpulist}')
|
||||
+ self.__cmd.append(f'-E{self.__buckets}')
|
||||
self._log(Log.DEBUG, f'self.__cmd = {self.__cmd}')
|
||||
self.__timerlat_out = tempfile.SpooledTemporaryFile(mode='w+b')
|
||||
self.__timerlat_err = tempfile.SpooledTemporaryFile(mode='w+b')
|
||||
@@ -77,16 +252,82 @@ class Timerlat(rtevalModulePrototype):
|
||||
os.kill(self.__timerlat_process.pid, signal.SIGINT)
|
||||
time.sleep(2)
|
||||
|
||||
- self._setFinished()
|
||||
- self.__started = False
|
||||
-
|
||||
- def MakeReport(self):
|
||||
+ # Parse histogram output
|
||||
self.__timerlat_out.seek(0)
|
||||
for line in self.__timerlat_out:
|
||||
line = bytes.decode(line)
|
||||
- print(line)
|
||||
+
|
||||
+ # Skip any blank lines
|
||||
+ if not line:
|
||||
+ continue
|
||||
+
|
||||
+ if line.startswith('#'):
|
||||
+ if line.startswith('# Duration:'):
|
||||
+ duration = line.split()[2]
|
||||
+ duration += line.split()[3]
|
||||
+ self.__timerlatdata['system'].duration = duration
|
||||
+ continue
|
||||
+ elif line.startswith('Index'):
|
||||
+ #print(line)
|
||||
+ continue
|
||||
+ elif line.startswith('over:'):
|
||||
+ #print(line)
|
||||
+ continue
|
||||
+ elif line.startswith('count:'):
|
||||
+ #print(line)
|
||||
+ continue
|
||||
+ elif line.startswith('min:'):
|
||||
+ #print(line)
|
||||
+ continue
|
||||
+ elif line.startswith('avg:'):
|
||||
+ #print(line)
|
||||
+ continue
|
||||
+ elif line.startswith('max:'):
|
||||
+ #print(line)
|
||||
+ continue
|
||||
+ else:
|
||||
+ pass
|
||||
+ #print(line)
|
||||
+
|
||||
+ vals = line.split()
|
||||
+ if not vals:
|
||||
+ # If we don't have any values, don't try parsing
|
||||
+ continue
|
||||
+ try:
|
||||
+ # The index corresponds to the bucket number
|
||||
+ index = int(vals[0])
|
||||
+ except:
|
||||
+ self._log(Log.DEBUG, f'timerlat: unexpected output: {line}')
|
||||
+ continue
|
||||
+
|
||||
+ for i, core in enumerate(self.__cpus):
|
||||
+ self.__timerlatdata[core].bucket(index, int(vals[i*3+1]),
|
||||
+ int(vals[i*3+2]),
|
||||
+ int(vals[i*3+3]))
|
||||
+ self.__timerlatdata['system'].bucket(index, int(vals[i*3+1]),
|
||||
+ int(vals[i*3+2]),
|
||||
+ int(vals[i*3+3]))
|
||||
+ # Generate statistics for each RunData object
|
||||
+ for n in list(self.__timerlatdata.keys()):
|
||||
+ self.__timerlatdata[n].reduce()
|
||||
+
|
||||
self.__timerlat_out.close()
|
||||
|
||||
+ self._setFinished()
|
||||
+ self.__started = False
|
||||
+
|
||||
+ def MakeReport(self):
|
||||
+ rep_n = libxml2.newNode('timerlat')
|
||||
+ rep_n.newProp('command_line', ' '.join(self.__cmd))
|
||||
+
|
||||
+ rep_n.addChild(self.__timerlatdata['system'].MakeReport())
|
||||
+ for thr in self.__cpus:
|
||||
+ if str(thr) not in self.__timerlatdata:
|
||||
+ continue
|
||||
+ rep_n.addChild(self.__timerlatdata[str(thr)].MakeReport())
|
||||
+
|
||||
+ return rep_n
|
||||
+
|
||||
|
||||
def ModuleInfo():
|
||||
""" Required measurement module information """
|
||||
@@ -97,7 +338,10 @@ def ModuleParameters():
|
||||
""" default parameters """
|
||||
return {"priority": {"descr": "Run rtla timerlat with this priority",
|
||||
"default": 95,
|
||||
- "metavar": "PRIO" }
|
||||
+ "metavar": "PRIO" },
|
||||
+ "buckets": {"descr": "Number of buckets",
|
||||
+ "default": 3500,
|
||||
+ "metavar": "NUM"}
|
||||
}
|
||||
|
||||
def create(params, logger):
|
||||
@@ -107,6 +351,10 @@ def create(params, logger):
|
||||
if __name__ == '__main__':
|
||||
from rteval.rtevalConfig import rtevalConfig
|
||||
|
||||
+ if os.getuid() != 0:
|
||||
+ print("Must be root to run timerlat!")
|
||||
+ sys.exit(1)
|
||||
+
|
||||
l = Log()
|
||||
l.SetLogVerbosity(Log.INFO|Log.DEBUG|Log.ERR|Log.WARN)
|
||||
|
||||
@@ -128,4 +376,8 @@ if __name__ == '__main__':
|
||||
tl._WorkloadTask()
|
||||
time.sleep(RUNTIME)
|
||||
tl._WorkloadCleanup()
|
||||
- tl.MakeReport()
|
||||
+ rep_n = tl.MakeReport()
|
||||
+
|
||||
+ xml = libxml2.newDoc('1.0')
|
||||
+ xml.setRootElement(rep_n)
|
||||
+ xml.saveFormatFileEnc('-', 'UTF-8', 1)
|
||||
--
|
||||
2.44.0
|
||||
|
33
rteval.spec
33
rteval.spec
@ -1,6 +1,6 @@
|
||||
Name: rteval
|
||||
Version: 3.7
|
||||
Release: 5%{?dist}
|
||||
Release: 6%{?dist}
|
||||
Summary: Utility to evaluate system suitability for RT Linux
|
||||
|
||||
Group: Development/Tools
|
||||
@ -41,7 +41,18 @@ 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
|
||||
Patch9: rteval-Implement-initial-dmidecode-support.patch
|
||||
|
||||
Patch10: rteval-Add-relative-cpulists-for-loads.patch
|
||||
Patch11: rteval-Cyclictest.py-Make-standalone-file-work-again.patch
|
||||
Patch12: rteval-Add-rtla-timerlat-as-a-measurement-module.patch
|
||||
Patch13: rteval-cyclictest.py-Fix-the-description-in-the-xml-.patch
|
||||
Patch14: rteval-cyclictest.py-Remove-unused-method-sample.patch
|
||||
Patch15: rteval-cyclictest.py-Fix-the-median-calculation.patch
|
||||
Patch16: rteval-timerlat.py-Add-statistics-and-generate-xml.patch
|
||||
Patch17: rteval-Add-summary-reporting-for-timerlat.patch
|
||||
Patch18: rteval-Generate-raw-histogram-data-for-an-existing-t.patch
|
||||
Patch19: rteval-Make-output-work-with-new-dmiinfo-format.patch
|
||||
Patch20: rteval-cyclictest.py-reduce-Fix-exception-with-missi.patch
|
||||
Patch21: rteval-Use-get-instead-of-setdefault-for-calculating.patch
|
||||
|
||||
%description
|
||||
The rteval script is a utility for measuring various aspects of
|
||||
@ -63,6 +74,18 @@ to the screen.
|
||||
%patch 7 -p1
|
||||
%patch 8 -p1
|
||||
%patch 9 -p1
|
||||
%patch 10 -p1
|
||||
%patch 11 -p1
|
||||
%patch 12 -p1
|
||||
%patch 13 -p1
|
||||
%patch 14 -p1
|
||||
%patch 15 -p1
|
||||
%patch 16 -p1
|
||||
%patch 17 -p1
|
||||
%patch 18 -p1
|
||||
%patch 19 -p1
|
||||
%patch 20 -p1
|
||||
%patch 21 -p1
|
||||
|
||||
%build
|
||||
%{__python3} setup.py build
|
||||
@ -83,7 +106,11 @@ to the screen.
|
||||
%{_bindir}/rteval
|
||||
|
||||
%changelog
|
||||
* Thu Apr 05 2024 Tomas Glozar <tglozar@redhat.com> - 3.7-5
|
||||
* Mon May 06 2024 John Kacur <jkacur@redhat.com> - 3.7-6
|
||||
- Add timerlat as a measurement module
|
||||
Resolves: RHEL-28058
|
||||
|
||||
* Thu Apr 04 2024 Tomas Glozar <tglozar@redhat.com> - 3.7-5
|
||||
- Replace python-dmidecode with initial dmidecode output parsing
|
||||
Resolves: RHEL-30158
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user