SoW-2020-0002: Trace Hit Counters: trigger error reporting integration
[lttng-tools.git] / doc / examples / trigger-on-event / performance / bt_plugin_plot.py
1 import bt2
2 import itertools
3 import matplotlib.pyplot as plt
4 import sys
5 import statistics
6 import csv
7 from collections import defaultdict
8
9
10 class DataLogger(object):
11 def __init__(self, name="Untitled"):
12 self._name = name
13
14 def get_name(self):
15 return self._name
16
17 def get_x_data(self):
18 raise NotImplementedError
19
20 def get_y_data(self):
21 raise NotImplementedError
22
23 def received_event(self, ts, event):
24 raise NotImplementedError
25
26
27 class DurationDataLogger(DataLogger):
28 """
29 This class allow to create a duration histogram for the given pair of
30 event and unique tuple key generator.
31
32 """
33 def __init__(self, start_event, end_event, *args, **kwargs):
34 super(DurationDataLogger, self).__init__(*args, **kwargs)
35
36 (self._event_start, self._start_fields) = start_event
37 (self._event_end, self._end_fields) = end_event
38
39 self._durations = []
40 self._pair = dict()
41
42 def get_x_data(self):
43 return self._durations
44
45 def received_event(self, ts, event):
46 if event.name == self._event_start:
47 key = ()
48 for field in self._start_fields:
49 value = event.payload_field[str(field)]
50 key = key + (value,)
51 self._pair[key] = ts
52 return
53
54 if event.name == self._event_end:
55 key = ()
56 for field in self._end_fields:
57 value = event.payload_field[str(field)]
58 key = key + (value,)
59
60 if key not in self._pair:
61 print("unmatched end event")
62 return
63
64 start_ts = self._pair[key]
65 duration = (ts - start_ts) / 1000000.0
66 self._durations.append(duration)
67
68 class DurationCSVDataLogger(DataLogger):
69 """
70 This class allow to create a duration histogram for the given csv.
71 """
72 def __init__(self, filepath, *args, **kwargs):
73 super(DurationCSVDataLogger, self).__init__(*args, **kwargs)
74
75 self._filepath = filepath
76
77
78 self._durations = []
79 with open(filepath, newline='') as file:
80 reader = csv.reader(file, quoting=csv.QUOTE_NONE)
81 next(reader)
82 for row in reader:
83 self._durations.append(float(row[0]))
84
85 def get_x_data(self):
86 return self._durations
87
88 def received_event(self, ts, event):
89 return
90
91
92 class Plot(object):
93 def __init__(
94 self, loggers, title="Untitled", x_label="Untitled", y_label="Untitled"
95 ):
96 self._loggers = loggers
97 self._title = title
98 self._x_label = x_label
99 self._y_label = y_label
100
101 def received_event(self, ts, event):
102 for logger in self._loggers:
103 logger.received_event(ts, event)
104
105 def plot(self):
106 raise NotImplementedError
107
108 def generate_csv(self):
109 raise NotImplementedError
110
111 @staticmethod
112 def _format_filename(title, ext):
113 title = title.lower()
114 title = "".join("-" if not c.isalnum() else c for c in title)
115 title = "".join(
116 ["".join(j) if i != "-" else i for (i, j) in itertools.groupby(title)]
117 )
118 return f"{title}.{ext}"
119
120 class HistogramPlot(Plot):
121 def __init__(self, *args, **kwargs):
122 super(HistogramPlot, self).__init__(*args, **kwargs)
123
124 @staticmethod
125 def get_statistics_header():
126 return ["minimum", "maximum", "mean", "pstdev", "count"]
127
128 @staticmethod
129 def get_statistics(samples):
130 stats = []
131 stats.append('%f' % min(samples))
132 stats.append('%f' % max(samples))
133 stats.append('%f' % statistics.mean(samples))
134 stats.append('%f' % statistics.pstdev(samples))
135 stats.append('%d' % len(samples))
136 return stats
137
138 def plot(self):
139 sys.argv = ['']
140 complete_set = [];
141 logger_statistic = defaultdict(dict)
142
143 figure = plt.figure()
144 plt.title(self._title)
145 plt.xlabel(self._x_label, figure=figure)
146 plt.ylabel(self._y_label, figure=figure)
147 plt.yscale('log', nonposy='clip')
148
149 table_rows_label = []
150 table_celltext = []
151 for logger in self._loggers:
152 x = logger.get_x_data()
153 table_rows_label.append(logger.get_name())
154 table_celltext.append(HistogramPlot.get_statistics(x))
155
156 complete_set +=x;
157 plt.hist(x, bins='auto', alpha=0.5, figure=figure, label=logger.get_name())
158
159 table_rows_label.append("all")
160 table_celltext.append(HistogramPlot.get_statistics(complete_set))
161 the_table = plt.table(cellText=table_celltext,
162 rowLabels=table_rows_label,
163 colLabels=HistogramPlot.get_statistics_header(),
164 loc='bottom',
165 bbox=[0.0,-0.45,1,.28],
166 )
167
168 the_table.auto_set_font_size(False)
169 the_table.set_fontsize(8)
170
171 plt.subplots_adjust(bottom=0.20)
172 plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
173 plt.savefig(Plot._format_filename(self._title, "pdf"), bbox_inches="tight")
174
175 def generate_csv(self):
176 for logger in self._loggers:
177 x_data = logger.get_x_data()
178 with open(Plot._format_filename(self._title, "%s.csv" % logger.get_name()), 'w', newline='') as export:
179 wr = csv.writer(export, quoting=csv.QUOTE_NONE)
180 wr.writerow([self._x_label])
181 for x in x_data:
182 wr.writerow([x])
183
184
185 @bt2.plugin_component_class
186 class PlotSink(bt2._UserSinkComponent):
187 def __init__(self, config, params, obj):
188 self._plots = []
189
190 if "histograms" in params:
191 for plot in params["histograms"]:
192 self._plots.append(PlotSink.create_histogram(plot))
193
194 self._add_input_port("in")
195
196 def _user_consume(self):
197 msg = next(self._iter)
198 if type(msg) in [
199 bt2._PacketBeginningMessageConst,
200 bt2._PacketEndMessageConst,
201 bt2._StreamBeginningMessageConst,
202 bt2._StreamEndMessageConst,
203 ]:
204 return
205
206 ts = msg.default_clock_snapshot.value
207 for plot in self._plots:
208 plot.received_event(ts, msg.event)
209
210 def _user_finalize(self):
211 {plot.plot() for plot in self._plots}
212 {plot.generate_csv () for plot in self._plots}
213 return
214
215 def _user_graph_is_configured(self):
216 self._iter = self._create_message_iterator(self._input_ports["in"])
217
218 @staticmethod
219 def create_histogram(params):
220 loggers = []
221 for logger in params[3]:
222 if logger[0] == "duration":
223 logger = PlotSink.create_duration_logger(logger)
224 elif logger[0] == "duration-csv":
225 logger = PlotSink.create_duration_logger_csv(logger)
226 else:
227 raise ValueError
228
229 loggers.append(logger)
230
231 title = str(params[0])
232 x_label = str(params[1])
233 y_label = str(params[2])
234
235 return HistogramPlot(loggers, title=title, x_label=x_label,
236 y_label=y_label)
237
238 @staticmethod
239 def create_duration_logger(params):
240 return DurationDataLogger(
241 (str(params[2]), params[3]),
242 (str(params[4]), params[5]),
243 name=str(params[1]),
244 )
245
246 def create_duration_logger_csv(params):
247 return DurationCSVDataLogger(
248 str(params[2]),
249 name=str(params[1]),
250 )
251
252
253 bt2.register_plugin(
254 module_name=__name__,
255 name="plot",
256 description="Plot Sink",
257 author="EfficiOS inc.",
258 license="GPL",
259 version=(1, 0, 0),
260 )
This page took 0.034957 seconds and 5 git commands to generate.