telemetry: linux telemetry with perf-stat
[csit.git] / resources / tools / telemetry / bundle_perf_stat.py
diff --git a/resources/tools/telemetry/bundle_perf_stat.py b/resources/tools/telemetry/bundle_perf_stat.py
new file mode 100644 (file)
index 0000000..038e86e
--- /dev/null
@@ -0,0 +1,109 @@
+# Copyright (c) 2022 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Perf Stat performance bundle."""
+
+from logging import getLogger
+import sys
+import subprocess
+
+from .constants import Constants
+
+
+class BundlePerfStat:
+    """
+    Creates a Perf stat object. This is the main object for defining a Perf Stat
+    program and interacting with its output.
+
+    Syntax: perf stat [-e <EVENT> | --event=EVENT] [-a] — <command> [<options>]
+    """
+    def __init__(self, program, serializer, hook):
+        """Initialize Bundle Perf Stat event class.
+
+        :param program: events
+        :param serializer: Metric serializer.
+        :param hook: Process ID.
+        :type program: dict
+        :type serializer: Serializer
+        :type hook: int
+        """
+        self.metrics = program[u"metrics"]
+        self.events = program[u"events"]
+        self.api_replies_list = list()
+        self.serializer = serializer
+        self.hook = hook
+
+    def attach(self, duration=1):
+        """
+               Performs perf stat.
+
+               :param duration: Time how long perf stat is collecting data (in
+               seconds). Default value is 1 second.
+               :type duration: int
+               EventCode, UMask, EdgeDetect, AnyThread, Invert, CounterMask
+               """
+        try:
+            self.serializer.create(metrics=self.metrics)
+            for event in self.events:
+                text = subprocess.getoutput(
+                    f"""sudo perf stat -x\; -e\
+                    '{{cpu/event={hex(event[u"EventCode"])},\
+                    umask={hex(event[u"UMask"])}/u}}'\
+                    -a --per-thread\
+                    sleep {duration}"""
+                )
+
+                if text == u"":
+                    getLogger("console_stdout").info(event[u"name"])
+                    continue
+                if u";" not in text:
+                    getLogger("console_stdout").info(
+                        f"Could not get counters for event \"{event[u'name']}\""
+                        f". Is it supported by CPU?"
+                    )
+                    continue
+
+                for line in text.splitlines():
+                    item = dict()
+                    labels = dict()
+                    item[u"name"] = event[u"name"]
+                    item[u"value"] = line.split(";")[1]
+                    labels["thread"] = u"-".join(
+                        line.split(";")[0].split("-")[0:-1]
+                    )
+                    labels["pid"] = line.split(";")[0].split("-")[-1]
+                    labels["name"] = item[u"name"]
+                    item[u"labels"] = labels
+
+                    getLogger("console_stdout").info(item)
+                    self.api_replies_list.append(item)
+
+        except AttributeError:
+            getLogger("console_stderr").error(f"Could not successfully run "
+                                              f"perf stat command.")
+            sys.exit(Constants.err_linux_perf_stat)
+
+    def detach(self):
+        pass
+
+    def fetch_data(self):
+       pass
+
+    def process_data(self):
+        """
+        Post process API replies.
+        """
+        for item in self.api_replies_list:
+            self.serializer.serialize(
+                metric=item[u"name"], labels=item[u"labels"], item=item
+            )