Tue, Oct 23, 2018 at 01:56:49PM CEST, olichtne(a)redhat.com wrote:
From: Ondrej Lichtner <olichtne(a)redhat.com>
This test module can be used to periodically sample the /proc/stat file
for statistics and report back a list of differences between the
individual samples as well as the raw data.
Can be used to calculate per-cpu and system wide cpu utilization.
Currently the test module samples until interrupted, so it should be run
in the background and stopped with a job.kill(signal.SIGINT) call.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Tests/CPUStatMonitor.py | 113 +++++++++++++++++++++++++++++++++++
1 file changed, 113 insertions(+)
create mode 100644 lnst/Tests/CPUStatMonitor.py
diff --git a/lnst/Tests/CPUStatMonitor.py b/lnst/Tests/CPUStatMonitor.py
new file mode 100644
index 0000000..9b4a104
--- /dev/null
+++ b/lnst/Tests/CPUStatMonitor.py
@@ -0,0 +1,113 @@
+import re
+import time
+import signal
+from time import sleep
+from lnst.Common.Parameters import IntParam
+from lnst.Tests.BaseTestModule import BaseTestModule, TestModuleError,
InterruptException
+
+def sigint_handler(signum, frame):
+ raise InterruptException()
+
+class CPUStatMonitor(BaseTestModule):
+ interval = IntParam(default=1000)
I guess this value is in miliseconds, correct? Some note about the value
meaning would be good.
+
+ def run(self):
+ self._res_data = {}
+
+ raw_samples = []
+ try:
+ old_handler = signal.signal(signal.SIGINT, sigint_handler)
If the exception is raised in signal.signal the old_handler will be
undefined and 'finally' would attempt to use the undefined value to
reset the signal handler. Not quite sure if signal() can raise an
exception however (checked the python3 docs and it's only possible if
threads are enabled). I just think about moving this before try-except
block.
>+ with open("/proc/stat") as stat:
>+ while True:
>+ stat.seek(0)
>+ timestamp = time.time()
>+ stat_lines = "".join(stat.readlines())
>+ raw_samples.append({
>+ "timestamp": timestamp,
>+ "stat": stat_lines
>+ })
>+ sleep(self.params.interval / float(1000))
>+ except InterruptException:
>+ pass
>+ finally:
>+ signal.signal(signal.SIGINT, old_handler)
>+
>+ self._res_data["raw_data"] = raw_samples
>+ self._res_data["data"] = self._process_samples(raw_samples)
>+
>+ return True
>+
>+ def _process_samples(self, samples):
>+ result = []
>+ prev_sample = None
>+ for sample in samples:
>+ if prev_sample is not None:
>+ parsed_prev = self._parse_stat_lines(prev_sample["stat"])
>+ parsed_cur = self._parse_stat_lines(sample["stat"])
>+
>+ interval = self._subtract_nested_dicts(parsed_cur, parsed_prev)
>+ interval["duration"] = (sample["timestamp"] -
>+ prev_sample["timestamp"])
>+
>+ result.append(interval)
>+
>+ prev_sample = sample
>+ return result
>+
>+ def _subtract_nested_dicts(self, first, second):
>+ result = {}
>+ for key, val in first.items():
>+ if isinstance(val, dict):
>+ result[key] = self._subtract_nested_dicts(val, second[key])
>+ else:
>+ result[key] = val - second[key]
>+ return result
>+
>+ def _parse_stat_lines(self, stat):
>+ result = {}
>+ for line in stat.split("\n"):
>+ cpu_data = self._parse_cpu_stats(line)
>+ if cpu_data:
>+ result[cpu_data[0]] = cpu_data[1]
>+ continue
>+
>+ intr_data = self._parse_intr_stats(line)
>+ if intr_data:
>+ result[intr_data[0]] = intr_data[1]
>+ continue
>+
>+ m = re.match(r"^(.*?) (\d+)$", line)
>+ if m:
>+ result[m.group(1)] = int(m.group(2))
>+ return result
>+
>+ def _parse_cpu_stats(self, stat_line):
>+ result = {}
>+ m = re.match(r"^(cpu\d*)\s+(\d+) (\d+) (\d+) (\d+) (\d+) (\d+) (\d+)
(\d+) (\d+) (\d+)$",
>+ stat_line)
>+ if m:
>+ cpu = m.group(1)
>+ result["user"] = int(m.group(2))
>+ result["nice"] = int(m.group(3))
>+ result["system"] = int(m.group(4))
>+ result["idle"] = int(m.group(5))
>+ result["iowait"] = int(m.group(6))
>+ result["irq"] = int(m.group(7))
>+ result["softirq"] = int(m.group(8))
>+ result["steal"] = int(m.group(9))
>+ result["guest"] = int(m.group(10))
>+ result["guest_nice"] = int(m.group(11))
>+ return cpu, result
>+ else:
>+ return None
>+
>+ def _parse_intr_stats(self, stat_line):
>+ result = {}
>+ m = re.match(r"^(intr|softirq) (\d+) (.*)$", stat_line)
>+ if m:
>+ result["total"] = int(m.group(2))
>+ for i, irq in enumerate(m.group(3).split(" ")):
>+ result[i] = int(irq)
>+ return m.group(1), result
>+ else:
>+ return None
>--
>2.19.1
>_______________________________________________
>LNST-developers mailing list -- lnst-developers(a)lists.fedorahosted.org
>To unsubscribe send an email to lnst-developers-leave(a)lists.fedorahosted.org
>Fedora Code of Conduct:
https://getfedora.org/code-of-conduct.html
>List Guidelines:
https://fedoraproject.org/wiki/Mailing_list_guidelines
>List Archives:
https://lists.fedorahosted.org/archives/list/lnst-developers@lists.fedora...