17b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# SPDX-License-Identifier: Apache-2.0
27b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio#
37b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# Copyright (C) 2015, ARM Limited and contributors.
47b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio#
57b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# Licensed under the Apache License, Version 2.0 (the "License"); you may
67b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# not use this file except in compliance with the License.
77b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# You may obtain a copy of the License at
87b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio#
97b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# http://www.apache.org/licenses/LICENSE-2.0
107b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio#
117b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# Unless required by applicable law or agreed to in writing, software
127b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
137b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
147b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# See the License for the specific language governing permissions and
157b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# limitations under the License.
167b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio#
177b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
187b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio""" Idle Analysis Module """
197b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
207b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgioimport matplotlib.gridspec as gridspec
217b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgioimport matplotlib.pyplot as plt
227b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgioimport pandas as pd
237b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgioimport pylab as pl
247b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
257b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgiofrom analysis_module import AnalysisModule
26a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgiofrom trace import ResidencyTime, ResidencyData
277b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgiofrom trappy.utils import listify
287b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
297b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
307b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgioclass IdleAnalysis(AnalysisModule):
317b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio    """
327b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio    Support for plotting Idle Analysis data
337b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
347b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio    :param trace: input Trace object
357b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio    :type trace: :mod:`libs.utils.Trace`
367b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio    """
377b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
387b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio    def __init__(self, trace):
397b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        super(IdleAnalysis, self).__init__(trace)
407b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
417b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio###############################################################################
427b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# DataFrame Getter Methods
437b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio###############################################################################
447b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
457b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio    def _dfg_cpu_idle_state_residency(self, cpu):
467b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        """
477b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        Compute time spent by a given CPU in each idle state.
487b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
497b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        :param entity: CPU ID
507b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        :type entity: int
517b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
527b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        :returns: :mod:`pandas.DataFrame` - idle state residency dataframe
537b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        """
547b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        if not self._trace.hasEvents('cpu_idle'):
55c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.warning('Events [cpu_idle] not found, '
56c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                              'idle state residency computation not possible!')
577b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            return None
587b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
597b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        idle_df = self._dfg_trace_event('cpu_idle')
607b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        cpu_idle = idle_df[idle_df.cpu_id == cpu]
617b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
627b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        cpu_is_idle = self._trace.getCPUActiveSignal(cpu) ^ 1
637b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
647b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # In order to compute the time spent in each idle state we
657b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # multiply 2 square waves:
667b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # - cpu_idle
677b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # - idle_state, square wave of the form:
687b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        #     idle_state[t] == 1 if at time t CPU is in idle state i
697b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        #     idle_state[t] == 0 otherwise
707b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        available_idles = sorted(idle_df.state.unique())
717b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # Remove non-idle state from availables
72d162fa6bab716eacb5b423f94e60c720993bbe5bBrendan Jackman        available_idles = available_idles[1:]
737b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        cpu_idle = cpu_idle.join(cpu_is_idle.to_frame(name='is_idle'),
747b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio                                 how='outer')
757b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        cpu_idle.fillna(method='ffill', inplace=True)
76181dd7b70747c83189afa450660a9ab746c53c8cBrendan Jackman
77181dd7b70747c83189afa450660a9ab746c53c8cBrendan Jackman        # Extend the last cpu_idle event to the end of the time window under
78181dd7b70747c83189afa450660a9ab746c53c8cBrendan Jackman        # consideration
79181dd7b70747c83189afa450660a9ab746c53c8cBrendan Jackman        final_entry = pd.DataFrame([cpu_idle.iloc[-1]], index=[self._trace.x_max])
80181dd7b70747c83189afa450660a9ab746c53c8cBrendan Jackman        cpu_idle = cpu_idle.append(final_entry)
81181dd7b70747c83189afa450660a9ab746c53c8cBrendan Jackman
827b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        idle_time = []
837b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        for i in available_idles:
847b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            idle_state = cpu_idle.state.apply(
857b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio                lambda x: 1 if x == i else 0
867b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            )
877b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            idle_t = cpu_idle.is_idle * idle_state
887b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            # Compute total time by integrating the square wave
897b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            idle_time.append(self._trace.integrate_square_wave(idle_t))
907b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
917b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        idle_time_df = pd.DataFrame({'time' : idle_time}, index=available_idles)
927b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        idle_time_df.index.name = 'idle_state'
937b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        return idle_time_df
947b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
957b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio    def _dfg_cluster_idle_state_residency(self, cluster):
967b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        """
977b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        Compute time spent by a given cluster in each idle state.
987b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
997b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        :param cluster: cluster name or list of CPU IDs
1007b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        :type cluster: str or list(int)
1017b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
1027b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        :returns: :mod:`pandas.DataFrame` - idle state residency dataframe
1037b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        """
1047b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        if not self._trace.hasEvents('cpu_idle'):
105c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.warning('Events [cpu_idle] not found, '
106c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                              'idle state residency computation not possible!')
1077b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            return None
1087b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
1097b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        _cluster = cluster
1107b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        if isinstance(cluster, str) or isinstance(cluster, unicode):
1117b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            try:
1127b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio                _cluster = self._platform['clusters'][cluster.lower()]
1137b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            except KeyError:
114c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                self._log.warning('%s cluster not found!', cluster)
1157b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio                return None
1167b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
1177b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        idle_df = self._dfg_trace_event('cpu_idle')
1187b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # Each core in a cluster can be in a different idle state, but the
1197b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # cluster lies in the idle state with lowest ID, that is the shallowest
1207b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # idle state among the idle states of its CPUs
1217b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        cl_idle = idle_df[idle_df.cpu_id == _cluster[0]].state.to_frame(
1227b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            name=_cluster[0])
1237b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        for cpu in _cluster[1:]:
1247b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            cl_idle = cl_idle.join(
1257b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio                idle_df[idle_df.cpu_id == cpu].state.to_frame(name=cpu),
1267b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio                how='outer'
1277b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            )
1287b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        cl_idle.fillna(method='ffill', inplace=True)
1297b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        cl_idle = pd.DataFrame(cl_idle.min(axis=1), columns=['state'])
1307b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
1317b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # Build a square wave of the form:
1327b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        #     cl_is_idle[t] == 1 if all CPUs in the cluster are reported
1337b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        #                      to be idle by cpufreq at time t
1347b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        #     cl_is_idle[t] == 0 otherwise
1357b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        cl_is_idle = self._trace.getClusterActiveSignal(_cluster) ^ 1
1367b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
1377b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # In order to compute the time spent in each idle statefrequency we
1387b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # multiply 2 square waves:
1397b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # - cluster_is_idle
1407b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # - idle_state, square wave of the form:
1417b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        #     idle_state[t] == 1 if at time t cluster is in idle state i
1427b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        #     idle_state[t] == 0 otherwise
1437b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        available_idles = sorted(idle_df.state.unique())
1447b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        # Remove non-idle state from availables
145d162fa6bab716eacb5b423f94e60c720993bbe5bBrendan Jackman        available_idles = available_idles[1:]
1467b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        cl_idle = cl_idle.join(cl_is_idle.to_frame(name='is_idle'),
1477b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio                               how='outer')
1487b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        cl_idle.fillna(method='ffill', inplace=True)
1497b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        idle_time = []
1507b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        for i in available_idles:
1517b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            idle_state = cl_idle.state.apply(
1527b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio                lambda x: 1 if x == i else 0
1537b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            )
1547b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            idle_t = cl_idle.is_idle * idle_state
1557b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            # Compute total time by integrating the square wave
1567b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio            idle_time.append(self._trace.integrate_square_wave(idle_t))
1577b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
1587b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        idle_time_df = pd.DataFrame({'time' : idle_time}, index=available_idles)
1597b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        idle_time_df.index.name = 'idle_state'
1607b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio        return idle_time_df
1617b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
1627b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
1637b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio###############################################################################
1647b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# Plotting Methods
1657b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio###############################################################################
1667b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
167a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio    def plotCPUIdleStateResidency(self, cpus=None, pct=False):
168a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        """
169a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        Plot per-CPU idle state residency. big CPUs are plotted first and then
170a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        LITTLEs.
171a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
172a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        Requires cpu_idle trace events.
173a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
174a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :param cpus: list of CPU IDs. By default plot all CPUs
175a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :type cpus: list(int) or int
176a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
177a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :param pct: plot residencies in percentage
178a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :type pct: bool
179a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        """
180a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        if not self._trace.hasEvents('cpu_idle'):
181c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.warning('Events [cpu_idle] not found, '
182c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                              'plot DISABLED!')
183a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            return
184a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
185a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        if cpus is None:
186a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            # Generate plots only for available CPUs
187a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            cpuidle_data = self._dfg_trace_event('cpu_idle')
188a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            _cpus = range(cpuidle_data.cpu_id.max() + 1)
189a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        else:
190a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            _cpus = listify(cpus)
191a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
192a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        # Split between big and LITTLE CPUs ordered from higher to lower ID
193a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        _cpus.reverse()
194f2a93cfe4c1c35d5237810e7fed4f1330859a358Brendan Jackman        big_cpus = [c for c in _cpus if c in self._big_cpus]
195f2a93cfe4c1c35d5237810e7fed4f1330859a358Brendan Jackman        little_cpus = [c for c in _cpus if c in self._little_cpus]
196a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        _cpus = big_cpus + little_cpus
197a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
198a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        residencies = []
199a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        xmax = 0.0
200a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        for cpu in _cpus:
201a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            r = self._dfg_cpu_idle_state_residency(cpu)
202a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            residencies.append(ResidencyData('CPU{}'.format(cpu), r))
203a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
204a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            max_time = r.max().values[0]
205a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            if xmax < max_time:
206a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                xmax = max_time
207a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
208a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        self._plotIdleStateResidency(residencies, 'cpu', xmax, pct=pct)
209a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
210a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio    def plotClusterIdleStateResidency(self, clusters=None, pct=False):
211a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        """
212a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        Plot per-cluster idle state residency in a given cluster, i.e. the
213a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        amount of time cluster `cluster` spent in idle state `i`. By default,
214a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        both 'big' and 'LITTLE' clusters data are plotted.
215a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
216a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        Requires cpu_idle following trace events.
217a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :param clusters: name of the clusters to be plotted (all of them by
218a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            default)
219a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :type clusters: str ot list(str)
220a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        """
221a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        if not self._trace.hasEvents('cpu_idle'):
222c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.warning('Events [cpu_idle] not found, plot DISABLED!')
223a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            return
224f2a93cfe4c1c35d5237810e7fed4f1330859a358Brendan Jackman        if 'clusters' not in self._platform:
225f2a93cfe4c1c35d5237810e7fed4f1330859a358Brendan Jackman            self._log.warning('No platform cluster info. Plot DISABLED!')
226f2a93cfe4c1c35d5237810e7fed4f1330859a358Brendan Jackman            return
227a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
228a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        # Sanitize clusters
229a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        if clusters is None:
230a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            _clusters = self._platform['clusters'].keys()
231a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        else:
232a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            _clusters = listify(clusters)
233a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
234a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        # Precompute residencies for each cluster
235a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        residencies = []
236a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        xmax = 0.0
237a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        for c in _clusters:
238a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            r = self._dfg_cluster_idle_state_residency(c.lower())
239a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            residencies.append(ResidencyData('{} Cluster'.format(c), r))
240a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
241a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            max_time = r.max().values[0]
242a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            if xmax < max_time:
243a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                xmax = max_time
244a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
245a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        self._plotIdleStateResidency(residencies, 'cluster', xmax, pct=pct)
2467b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
2477b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio###############################################################################
2487b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# Utility Methods
2497b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio###############################################################################
2507b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
251a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio    def _plotIdleStateResidency(self, residencies, entity_name, xmax,
252a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                                pct=False):
253a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        """
254a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        Generate Idle state residency plots for the given entities.
255a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
256a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :param residencies: list of residencies to be plot
257a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :type residencies: list(namedtuple(ResidencyData)) - each tuple
258a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            contains:
259a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            - a label to be used as subplot title
260a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            - a dataframe with residency for each idle state
261a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
262a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :param entity_name: name of the entity ('cpu' or 'cluster') used in the
263a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            figure name
264a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :type entity_name: str
265a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
266a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :param xmax: upper bound of x-axes
267a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :type xmax: double
268a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
269a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :param pct: plot residencies in percentage
270a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        :type pct: bool
271a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        """
272a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        n_plots = len(residencies)
273a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        gs = gridspec.GridSpec(n_plots, 1)
274a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        fig = plt.figure()
275a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
276a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        for idx, data in enumerate(residencies):
277a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            r = data.residency
278a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            if r is None:
279a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                plt.close(fig)
280a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                return
281a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
282a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            axes = fig.add_subplot(gs[idx])
283a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            is_first = idx == 0
284a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            is_last = idx+1 == n_plots
285a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            yrange = 0.4 * max(6, len(r)) * n_plots
286a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            if pct:
287a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                duration = r.time.sum()
288a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                r_pct = r.apply(lambda x: x*100/duration)
289a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                r_pct.columns = [data.label]
290a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                r_pct.T.plot.barh(ax=axes, stacked=True, figsize=(16, yrange))
291a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
292a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                axes.legend(loc='lower center', ncol=7)
293a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                axes.set_xlim(0, 100)
294a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            else:
295a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                r.plot.barh(ax=axes, color='g',
296a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                            legend=False, figsize=(16, yrange))
297a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
298a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                axes.set_xlim(0, 1.05*xmax)
299a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                axes.set_ylabel('Idle State')
300a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                axes.set_title(data.label)
301a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
302a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            axes.grid(True)
303a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            if is_last:
304a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                if pct:
305a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                    axes.set_xlabel('Residency [%]')
306a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                else:
307a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                    axes.set_xlabel('Time [s]')
308a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            else:
309a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                axes.set_xticklabels([])
310a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
311a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio            if is_first:
312a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                legend_y = axes.get_ylim()[1]
313a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                axes.annotate('Idle State Residency Time', xy=(0, legend_y),
314a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                              xytext=(-50, 45), textcoords='offset points',
315a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                              fontsize=18)
316a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
317a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        figname = '{}/{}{}_idle_state_residency.png'\
318a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                  .format(self._trace.plots_dir,
319a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                          self._trace.plots_prefix,
320a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio                          entity_name)
321a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio
322a5bc71881d8d8ff169006a09e19ca771dee26711Michele Di Giorgio        pl.savefig(figname, bbox_inches='tight')
3237b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio
3247b789df5a17a406c64e0fb06553d4b60846ae36bMichele Di Giorgio# vim :set tabstop=4 shiftwidth=4 expandtab
325