scripts: sysmon: add io metrics of processes
BUG=b:255782067
TEST=None
Change-Id: I8b87e8a357c0c48d5b1cf9326f29db84527a480a
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/4242822
Reviewed-by: Sergey Fetisov <sfetisov@google.com>
Commit-Queue: Congbin Guo <guocb@chromium.org>
Auto-Submit: Congbin Guo <guocb@chromium.org>
Tested-by: Congbin Guo <guocb@chromium.org>
diff --git a/scripts/sysmon/proc_metrics.py b/scripts/sysmon/proc_metrics.py
index 2488c29..34d231c 100644
--- a/scripts/sysmon/proc_metrics.py
+++ b/scripts/sysmon/proc_metrics.py
@@ -29,6 +29,28 @@
"proc/cpu_times",
description="Accumulated CPU time in each specific mode of processes.",
)
+_read_count_metric = metrics.CounterMetric(
+ "proc/read/count",
+ description="Accumulated read operation count of processes.",
+)
+_read_bytes_metric = metrics.CounterMetric(
+ "proc/read/bytes", description="Accumulated read bytes of processes."
+)
+_read_chars_metric = metrics.CounterMetric(
+ "proc/read/chars",
+ description="Accumulated buffered read bytes of processes.",
+)
+_write_count_metric = metrics.CounterMetric(
+ "proc/write/count",
+ description="Accumulated write operation count of processes.",
+)
+_write_bytes_metric = metrics.CounterMetric(
+ "proc/write/bytes", description="Accumulated write bytes of processes."
+)
+_write_chars_metric = metrics.CounterMetric(
+ "proc/write/chars",
+ description="Accumulated buffered write bytes of processes.",
+)
def collect_proc_info():
@@ -42,6 +64,7 @@
# We need to store some per process metrics of last run in order to
# calculate the detla and aggregate them.
old_cpu_times = {}
+ old_io_counters = {}
def __init__(self):
self._metrics = [
@@ -114,11 +137,14 @@
def collect(self):
new_cpu_times = {}
+ new_io_counters = {}
for proc in psutil.process_iter():
new_cpu_times[proc.pid] = proc.cpu_times()
+ new_io_counters[proc.pid] = proc.io_counters()
self._collect_proc(proc)
self._flush()
_ProcessMetricsCollector.old_cpu_times = new_cpu_times
+ _ProcessMetricsCollector.old_io_counters = new_io_counters
def _collect_proc(self, proc):
for metric in self._metrics:
@@ -153,6 +179,7 @@
self._thread_count = 0
self._cpu_percent = 0
self._cpu_times = _CPUTimes()
+ self._io_counters = _IOCounters()
def add(self, proc):
"""Do metric collection for the given process.
@@ -169,6 +196,10 @@
proc.cpu_times()
) - _ProcessMetricsCollector.old_cpu_times.get(proc.pid)
+ self._io_counters += _IOCounters(
+ proc.io_counters()
+ ) - _ProcessMetricsCollector.old_io_counters.get(proc.pid)
+
return True
def flush(self):
@@ -190,6 +221,26 @@
)
self._cpu_times = _CPUTimes()
+ _read_count_metric.increment_by(
+ self._io_counters.read_count, fields=self._fields
+ )
+ _read_bytes_metric.increment_by(
+ self._io_counters.read_bytes, fields=self._fields
+ )
+ _read_chars_metric.increment_by(
+ self._io_counters.read_chars, fields=self._fields
+ )
+ _write_count_metric.increment_by(
+ self._io_counters.write_count, fields=self._fields
+ )
+ _write_bytes_metric.increment_by(
+ self._io_counters.write_bytes, fields=self._fields
+ )
+ _write_chars_metric.increment_by(
+ self._io_counters.write_chars, fields=self._fields
+ )
+ self._io_counters = _IOCounters()
+
class _CPUTimes(object):
"""A container for CPU times metrics."""
@@ -345,3 +396,81 @@
"""
cmdline = proc.cmdline()
return proc.name() == "podman" and len(cmdline) > 1 and cmdline[1] == subcmd
+
+
+class _CPUTimes(object):
+ """A container for CPU times metrics."""
+
+ def __init__(self, v=None):
+ self.system = v.system if v else 0
+ self.user = v.user if v else 0
+ self.iowait = v.iowait if v else 0
+ self.children_system = v.children_system if v else 0
+ self.children_user = v.children_user if v else 0
+
+ def __sub__(self, rhs):
+ if not rhs:
+ return self
+
+ r = _CPUTimes()
+ r.system = self.system - rhs.system
+ r.user = self.user - rhs.user
+ r.iowait = self.iowait - rhs.iowait
+ r.children_system = self.children_system - rhs.children_system
+ r.children_user = self.children_user - rhs.children_user
+ return r
+
+ def __iadd__(self, rhs):
+ if not rhs:
+ return self
+
+ self.system += rhs.system
+ self.user += rhs.user
+ self.iowait += rhs.iowait
+ self.children_system += rhs.children_system
+ self.children_user += rhs.children_user
+ return self
+
+ def asdict(self):
+ return {
+ "system": self.system,
+ "user": self.user,
+ "iowait": self.iowait,
+ "children_system": self.children_system,
+ "children_user": self.children_user,
+ }
+
+
+class _IOCounters(object):
+ """A container for I/O counter metrics."""
+
+ def __init__(self, v=None):
+ self.read_count = v.read_count if v else 0
+ self.read_bytes = v.read_bytes if v else 0
+ self.read_chars = v.read_chars if v else 0
+ self.write_count = v.write_count if v else 0
+ self.write_bytes = v.write_bytes if v else 0
+ self.write_chars = v.write_chars if v else 0
+
+ def __sub__(self, rhs):
+ if not rhs:
+ return self
+
+ r = _IOCounters()
+ r.read_count = self.read_count - rhs.read_count
+ r.read_bytes = self.read_bytes - rhs.read_bytes
+ r.read_chars = self.read_chars - rhs.read_chars
+ r.write_count = self.write_count - rhs.write_count
+ r.write_bytes = self.write_bytes - rhs.write_bytes
+ r.write_chars = self.write_chars - rhs.write_chars
+ return r
+
+ def __iadd__(self, rhs):
+ if not rhs:
+ return self
+
+ self.read_count += rhs.read_count
+ self.read_bytes += rhs.read_bytes
+ self.write_count += rhs.write_count
+ self.write_bytes += rhs.write_bytes
+ return self