SoW-2020-0002: Trace Hit Counters: trigger error reporting integration
[lttng-tools.git] / doc / examples / trigger-on-event / performance / bt_plugin_plot.py
diff --git a/doc/examples/trigger-on-event/performance/bt_plugin_plot.py b/doc/examples/trigger-on-event/performance/bt_plugin_plot.py
new file mode 100644 (file)
index 0000000..626a9a1
--- /dev/null
@@ -0,0 +1,260 @@
+import bt2
+import itertools
+import matplotlib.pyplot as plt
+import sys
+import statistics
+import csv
+from collections import defaultdict
+
+
+class DataLogger(object):
+    def __init__(self, name="Untitled"):
+        self._name = name
+
+    def get_name(self):
+        return self._name
+
+    def get_x_data(self):
+        raise NotImplementedError
+
+    def get_y_data(self):
+        raise NotImplementedError
+
+    def received_event(self, ts, event):
+        raise NotImplementedError
+
+
+class DurationDataLogger(DataLogger):
+    """
+        This class allow to create a duration histogram for the given pair of
+        event and unique tuple key generator.
+
+    """
+    def __init__(self, start_event, end_event, *args, **kwargs):
+        super(DurationDataLogger, self).__init__(*args, **kwargs)
+
+        (self._event_start, self._start_fields) = start_event
+        (self._event_end, self._end_fields) = end_event
+
+        self._durations = []
+        self._pair = dict()
+
+    def get_x_data(self):
+        return self._durations
+
+    def received_event(self, ts, event):
+        if event.name == self._event_start:
+            key = ()
+            for field in self._start_fields:
+                value = event.payload_field[str(field)]
+                key = key + (value,)
+            self._pair[key] = ts
+            return
+
+        if event.name == self._event_end:
+            key = ()
+            for field in self._end_fields:
+                value = event.payload_field[str(field)]
+                key = key + (value,)
+
+            if key not in self._pair:
+                print("unmatched end event")
+                return
+
+            start_ts = self._pair[key]
+            duration = (ts - start_ts) / 1000000.0
+            self._durations.append(duration)
+
+class DurationCSVDataLogger(DataLogger):
+    """
+        This class allow to create a duration histogram for the given csv.
+    """
+    def __init__(self, filepath, *args, **kwargs):
+        super(DurationCSVDataLogger, self).__init__(*args, **kwargs)
+
+        self._filepath = filepath
+
+
+        self._durations = []
+        with open(filepath, newline='') as file:
+            reader = csv.reader(file, quoting=csv.QUOTE_NONE)
+            next(reader)
+            for row in reader:
+                self._durations.append(float(row[0]))
+
+    def get_x_data(self):
+        return self._durations
+
+    def received_event(self, ts, event):
+        return
+
+
+class Plot(object):
+    def __init__(
+        self, loggers, title="Untitled", x_label="Untitled", y_label="Untitled"
+    ):
+        self._loggers = loggers
+        self._title = title
+        self._x_label = x_label
+        self._y_label = y_label
+
+    def received_event(self, ts, event):
+        for logger in self._loggers:
+            logger.received_event(ts, event)
+
+    def plot(self):
+        raise NotImplementedError
+
+    def generate_csv(self):
+        raise NotImplementedError
+
+    @staticmethod
+    def _format_filename(title, ext):
+        title = title.lower()
+        title = "".join("-" if not c.isalnum() else c for c in title)
+        title = "".join(
+            ["".join(j) if i != "-" else i for (i, j) in itertools.groupby(title)]
+        )
+        return f"{title}.{ext}"
+
+class HistogramPlot(Plot):
+    def __init__(self, *args, **kwargs):
+        super(HistogramPlot, self).__init__(*args, **kwargs)
+
+    @staticmethod
+    def get_statistics_header():
+        return ["minimum", "maximum", "mean", "pstdev", "count"]
+
+    @staticmethod
+    def get_statistics(samples):
+        stats = []
+        stats.append('%f' % min(samples))
+        stats.append('%f' % max(samples))
+        stats.append('%f' % statistics.mean(samples))
+        stats.append('%f' % statistics.pstdev(samples))
+        stats.append('%d' % len(samples))
+        return stats
+
+    def plot(self):
+        sys.argv = ['']
+        complete_set = [];
+        logger_statistic = defaultdict(dict)
+
+        figure = plt.figure()
+        plt.title(self._title)
+        plt.xlabel(self._x_label, figure=figure)
+        plt.ylabel(self._y_label, figure=figure)
+        plt.yscale('log', nonposy='clip')
+
+        table_rows_label = []
+        table_celltext = []
+        for logger in self._loggers:
+            x = logger.get_x_data()
+            table_rows_label.append(logger.get_name())
+            table_celltext.append(HistogramPlot.get_statistics(x))
+
+            complete_set +=x;
+            plt.hist(x, bins='auto', alpha=0.5, figure=figure, label=logger.get_name())
+
+        table_rows_label.append("all")
+        table_celltext.append(HistogramPlot.get_statistics(complete_set))
+        the_table = plt.table(cellText=table_celltext,
+                rowLabels=table_rows_label,
+                colLabels=HistogramPlot.get_statistics_header(),
+                loc='bottom',
+                bbox=[0.0,-0.45,1,.28],
+                )
+
+        the_table.auto_set_font_size(False)
+        the_table.set_fontsize(8)
+
+        plt.subplots_adjust(bottom=0.20)
+        plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
+        plt.savefig(Plot._format_filename(self._title, "pdf"), bbox_inches="tight")
+
+    def generate_csv(self):
+        for logger in self._loggers:
+            x_data = logger.get_x_data()
+            with open(Plot._format_filename(self._title, "%s.csv" % logger.get_name()), 'w', newline='') as export:
+                wr = csv.writer(export, quoting=csv.QUOTE_NONE)
+                wr.writerow([self._x_label])
+                for x in x_data:
+                    wr.writerow([x])
+
+
+@bt2.plugin_component_class
+class PlotSink(bt2._UserSinkComponent):
+    def __init__(self, config, params, obj):
+        self._plots = []
+
+        if "histograms" in params:
+            for plot in params["histograms"]:
+                self._plots.append(PlotSink.create_histogram(plot))
+
+        self._add_input_port("in")
+
+    def _user_consume(self):
+        msg = next(self._iter)
+        if type(msg) in [
+            bt2._PacketBeginningMessageConst,
+            bt2._PacketEndMessageConst,
+            bt2._StreamBeginningMessageConst,
+            bt2._StreamEndMessageConst,
+        ]:
+            return
+
+        ts = msg.default_clock_snapshot.value
+        for plot in self._plots:
+            plot.received_event(ts, msg.event)
+
+    def _user_finalize(self):
+            {plot.plot() for plot in self._plots}
+            {plot.generate_csv () for plot in self._plots}
+            return
+
+    def _user_graph_is_configured(self):
+        self._iter = self._create_message_iterator(self._input_ports["in"])
+
+    @staticmethod
+    def create_histogram(params):
+        loggers = []
+        for logger in params[3]:
+            if logger[0] == "duration":
+                logger = PlotSink.create_duration_logger(logger)
+            elif logger[0] == "duration-csv":
+                logger = PlotSink.create_duration_logger_csv(logger)
+            else:
+                raise ValueError
+
+            loggers.append(logger)
+
+        title = str(params[0])
+        x_label = str(params[1])
+        y_label = str(params[2])
+
+        return HistogramPlot(loggers, title=title, x_label=x_label,
+                y_label=y_label)
+
+    @staticmethod
+    def create_duration_logger(params):
+        return DurationDataLogger(
+            (str(params[2]), params[3]),
+            (str(params[4]), params[5]),
+            name=str(params[1]),
+        )
+
+    def create_duration_logger_csv(params):
+        return DurationCSVDataLogger(
+            str(params[2]),
+            name=str(params[1]),
+        )
+
+
+bt2.register_plugin(
+    module_name=__name__,
+    name="plot",
+    description="Plot Sink",
+    author="EfficiOS inc.",
+    license="GPL",
+    version=(1, 0, 0),
+)
This page took 0.028425 seconds and 5 git commands to generate.