From 22b0c548dc889fb8e2f347fad5c09216c5a66133 Mon Sep 17 00:00:00 2001 From: Michele Di Giorgio Date: Wed, 7 Sep 2016 16:14:48 +0100 Subject: [PATCH 1/7] cpus_analysis: add overutilized signal to plots Signed-off-by: Michele Di Giorgio --- libs/utils/analysis/cpus_analysis.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/utils/analysis/cpus_analysis.py b/libs/utils/analysis/cpus_analysis.py index 5b161ac90..9511a90b6 100644 --- a/libs/utils/analysis/cpus_analysis.py +++ b/libs/utils/analysis/cpus_analysis.py @@ -123,6 +123,9 @@ 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) -- GitLab From a5b452f333547189676a3102f6f81acb37e8a52d Mon Sep 17 00:00:00 2001 From: Michele Di Giorgio Date: Wed, 7 Sep 2016 17:48:13 +0100 Subject: [PATCH 2/7] cpus_analysis: add parameters description to plotCPU docstring Signed-off-by: Michele Di Giorgio --- libs/utils/analysis/cpus_analysis.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/utils/analysis/cpus_analysis.py b/libs/utils/analysis/cpus_analysis.py index 9511a90b6..6f6e39ac0 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, ' -- GitLab From 3075e63cd3b274f15a0fe75bbd17b80ed700cb51 Mon Sep 17 00:00:00 2001 From: Michele Di Giorgio Date: Wed, 7 Sep 2016 18:28:43 +0100 Subject: [PATCH 3/7] cpus_analysis: fix plotting CPUs of only one cluster Plotting CPUs of only one cluster makes the call to _plotCPU fail because an empty set is passed to the function. Instead, avoid calling the internal plotting method if there is nothing to plot. Signed-off-by: Michele Di Giorgio --- libs/utils/analysis/cpus_analysis.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/utils/analysis/cpus_analysis.py b/libs/utils/analysis/cpus_analysis.py index 6f6e39ac0..e33a6340f 100644 --- a/libs/utils/analysis/cpus_analysis.py +++ b/libs/utils/analysis/cpus_analysis.py @@ -63,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") ############################################################################### -- GitLab From 2eafe1af3d46933f710fac8f132aab8f6c84ae02 Mon Sep 17 00:00:00 2001 From: Michele Di Giorgio Date: Wed, 7 Sep 2016 18:56:45 +0100 Subject: [PATCH 4/7] tasks_analysis: check input tasks type only when provided by the user Signed-off-by: Michele Di Giorgio --- libs/utils/analysis/tasks_analysis.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/utils/analysis/tasks_analysis.py b/libs/utils/analysis/tasks_analysis.py index be3f966a2..8849662e7 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: -- GitLab From 733b46df8ac75cfd2a667b51f57607d377103bfc Mon Sep 17 00:00:00 2001 From: Michele Di Giorgio Date: Fri, 9 Sep 2016 13:40:34 +0100 Subject: [PATCH 5/7] frequency_analysis: squash residency computations into a single function It is redundant to have two private methods to compute frequency residency. Especially because one of them simply calls the other one straight away. Still we expose two dataframe getters to the user, one for per-CPU frequency residency and the other one for per-cluster frequency residency. Signed-off-by: Michele Di Giorgio --- libs/utils/analysis/frequency_analysis.py | 67 +++++++++++------------ 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/libs/utils/analysis/frequency_analysis.py b/libs/utils/analysis/frequency_analysis.py index 24243c52f..60826cb5a 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=''): """ -- GitLab From 07eeed6d7548be3cc1a350fbff191ddc1147fde6 Mon Sep 17 00:00:00 2001 From: Michele Di Giorgio Date: Tue, 13 Sep 2016 16:03:52 +0100 Subject: [PATCH 6/7] cpus_analysis: improve title style in plotCPU() Signed-off-by: Michele Di Giorgio --- libs/utils/analysis/cpus_analysis.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libs/utils/analysis/cpus_analysis.py b/libs/utils/analysis/cpus_analysis.py index e33a6340f..d3b4ac673 100644 --- a/libs/utils/analysis/cpus_analysis.py +++ b/libs/utils/analysis/cpus_analysis.py @@ -89,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: @@ -134,6 +132,11 @@ class CpusAnalysis(AnalysisModule): 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([]) -- GitLab From c8bad1b70488e13d1833d2288d4fd5d0ad76cdf8 Mon Sep 17 00:00:00 2001 From: Michele Di Giorgio Date: Tue, 13 Sep 2016 16:51:38 +0100 Subject: [PATCH 7/7] tasks_analysis: remove sched_overutilized from plot accounting In plotTasks(), sched_overutilized must not be considered when deciding whether to generate one of the three plots (utilization, residency, PELT). Furthermore, sched_overutilized is only shown when either the user specifies it as one of the signals to be plot or no signals are specified (so, by default all three plots are generated with all signals). Signed-off-by: Michele Di Giorgio --- libs/utils/analysis/tasks_analysis.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/libs/utils/analysis/tasks_analysis.py b/libs/utils/analysis/tasks_analysis.py index 8849662e7..d47cfadde 100644 --- a/libs/utils/analysis/tasks_analysis.py +++ b/libs/utils/analysis/tasks_analysis.py @@ -282,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]) @@ -297,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]) @@ -310,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 -- GitLab