diff --git a/libs/utils/analysis/cpus_analysis.py b/libs/utils/analysis/cpus_analysis.py index 5b161ac901db4d0d076134370e5b8d5d3f1b72b4..d3b4ac673fd339e0a617c5fdc1d80d9530a82dbe 100644 --- a/libs/utils/analysis/cpus_analysis.py +++ b/libs/utils/analysis/cpus_analysis.py @@ -46,6 +46,9 @@ class CpusAnalysis(AnalysisModule): def plotCPU(self, cpus=None): """ Plot CPU-related signals for both big and LITTLE clusters. + + :param cpus: list of CPUs to be plotted + :type cpus: list(int) """ if not self._trace.hasEvents('sched_load_avg_cpu'): logging.warn('Events [sched_load_avg_cpu] not found, ' @@ -60,11 +63,13 @@ class CpusAnalysis(AnalysisModule): # Plot: big CPUs bcpus = set(cpus) & set(self._platform['clusters']['big']) - self._plotCPU(bcpus, "big") + if bcpus: + self._plotCPU(bcpus, "big") # Plot: LITTLE CPUs lcpus = set(cpus) & set(self._platform['clusters']['little']) - self._plotCPU(lcpus, "LITTLE") + if lcpus: + self._plotCPU(lcpus, "LITTLE") ############################################################################### @@ -84,8 +89,6 @@ class CpusAnalysis(AnalysisModule): # Plot required CPUs _, pltaxes = plt.subplots(len(cpus), 1, figsize=(16, 3*(len(cpus)))) - plt.suptitle("{}CPUs Signals".format(label1), - y=.99, fontsize=16, horizontalalignment='center') idx = 0 for cpu in cpus: @@ -123,9 +126,17 @@ class CpusAnalysis(AnalysisModule): data.plot(ax=axes, style=['m', '--y'], drawstyle='steps-post') + # Add overutilized signal to the plot + self._trace.analysis.status.plotOverutilized(axes) + axes.set_ylim(0, 1100) axes.set_xlim(self._trace.x_min, self._trace.x_max) + if idx == 0: + axes.annotate("{}CPUs Signals".format(label1), + xy=(0, axes.get_ylim()[1]), + xytext=(-50, 25), + textcoords='offset points', fontsize=16) # Disable x-axis timestamp for top-most cpus if len(cpus) > 1 and idx < len(cpus)-1: axes.set_xticklabels([]) diff --git a/libs/utils/analysis/frequency_analysis.py b/libs/utils/analysis/frequency_analysis.py index 24243c52f19deed39996ed31c1090239fbb0a56d..60826cb5a9465c3175a5d13755ce7ca269736030 100644 --- a/libs/utils/analysis/frequency_analysis.py +++ b/libs/utils/analysis/frequency_analysis.py @@ -66,8 +66,13 @@ class FrequencyAnalysis(AnalysisModule): :returns: :mod:`pandas.DataFrame` - "total" or "active" time residency at each frequency. + + :raises: TypeError """ - residency = self._getCPUFrequencyResidency(cpu) + if not isinstance(cpu, int): + raise TypeError('Input CPU parameter must be an integer') + + residency = self._getFrequencyResidency(cpu) if not residency: return None if total: @@ -79,10 +84,10 @@ class FrequencyAnalysis(AnalysisModule): Get per-Cluster frequency residency, i.e. amount of time CLUSTER `cluster` spent at each frequency. - :param cluster: this can be either a single CPU ID or a list of CPU IDs - belonging to a cluster or the cluster name as specified in the - platform description - :type cluster: str or int or list(int) + :param cluster: this can be either a list of CPU IDs belonging to a + cluster or the cluster name as specified in the platform + description + :type cluster: str or list(int) :param total: if true returns the "total" time, otherwise the "active" time is returned @@ -90,8 +95,22 @@ class FrequencyAnalysis(AnalysisModule): :returns: :mod:`pandas.DataFrame` - "total" or "active" time residency at each frequency. + + :raises: KeyError """ - residency = self._getClusterFrequencyResidency(cluster) + if isinstance(cluster, str): + try: + residency = self._getFrequencyResidency( + self._platform['clusters'][cluster.lower()] + ) + except KeyError: + logging.warn( + 'Platform descriptor has not a cluster named [%s], ' + 'plot disabled!', cluster + ) + return None + else: + residency = self._getFrequencyResidency(cluster) if not residency: return None if total: @@ -251,7 +270,7 @@ class FrequencyAnalysis(AnalysisModule): residencies = [] xmax = 0.0 for cpu in _cpus: - res = self._getCPUFrequencyResidency(cpu) + res = self._getFrequencyResidency(cpu) residencies.append(ResidencyData('CPU{}'.format(cpu), res)) max_time = res.total.max().values[0] @@ -306,7 +325,7 @@ class FrequencyAnalysis(AnalysisModule): residencies = [] xmax = 0.0 for cluster in _clusters: - res = self._getClusterFrequencyResidency( + res = self._getFrequencyResidency( self._platform['clusters'][cluster.lower()]) residencies.append(ResidencyData('{} Cluster'.format(cluster), res)) @@ -384,20 +403,17 @@ class FrequencyAnalysis(AnalysisModule): return cluster_active @memoized - def _getClusterFrequencyResidency(self, cluster): + def _getFrequencyResidency(self, cluster): """ Get a DataFrame with per cluster frequency residency, i.e. amount of time spent at a given frequency in each cluster. :param cluster: this can be either a single CPU ID or a list of CPU IDs - belonging to a cluster or the cluster name as specified in the - platform description - :type cluster: str or int or list(int) + belonging to a cluster + :type cluster: int or list(int) :returns: namedtuple(ResidencyTime) - tuple of total and active time dataframes - - :raises: KeyError """ if not self._trace.hasEvents('cpu_frequency'): logging.warn('Events [cpu_frequency] not found, ' @@ -408,14 +424,7 @@ class FrequencyAnalysis(AnalysisModule): 'frequency residency computation not possible!') return None - if isinstance(cluster, str): - try: - _cluster = self._platform['clusters'][cluster.lower()] - except KeyError: - logging.warn('%s cluster not found!', cluster) - return None - else: - _cluster = listify(cluster) + _cluster = listify(cluster) freq_df = self._dfg_trace_event('cpu_frequency') # Assumption: all CPUs in a cluster run at the same frequency, i.e. the @@ -467,20 +476,6 @@ class FrequencyAnalysis(AnalysisModule): active_time.index.name = 'frequency' return ResidencyTime(total_time, active_time) - def _getCPUFrequencyResidency(self, cpu): - """ - Get a DataFrame with per-CPU frequency residency, i.e. amount of - time CPU `cpu` spent at each frequency. Both total and active times - will be computed. - - :param cpu: CPU ID - :type cpu: int - - :returns: namedtuple(ResidencyTime) - tuple of total and active time - dataframes - """ - return self._getClusterFrequencyResidency(cpu) - def _plotFrequencyResidencyAbs(self, axes, residency, n_plots, is_first, is_last, xmax, title=''): """ diff --git a/libs/utils/analysis/tasks_analysis.py b/libs/utils/analysis/tasks_analysis.py index be3f966a26e4048e78416da7f7cc8cf1e3c64267..d47cfaddefe7f31220746cd669c880ceb0716062 100644 --- a/libs/utils/analysis/tasks_analysis.py +++ b/libs/utils/analysis/tasks_analysis.py @@ -231,11 +231,12 @@ class TasksAnalysis(AnalysisModule): 'plot DISABLED!') return - if not isinstance(tasks, str) and \ - not isinstance(tasks, list): + # Defined list of tasks to plot + if tasks and \ + not isinstance(tasks, str) and \ + not isinstance(tasks, list): raise ValueError('Wrong format for tasks parameter') - # Defined list of tasks to plot if tasks: tasks_to_plot = tasks elif self._tasks: @@ -281,14 +282,13 @@ class TasksAnalysis(AnalysisModule): logging.info('Plotting %5d: %s...', tid, ', '.join(task_name)) plot_id = 0 - # Figure + # For each task create a figure with plots_count plots plt.figure(figsize=(16, 2*6+3)) plt.suptitle("Task Signals", y=.94, fontsize=16, horizontalalignment='center') # Plot load and utilization - signals_to_plot = {'load_avg', 'util_avg', - 'boosted_util', 'sched_overutilized'} + signals_to_plot = {'load_avg', 'util_avg', 'boosted_util'} signals_to_plot = list(signals_to_plot.intersection(signals)) if len(signals_to_plot) > 0: axes = plt.subplot(gs[plot_id, 0]) @@ -296,10 +296,12 @@ class TasksAnalysis(AnalysisModule): .format(tid, task_name)) plot_id = plot_id + 1 is_last = (plot_id == plots_count) + if 'sched_overutilized' in signals: + signals_to_plot.append('sched_overutilized') self._plotTaskSignals(axes, tid, signals_to_plot, is_last) # Plot CPUs residency - signals_to_plot = {'residencies', 'sched_overutilized'} + signals_to_plot = {'residencies'} signals_to_plot = list(signals_to_plot.intersection(signals)) if len(signals_to_plot) > 0: axes = plt.subplot(gs[plot_id, 0]) @@ -309,18 +311,20 @@ class TasksAnalysis(AnalysisModule): ) plot_id = plot_id + 1 is_last = (plot_id == plots_count) + if 'sched_overutilized' in signals: + signals_to_plot.append('sched_overutilized') self._plotTaskResidencies(axes, tid, signals_to_plot, is_last) # Plot PELT signals - signals_to_plot = { - 'load_sum', 'util_sum', - 'period_contrib', 'sched_overutilized'} + signals_to_plot = {'load_sum', 'util_sum', 'period_contrib'} signals_to_plot = list(signals_to_plot.intersection(signals)) if len(signals_to_plot) > 0: axes = plt.subplot(gs[plot_id, 0]) axes.set_title('Task [{0:d}:{1:s}] PELT Signals' .format(tid, task_name)) plot_id = plot_id + 1 + if 'sched_overutilized' in signals: + signals_to_plot.append('sched_overutilized') self._plotTaskPelt(axes, tid, signals_to_plot) # Save generated plots into datadir