analysis/frequency: add per CPU frequency plotting
Using plotClusterFrequencies() is overkilling in some notebooks where we
need/want to focus on a specific CPU.
Teach the frequency_analysis how to generate plots for a list of specified
CPUs only.
Signed-off-by: Patrick Bellasi <patrick.bellasi@arm.com>
diff --git a/libs/utils/analysis/frequency_analysis.py b/libs/utils/analysis/frequency_analysis.py
index ad50597..9a617b8 100644
--- a/libs/utils/analysis/frequency_analysis.py
+++ b/libs/utils/analysis/frequency_analysis.py
@@ -27,6 +27,7 @@
from analysis_module import AnalysisModule
from trace import ResidencyTime, ResidencyData
+from bart.common.Utils import area_under_curve
class FrequencyAnalysis(AnalysisModule):
@@ -218,6 +219,119 @@
return (avg_lfreq/1e3, avg_bfreq/1e3)
+ def plotCPUFrequencies(self, cpus=None):
+ """
+ Plot frequency for the specified CPUs (or all if not specified).
+ If sched_overutilized events are available, the plots will also show the
+ intervals of time where the system was overutilized.
+
+ The generated plots are also saved as PNG images under the folder
+ specified by the `plots_dir` parameter of :class:`Trace`.
+
+ :param cpus: the list of CPUs to plot, if None it generate a plot
+ for each available CPU
+ :type cpus: int or list(int)
+
+ :return: a dictionary of average frequency for each CPU.
+ """
+ if not self._trace.hasEvents('cpu_frequency'):
+ self._log.warning('Events [cpu_frequency] not found, plot DISABLED!')
+ return
+ df = self._dfg_trace_event('cpu_frequency')
+
+ if cpus is None:
+ # Generate plots only for available CPUs
+ cpus = range(df.cpu.max()+1)
+ else:
+ # Generate plots only specified CPUs
+ cpus = listify(cpus)
+
+ chained_assignment = pd.options.mode.chained_assignment
+ pd.options.mode.chained_assignment = None
+
+ freq = {}
+ for cpu_id in listify(cpus):
+ # Extract CPUs' frequencies and scale them to [MHz]
+ _df = df[df.cpu == cpu_id]
+ if _df.empty:
+ self._log.warning('No [cpu_frequency] events for CPU%d, '
+ 'plot DISABLED!', cpu_id)
+ continue
+ _df['frequency'] = _df.frequency / 1e3
+
+ # Compute AVG frequency for this CPU
+ avg_freq = 0
+ if len(_df) > 1:
+ timespan = _df.index[-1] - _df.index[0]
+ avg_req = area_under_curve(_df['frequency']) / timespan
+
+ # Store DF for plotting
+ freq[cpu_id] = {
+ 'df' : _df,
+ 'avg' : avg_freq,
+ }
+
+ pd.options.mode.chained_assignment = chained_assignment
+
+ plots_count = len(freq)
+ if not plots_count:
+ return
+
+ # Setup CPUs plots
+ fig, pltaxes = plt.subplots(len(freq), 1, figsize=(16, 4 * plots_count))
+
+ avg_freqs = {}
+ for plot_idx, cpu_id in enumerate(freq):
+
+ # CPU frequencies and average value
+ _df = freq[cpu_id]['df']
+ _avg = freq[cpu_id]['avg']
+
+ # Plot average frequency
+ try:
+ axes = pltaxes[plot_idx]
+ except TypeError:
+ axes = pltaxes
+ axes.set_title('CPU{:2d} Frequency'.format(cpu_id))
+ axes.axhline(_avg, color='r', linestyle='--', linewidth=2)
+
+ # Set plot limit based on CPU min/max frequencies
+ for cluster,cpus in self._platform['clusters'].iteritems():
+ if cpu_id not in cpus:
+ continue
+ axes.set_ylim(
+ (self._platform['freqs'][cluster][0] - 100000)/1e3,
+ (self._platform['freqs'][cluster][-1] + 100000)/1e3
+ )
+ break
+
+ # Plot CPU frequency transitions
+ _df['frequency'].plot(style=['r-'], ax=axes,
+ drawstyle='steps-post', alpha=0.4)
+
+ # Plot overutilzied regions (if signal available)
+ self._trace.analysis.status.plotOverutilized(axes)
+
+ # Finalize plot
+ axes.set_xlim(self._trace.x_min, self._trace.x_max)
+ axes.set_ylabel('MHz')
+ axes.grid(True)
+ if plot_idx + 1 < plots_count:
+ axes.set_xticklabels([])
+ axes.set_xlabel('')
+
+ avg_freqs[cpu_id] = _avg/1e3
+ self._log.info('CPU%02d average frequency: %.3f GHz',
+ cpu_id, avg_freqs[cpu_id])
+
+ # Save generated plots into datadir
+ figname = '{}/{}cpus_freqs.png'\
+ .format(self._trace.plots_dir, self._trace.plots_prefix)
+ pl.savefig(figname, bbox_inches='tight')
+
+ return avg_freqs
+
+
def plotCPUFrequencyResidency(self, cpus=None, pct=False, active=False):
"""
Plot per-CPU frequency residency. big CPUs are plotted first and then