blob: 412d5546fae494b984e5ffe164c102d6bd1a78ad [file] [log] [blame]
Brendan Jackmane81fdcb2017-01-04 17:10:29 +00001# Copyright 2015-2017 ARM Limited
Javi Merinoc47d2df2015-02-06 16:04:03 +00002#
Javi Merinoaace7c02015-08-10 14:10:47 +01003# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15
Javi Merino7f3c8a62014-06-13 11:38:58 +010016"""Small functions to help with plots"""
17
Javi Merino8625d732015-06-17 17:21:31 +010018# pylint disable=star-args
19
Javi Merino7f3c8a62014-06-13 11:38:58 +010020from matplotlib import pyplot as plt
Javi Merino898783c2015-06-16 11:45:16 +010021import os
22import re
23
Javi Merino435457c2015-08-10 15:59:10 +010024from trappy.wa import SysfsExtractor
Javi Merino7f3c8a62014-06-13 11:38:58 +010025
26GOLDEN_RATIO = 1.618034
27
Javi Merino7f3c8a62014-06-13 11:38:58 +010028def normalize_title(title, opt_title):
Javi Merino8625d732015-06-17 17:21:31 +010029 """Return a string with that contains the title and opt_title if it's
30not the empty string
Javi Merino7f3c8a62014-06-13 11:38:58 +010031
32 See test_normalize_title() for usage
Javi Merino8625d732015-06-17 17:21:31 +010033
Javi Merino7f3c8a62014-06-13 11:38:58 +010034 """
35 if opt_title is not "":
36 title = opt_title + " - " + title
37
38 return title
Javi Merino3a736552014-06-19 19:22:44 +010039
Javi Merino8625d732015-06-17 17:21:31 +010040def set_lim(lim, get_lim_f, set_lim_f):
Javi Merinoa1561272014-06-21 17:49:02 +010041 """Set x or y limitis of the plot
42
43 lim can be a tuple containing the limits or the string "default"
44 or "range". "default" does nothing and uses matplotlib default.
45 "range" extends the current margin by 10%. This is useful since
46 the default xlim and ylim of the plots sometimes make it harder to
47 see data that is just in the margin.
48
49 """
50 if lim == "default":
51 return
52
53 if lim == "range":
Javi Merino8625d732015-06-17 17:21:31 +010054 cur_lim = get_lim_f()
Javi Merinoa1561272014-06-21 17:49:02 +010055 lim = (cur_lim[0] - 0.1 * (cur_lim[1] - cur_lim[0]),
56 cur_lim[1] + 0.1 * (cur_lim[1] - cur_lim[0]))
57
Javi Merino8625d732015-06-17 17:21:31 +010058 set_lim_f(lim[0], lim[1])
Javi Merinoa1561272014-06-21 17:49:02 +010059
60def set_xlim(ax, xlim):
61 """Set the xlim of the plot
62
63 See set_lim() for the details
64 """
65 set_lim(xlim, ax.get_xlim, ax.set_xlim)
66
67def set_ylim(ax, ylim):
68 """Set the ylim of the plot
69
70 See set_lim() for the details
71 """
72 set_lim(ylim, ax.get_ylim, ax.set_ylim)
73
Javi Merino270caac2014-08-12 10:41:38 +010074def pre_plot_setup(width=None, height=None, ncols=1, nrows=1):
Javi Merino3a736552014-06-19 19:22:44 +010075 """initialize a figure
76
Javi Merinofdf44582014-08-12 14:30:05 +010077 width and height are the height and width of each row of plots.
78 For 1x1 plots, that's the height and width of the plot. This
79 function should be called before any calls to plot()
Javi Merinof9ac5782014-06-21 19:02:50 +010080
Javi Merino3a736552014-06-19 19:22:44 +010081 """
82
Javi Merinof9ac5782014-06-21 19:02:50 +010083 if height is None:
84 if width is None:
85 height = 6
86 width = 10
87 else:
88 height = width / GOLDEN_RATIO
89 else:
90 if width is None:
91 width = height * GOLDEN_RATIO
Javi Merino3a736552014-06-19 19:22:44 +010092
Javi Merinofdf44582014-08-12 14:30:05 +010093 height *= nrows
94
Javi Merino270caac2014-08-12 10:41:38 +010095 _, axis = plt.subplots(ncols=ncols, nrows=nrows, figsize=(width, height))
Javi Merino3a736552014-06-19 19:22:44 +010096
Javi Merino0c484262014-08-12 11:55:04 +010097 # Needed for multirow blots to not overlap with each other
98 plt.tight_layout(h_pad=3.5)
99
Javi Merino7f88ae32014-08-11 16:53:35 +0100100 return axis
Javi Merino3a736552014-06-19 19:22:44 +0100101
Javi Merino0aa7cd82015-03-09 18:57:37 +0000102def post_plot_setup(ax, title="", xlabel=None, ylabel=None, xlim="default",
103 ylim="range"):
104 """Set xlabel, ylabel title, xlim and ylim of the plot
Javi Merino3a736552014-06-19 19:22:44 +0100105
Javi Merinoa1561272014-06-21 17:49:02 +0100106 This has to be called after calls to .plot(). The default ylim is
107 to extend it by 10% because matplotlib default makes it hard
108 values that are close to the margins
109
Javi Merino3a736552014-06-19 19:22:44 +0100110 """
111
Javi Merinob839b902014-06-21 19:30:13 +0100112 if xlabel is not None:
Javi Merinoece0ab12014-08-12 16:24:55 +0100113 ax.set_xlabel(xlabel)
Javi Merinof9d43af2014-06-21 16:32:04 +0100114
Javi Merino0aa7cd82015-03-09 18:57:37 +0000115 if ylabel is not None:
116 ax.set_ylabel(ylabel)
117
Javi Merino3a736552014-06-19 19:22:44 +0100118 if title:
Javi Merinofd38c552014-08-11 17:14:52 +0100119 ax.set_title(title)
Javi Merino3a736552014-06-19 19:22:44 +0100120
Javi Merinoa1561272014-06-21 17:49:02 +0100121 set_ylim(ax, ylim)
122 set_xlim(ax, xlim)
Javi Merinod04643f2014-06-21 17:00:16 +0100123
Javi Merino9d9c9b82015-04-29 11:15:11 +0100124def number_freq_plots(runs, map_label):
125 """Calculate the number of plots needed for allfreq plots and frequency
126 histogram plots
127
128 """
129 num_cpu_plots = len(map_label)
130
131 has_devfreq_data = False
132 for run in runs:
133 if len(run.devfreq_in_power.data_frame) > 0:
134 has_devfreq_data = True
135 break
136
137 num_freq_plots = num_cpu_plots
138 if has_devfreq_data:
139 num_freq_plots += 1
140
141 return num_freq_plots
142
Javi Merino1217c652016-08-22 20:04:15 +0100143def plot_temperature(runs, width=None, height=None, ylim="range", tz_id=None):
Javi Merino2919e8d2014-06-26 15:16:05 +0100144 """Plot temperatures
145
Javi Merinoc26a3232015-12-11 18:00:30 +0000146 runs is an array of FTrace() instances. Extract the control_temp
Javi Merino1cc4bfc2014-08-11 16:29:11 +0100147 from the governor data and plot the temperatures reported by the
148 thermal framework. The governor doesn't track temperature when
149 it's off, so the thermal framework trace is more reliable.
Javi Merino2919e8d2014-06-26 15:16:05 +0100150
151 """
152
Javi Merino80114162014-08-08 16:48:32 +0100153 ax = pre_plot_setup(width, height)
154
Javi Merino1cc4bfc2014-08-11 16:29:11 +0100155 for run in runs:
Javi Merino1217c652016-08-22 20:04:15 +0100156 gov_dfr = run.thermal_governor.data_frame
157 if tz_id:
158 gov_dfr = gov_dfr[gov_dfr["thermal_zone_id"] == tz_id]
159
Javi Merino25265832016-08-22 17:37:19 +0100160 try:
Javi Merino1217c652016-08-22 20:04:15 +0100161 current_temp = gov_dfr["current_temperature"]
162 delta_temp = gov_dfr["delta_temperature"]
Javi Merino25265832016-08-22 17:37:19 +0100163 control_series = (current_temp + delta_temp) / 1000
164 except KeyError:
165 control_series = None
Javi Merinofa3ed922015-02-06 18:44:44 +0000166
167 try:
168 run.thermal.plot_temperature(control_temperature=control_series,
Javi Merino1217c652016-08-22 20:04:15 +0100169 ax=ax, legend_label=run.name,
170 tz_id=tz_id)
Javi Merinofa3ed922015-02-06 18:44:44 +0000171 except ValueError:
172 run.thermal_governor.plot_temperature(ax=ax, legend_label=run.name)
Javi Merino80114162014-08-08 16:48:32 +0100173
174 post_plot_setup(ax, title="Temperature", ylim=ylim)
175 plt.legend(loc="best")
Javi Merino2919e8d2014-06-26 15:16:05 +0100176
Javi Merino23dff5d2015-03-09 19:05:46 +0000177def plot_hist(data, ax, title, unit, bins, xlabel, xlim, ylim):
Javi Merinoed977c12014-06-25 17:46:17 +0100178 """Plot a histogram"""
179
180 mean = data.mean()
181 std = data.std()
Javi Merino23dff5d2015-03-09 19:05:46 +0000182 title += " (mean = {:.2f}{}, std = {:.2f})".format(mean, unit, std)
183 xlabel += " ({})".format(unit)
Javi Merinoed977c12014-06-25 17:46:17 +0100184
Javi Merinoed977c12014-06-25 17:46:17 +0100185 data.hist(ax=ax, bins=bins)
Javi Merino0aa7cd82015-03-09 18:57:37 +0000186 post_plot_setup(ax, title=title, xlabel=xlabel, ylabel="count", xlim=xlim,
187 ylim=ylim)
Javi Merino6bf48832014-08-11 17:15:35 +0100188
189def plot_load(runs, map_label, width=None, height=None):
190 """Make a multiplot of all the loads"""
Javi Merinoaf05f312014-08-11 17:47:59 +0100191 num_runs = len(runs)
Javi Merinof9a8ea22015-06-16 17:50:53 +0100192 axis = pre_plot_setup(width=width, height=height, ncols=num_runs, nrows=2)
Javi Merinoaf05f312014-08-11 17:47:59 +0100193
194 if num_runs == 1:
195 axis = [axis]
Javi Merinof9a8ea22015-06-16 17:50:53 +0100196 else:
197 axis = zip(*axis)
Javi Merino6bf48832014-08-11 17:15:35 +0100198
199 for ax, run in zip(axis, runs):
Javi Merinof9a8ea22015-06-16 17:50:53 +0100200 run.plot_load(map_label, title=run.name, ax=ax[0])
201 run.plot_normalized_load(map_label, title=run.name, ax=ax[1])
Javi Merino0c484262014-08-12 11:55:04 +0100202
203def plot_allfreqs(runs, map_label, width=None, height=None):
204 """Make a multicolumn plots of the allfreqs plots of each run"""
205 num_runs = len(runs)
Javi Merino9d9c9b82015-04-29 11:15:11 +0100206 nrows = number_freq_plots(runs, map_label)
207
208 axis = pre_plot_setup(width=width, height=height, nrows=nrows,
Javi Merino0c484262014-08-12 11:55:04 +0100209 ncols=num_runs)
210
211 if num_runs == 1:
Javi Merino94d89c12016-01-05 14:47:07 +0000212 if nrows == 1:
213 axis = [[axis]]
214 else:
215 axis = [axis]
216 elif nrows == 1:
217 axis = [[ax] for ax in axis]
Javi Merino0c484262014-08-12 11:55:04 +0100218 else:
219 axis = zip(*axis)
220
221 for ax, run in zip(axis, runs):
222 run.plot_allfreqs(map_label, ax=ax)
Javi Merino38fd12d2014-08-12 15:02:47 +0100223
224def plot_controller(runs, width=None, height=None):
225 """Make a multicolumn plot of the pid controller of each run"""
226 num_runs = len(runs)
227 axis = pre_plot_setup(width=width, height=height, ncols=num_runs)
228
229 if num_runs == 1:
230 axis = [axis]
231
232 for ax, run in zip(axis, runs):
233 run.pid_controller.plot_controller(title=run.name, ax=ax)
Javi Merinof5cd04b2014-08-12 15:26:22 +0100234
Javi Merino898783c2015-06-16 11:45:16 +0100235def plot_weighted_input_power(runs, actor_order, width=None, height=None):
236 """Make a multicolumn plot of the weighted input power of each run"""
237
238 actor_weights = []
239 for run in runs:
240 run_path = os.path.dirname(run.trace_path)
241 sysfs = SysfsExtractor(run_path)
242
243 thermal_params = sysfs.get_parameters()
244
245 sorted_weights = []
246 for param in sorted(thermal_params):
247 if re.match(r"cdev\d+_weight", param):
248 sorted_weights.append(thermal_params[param])
249
250 actor_weights.append(zip(actor_order, sorted_weights))
251
252 # Do nothing if we don't have actor weights for any run
253 if not any(actor_weights):
254 return
255
256 num_runs = len(runs)
257 axis = pre_plot_setup(width=width, height=height, ncols=num_runs)
258
259 if num_runs == 1:
260 axis = [axis]
261
262 for ax, run, weights in zip(axis, runs, actor_weights):
263 run.thermal_governor.plot_weighted_input_power(weights, title=run.name,
264 ax=ax)
265
Javi Merinof5cd04b2014-08-12 15:26:22 +0100266def plot_input_power(runs, actor_order, width=None, height=None):
267 """Make a multicolumn plot of the input power of each run"""
268 num_runs = len(runs)
269 axis = pre_plot_setup(width=width, height=height, ncols=num_runs)
270
271 if num_runs == 1:
272 axis = [axis]
273
274 for ax, run in zip(axis, runs):
Javi Merino8625d732015-06-17 17:21:31 +0100275 run.thermal_governor.plot_input_power(actor_order, title=run.name,
276 ax=ax)
Javi Merino9fde2152014-08-12 15:34:24 +0100277
Javi Merino898783c2015-06-16 11:45:16 +0100278 plot_weighted_input_power(runs, actor_order, width, height)
279
Javi Merino9fde2152014-08-12 15:34:24 +0100280def plot_output_power(runs, actor_order, width=None, height=None):
281 """Make a multicolumn plot of the output power of each run"""
282 num_runs = len(runs)
283 axis = pre_plot_setup(width=width, height=height, ncols=num_runs)
284
285 if num_runs == 1:
286 axis = [axis]
287
288 for ax, run in zip(axis, runs):
Javi Merino8625d732015-06-17 17:21:31 +0100289 run.thermal_governor.plot_output_power(actor_order, title=run.name,
290 ax=ax)
Javi Merinoe5ea60a2014-08-12 16:41:42 +0100291
292def plot_freq_hists(runs, map_label):
293 """Plot frequency histograms of multiple runs"""
294 num_runs = len(runs)
Javi Merino9d9c9b82015-04-29 11:15:11 +0100295 nrows = 2 * number_freq_plots(runs, map_label)
Javi Merinoe5ea60a2014-08-12 16:41:42 +0100296 axis = pre_plot_setup(ncols=num_runs, nrows=nrows)
297
298 if num_runs == 1:
299 axis = [axis]
300 else:
301 axis = zip(*axis)
302
303 for ax, run in zip(axis, runs):
304 run.plot_freq_hists(map_label, ax=ax)
305
306def plot_temperature_hist(runs):
307 """Plot temperature histograms for all the runs"""
Javi Merinofa3ed922015-02-06 18:44:44 +0000308 num_runs = 0
309 for run in runs:
310 if len(run.thermal.data_frame):
311 num_runs += 1
312
313 if num_runs == 0:
314 return
315
Javi Merinoe5ea60a2014-08-12 16:41:42 +0100316 axis = pre_plot_setup(ncols=num_runs)
317
318 if num_runs == 1:
319 axis = [axis]
320
321 for ax, run in zip(axis, runs):
322 run.thermal.plot_temperature_hist(ax, run.name)