3 import matplotlib
.pyplot
as plt
7 from collections
import defaultdict
10 class DataLogger(object):
11 def __init__(self
, name
="Untitled"):
18 raise NotImplementedError
21 raise NotImplementedError
23 def received_event(self
, ts
, event
):
24 raise NotImplementedError
27 class DurationDataLogger(DataLogger
):
29 This class allow to create a duration histogram for the given pair of
30 event and unique tuple key generator.
33 def __init__(self
, start_event
, end_event
, *args
, **kwargs
):
34 super(DurationDataLogger
, self
).__init
__(*args
, **kwargs
)
36 (self
._event
_start
, self
._start
_fields
) = start_event
37 (self
._event
_end
, self
._end
_fields
) = end_event
43 return self
._durations
45 def received_event(self
, ts
, event
):
46 if event
.name
== self
._event
_start
:
48 for field
in self
._start
_fields
:
49 value
= event
.payload_field
[str(field
)]
54 if event
.name
== self
._event
_end
:
56 for field
in self
._end
_fields
:
57 value
= event
.payload_field
[str(field
)]
60 if key
not in self
._pair
:
61 print("unmatched end event")
64 start_ts
= self
._pair
[key
]
65 duration
= (ts
- start_ts
) / 1000000.0
66 self
._durations
.append(duration
)
68 class DurationCSVDataLogger(DataLogger
):
70 This class allow to create a duration histogram for the given csv.
72 def __init__(self
, filepath
, *args
, **kwargs
):
73 super(DurationCSVDataLogger
, self
).__init
__(*args
, **kwargs
)
75 self
._filepath
= filepath
79 with
open(filepath
, newline
='') as file:
80 reader
= csv
.reader(file, quoting
=csv
.QUOTE_NONE
)
83 self
._durations
.append(float(row
[0]))
86 return self
._durations
88 def received_event(self
, ts
, event
):
94 self
, loggers
, title
="Untitled", x_label
="Untitled", y_label
="Untitled"
96 self
._loggers
= loggers
98 self
._x
_label
= x_label
99 self
._y
_label
= y_label
101 def received_event(self
, ts
, event
):
102 for logger
in self
._loggers
:
103 logger
.received_event(ts
, event
)
106 raise NotImplementedError
108 def generate_csv(self
):
109 raise NotImplementedError
112 def _format_filename(title
, ext
):
113 title
= title
.lower()
114 title
= "".join("-" if not c
.isalnum() else c
for c
in title
)
116 ["".join(j
) if i
!= "-" else i
for (i
, j
) in itertools
.groupby(title
)]
118 return f
"{title}.{ext}"
120 class HistogramPlot(Plot
):
121 def __init__(self
, *args
, **kwargs
):
122 super(HistogramPlot
, self
).__init
__(*args
, **kwargs
)
125 def get_statistics_header():
126 return ["minimum", "maximum", "mean", "pstdev", "count"]
129 def get_statistics(samples
):
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
))
141 logger_statistic
= defaultdict(dict)
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')
149 table_rows_label
= []
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
))
157 plt
.hist(x
, bins
='auto', alpha
=0.5, figure
=figure
, label
=logger
.get_name())
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(),
165 bbox
=[0.0,-0.45,1,.28],
168 the_table
.auto_set_font_size(False)
169 the_table
.set_fontsize(8)
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")
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
])
185 @bt2.plugin_component_class
186 class PlotSink(bt2
._UserSinkComponent
):
187 def __init__(self
, config
, params
, obj
):
190 if "histograms" in params
:
191 for plot
in params
["histograms"]:
192 self
._plots
.append(PlotSink
.create_histogram(plot
))
194 self
._add
_input
_port
("in")
196 def _user_consume(self
):
197 msg
= next(self
._iter
)
199 bt2
._PacketBeginningMessageConst
,
200 bt2
._PacketEndMessageConst
,
201 bt2
._StreamBeginningMessageConst
,
202 bt2
._StreamEndMessageConst
,
206 ts
= msg
.default_clock_snapshot
.value
207 for plot
in self
._plots
:
208 plot
.received_event(ts
, msg
.event
)
210 def _user_finalize(self
):
211 {plot
.plot() for plot
in self
._plots
}
212 {plot
.generate_csv () for plot
in self
._plots
}
215 def _user_graph_is_configured(self
):
216 self
._iter
= self
._create
_message
_iterator
(self
._input
_ports
["in"])
219 def create_histogram(params
):
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
)
229 loggers
.append(logger
)
231 title
= str(params
[0])
232 x_label
= str(params
[1])
233 y_label
= str(params
[2])
235 return HistogramPlot(loggers
, title
=title
, x_label
=x_label
,
239 def create_duration_logger(params
):
240 return DurationDataLogger(
241 (str(params
[2]), params
[3]),
242 (str(params
[4]), params
[5]),
246 def create_duration_logger_csv(params
):
247 return DurationCSVDataLogger(
254 module_name
=__name__
,
256 description
="Plot Sink",
257 author
="EfficiOS inc.",