Source code for trappy.plot_utils

#    Copyright 2015-2016 ARM Limited
#
# 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.
#

"""Small functions to help with plots"""

# pylint disable=star-args

from matplotlib import pyplot as plt
import os
import re

from trappy.wa import SysfsExtractor

GOLDEN_RATIO = 1.618034

[docs]def normalize_title(title, opt_title): """Return a string with that contains the title and opt_title if it's not the empty string See test_normalize_title() for usage """ if opt_title is not "": title = opt_title + " - " + title return title
[docs]def set_lim(lim, get_lim_f, set_lim_f): """Set x or y limitis of the plot lim can be a tuple containing the limits or the string "default" or "range". "default" does nothing and uses matplotlib default. "range" extends the current margin by 10%. This is useful since the default xlim and ylim of the plots sometimes make it harder to see data that is just in the margin. """ if lim == "default": return if lim == "range": cur_lim = get_lim_f() lim = (cur_lim[0] - 0.1 * (cur_lim[1] - cur_lim[0]), cur_lim[1] + 0.1 * (cur_lim[1] - cur_lim[0])) set_lim_f(lim[0], lim[1])
[docs]def set_xlim(ax, xlim): """Set the xlim of the plot See set_lim() for the details """ set_lim(xlim, ax.get_xlim, ax.set_xlim)
[docs]def set_ylim(ax, ylim): """Set the ylim of the plot See set_lim() for the details """ set_lim(ylim, ax.get_ylim, ax.set_ylim)
[docs]def pre_plot_setup(width=None, height=None, ncols=1, nrows=1): """initialize a figure width and height are the height and width of each row of plots. For 1x1 plots, that's the height and width of the plot. This function should be called before any calls to plot() """ if height is None: if width is None: height = 6 width = 10 else: height = width / GOLDEN_RATIO else: if width is None: width = height * GOLDEN_RATIO height *= nrows _, axis = plt.subplots(ncols=ncols, nrows=nrows, figsize=(width, height)) # Needed for multirow blots to not overlap with each other plt.tight_layout(h_pad=3.5) return axis
[docs]def post_plot_setup(ax, title="", xlabel=None, ylabel=None, xlim="default", ylim="range"): """Set xlabel, ylabel title, xlim and ylim of the plot This has to be called after calls to .plot(). The default ylim is to extend it by 10% because matplotlib default makes it hard values that are close to the margins """ if xlabel is not None: ax.set_xlabel(xlabel) if ylabel is not None: ax.set_ylabel(ylabel) if title: ax.set_title(title) set_ylim(ax, ylim) set_xlim(ax, xlim)
[docs]def number_freq_plots(runs, map_label): """Calculate the number of plots needed for allfreq plots and frequency histogram plots """ num_cpu_plots = len(map_label) has_devfreq_data = False for run in runs: if len(run.devfreq_in_power.data_frame) > 0: has_devfreq_data = True break num_freq_plots = num_cpu_plots if has_devfreq_data: num_freq_plots += 1 return num_freq_plots
[docs]def plot_temperature(runs, width=None, height=None, ylim="range", tz_id=None): """Plot temperatures runs is an array of FTrace() instances. Extract the control_temp from the governor data and plot the temperatures reported by the thermal framework. The governor doesn't track temperature when it's off, so the thermal framework trace is more reliable. """ ax = pre_plot_setup(width, height) for run in runs: gov_dfr = run.thermal_governor.data_frame if tz_id: gov_dfr = gov_dfr[gov_dfr["thermal_zone_id"] == tz_id] try: current_temp = gov_dfr["current_temperature"] delta_temp = gov_dfr["delta_temperature"] control_series = (current_temp + delta_temp) / 1000 except KeyError: control_series = None try: run.thermal.plot_temperature(control_temperature=control_series, ax=ax, legend_label=run.name, tz_id=tz_id) except ValueError: run.thermal_governor.plot_temperature(ax=ax, legend_label=run.name) post_plot_setup(ax, title="Temperature", ylim=ylim) plt.legend(loc="best")
[docs]def plot_hist(data, ax, title, unit, bins, xlabel, xlim, ylim): """Plot a histogram""" mean = data.mean() std = data.std() title += " (mean = {:.2f}{}, std = {:.2f})".format(mean, unit, std) xlabel += " ({})".format(unit) data.hist(ax=ax, bins=bins) post_plot_setup(ax, title=title, xlabel=xlabel, ylabel="count", xlim=xlim, ylim=ylim)
[docs]def plot_load(runs, map_label, width=None, height=None): """Make a multiplot of all the loads""" num_runs = len(runs) axis = pre_plot_setup(width=width, height=height, ncols=num_runs, nrows=2) if num_runs == 1: axis = [axis] else: axis = zip(*axis) for ax, run in zip(axis, runs): run.plot_load(map_label, title=run.name, ax=ax[0]) run.plot_normalized_load(map_label, title=run.name, ax=ax[1])
[docs]def plot_allfreqs(runs, map_label, width=None, height=None): """Make a multicolumn plots of the allfreqs plots of each run""" num_runs = len(runs) nrows = number_freq_plots(runs, map_label) axis = pre_plot_setup(width=width, height=height, nrows=nrows, ncols=num_runs) if num_runs == 1: if nrows == 1: axis = [[axis]] else: axis = [axis] elif nrows == 1: axis = [[ax] for ax in axis] else: axis = zip(*axis) for ax, run in zip(axis, runs): run.plot_allfreqs(map_label, ax=ax)
[docs]def plot_controller(runs, width=None, height=None): """Make a multicolumn plot of the pid controller of each run""" num_runs = len(runs) axis = pre_plot_setup(width=width, height=height, ncols=num_runs) if num_runs == 1: axis = [axis] for ax, run in zip(axis, runs): run.pid_controller.plot_controller(title=run.name, ax=ax)
[docs]def plot_weighted_input_power(runs, actor_order, width=None, height=None): """Make a multicolumn plot of the weighted input power of each run""" actor_weights = [] for run in runs: run_path = os.path.dirname(run.trace_path) sysfs = SysfsExtractor(run_path) thermal_params = sysfs.get_parameters() sorted_weights = [] for param in sorted(thermal_params): if re.match(r"cdev\d+_weight", param): sorted_weights.append(thermal_params[param]) actor_weights.append(zip(actor_order, sorted_weights)) # Do nothing if we don't have actor weights for any run if not any(actor_weights): return num_runs = len(runs) axis = pre_plot_setup(width=width, height=height, ncols=num_runs) if num_runs == 1: axis = [axis] for ax, run, weights in zip(axis, runs, actor_weights): run.thermal_governor.plot_weighted_input_power(weights, title=run.name, ax=ax)
[docs]def plot_input_power(runs, actor_order, width=None, height=None): """Make a multicolumn plot of the input power of each run""" num_runs = len(runs) axis = pre_plot_setup(width=width, height=height, ncols=num_runs) if num_runs == 1: axis = [axis] for ax, run in zip(axis, runs): run.thermal_governor.plot_input_power(actor_order, title=run.name, ax=ax) plot_weighted_input_power(runs, actor_order, width, height)
[docs]def plot_output_power(runs, actor_order, width=None, height=None): """Make a multicolumn plot of the output power of each run""" num_runs = len(runs) axis = pre_plot_setup(width=width, height=height, ncols=num_runs) if num_runs == 1: axis = [axis] for ax, run in zip(axis, runs): run.thermal_governor.plot_output_power(actor_order, title=run.name, ax=ax)
[docs]def plot_freq_hists(runs, map_label): """Plot frequency histograms of multiple runs""" num_runs = len(runs) nrows = 2 * number_freq_plots(runs, map_label) axis = pre_plot_setup(ncols=num_runs, nrows=nrows) if num_runs == 1: axis = [axis] else: axis = zip(*axis) for ax, run in zip(axis, runs): run.plot_freq_hists(map_label, ax=ax)
[docs]def plot_temperature_hist(runs): """Plot temperature histograms for all the runs""" num_runs = 0 for run in runs: if len(run.thermal.data_frame): num_runs += 1 if num_runs == 0: return axis = pre_plot_setup(ncols=num_runs) if num_runs == 1: axis = [axis] for ax, run in zip(axis, runs): run.thermal.plot_temperature_hist(ax, run.name)