1d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# SPDX-License-Identifier: Apache-2.0
2d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi#
3d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# Copyright (C) 2015, ARM Limited and contributors.
4d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi#
5d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# Licensed under the Apache License, Version 2.0 (the "License"); you may
6d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# not use this file except in compliance with the License.
7d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# You may obtain a copy of the License at
8d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi#
9d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# http://www.apache.org/licenses/LICENSE-2.0
10d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi#
11d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# Unless required by applicable law or agreed to in writing, software
12d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# See the License for the specific language governing permissions and
15d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# limitations under the License.
16d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi#
178e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
18b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio""" Trace Parser Module """
19b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio
208e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasiimport numpy as np
218e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasiimport os
228e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasiimport pandas as pd
238e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasiimport sys
248e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasiimport trappy
2579a153ae2cea469af80c9d5324c479365980a3adPatrick Bellasiimport json
26fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasiimport warnings
27691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgioimport operator
28c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasiimport logging
2979a153ae2cea469af80c9d5324c479365980a3adPatrick Bellasi
306721d173e18d3c56a33fd3f2eb9b50acd675ec27Patrick Bellasifrom analysis_register import AnalysisRegister
31691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgiofrom collections import namedtuple
32691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgiofrom devlib.utils.misc import memoized
33d1ed4c86118d4a2231231d185b423e90ac38df56Patrick Bellasifrom trappy.utils import listify, handle_duplicate_index
348e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
358e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
36d162fa6bab716eacb5b423f94e60c720993bbe5bBrendan JackmanNON_IDLE_STATE = -1
37691fc118e5dff4df885a039cc855a746de4b7556Michele Di GiorgioResidencyTime = namedtuple('ResidencyTime', ['total', 'active'])
38691fc118e5dff4df885a039cc855a746de4b7556Michele Di GiorgioResidencyData = namedtuple('ResidencyData', ['label', 'residency'])
39b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio
408e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasiclass Trace(object):
41f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    """
42f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    The Trace object is the LISA trace events parser.
43f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
44f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :param platform: a dictionary containing information about the target
45f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        platform
46f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :type platform: dict
47f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
48f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :param data_dir: folder containing all trace data
49f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :type data_dir: str
50f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
51f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :param events: events to be parsed (everything in the trace by default)
52f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :type events: list(str)
53f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
54aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman    :param tasks: filter data for the specified tasks only. If None (default),
55aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman        use data for all tasks found in the trace.
56aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman    :type tasks: list(str) or NoneType
57f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
58f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :param window: time window to consider when parsing the trace
59f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :type window: tuple(int, int)
60f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
61f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :param normalize_time: normalize trace time stamps
62f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :type normalize_time: bool
63f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
64f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :param trace_format: format of the trace. Possible values are:
65f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        - FTrace
66f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        - SysTrace
67f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :type trace_format: str
68f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
69f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :param plots_dir: directory where to save plots
70f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :type plots_dir: str
71f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
72f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :param plots_prefix: prefix for plots file names
73f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    :type plots_prefix: str
749ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
759ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes    :param cgroup_info: add cgroup information for sanitization
769ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes      example:
779ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        {
789ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            'controller_ids': { 2: 'schedtune', 4: 'cpuset' },
799ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            'cgroups': [ 'root', 'background', 'foreground' ],  # list of allowed cgroup names
809ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        }
819ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes    :type cgroup_info: dict
82f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio    """
838e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
844e260b2b7c2b8718781f2fd4b502391ed1465f5aJoel Fernandes    def __init__(self, platform, data_dir, events=None,
85b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio                 tasks=None, window=(0, None),
86b4ab05a5cc9630963cdfafeab214dcf1661167c7Javi Merino                 normalize_time=True,
8769a445cdc130949aa54b84fb9b3dfbcb55fa2d53Patrick Bellasi                 trace_format='FTrace',
8869a445cdc130949aa54b84fb9b3dfbcb55fa2d53Patrick Bellasi                 plots_dir=None,
899ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                 plots_prefix='',
909ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                 cgroup_info={}):
918e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
928e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # The platform used to run the experiments
9350b5967280ce939d80f2d8b74253a0130ba16561Patrick Bellasi        self.platform = platform
948e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
9534f5d28c5ef4415709f75afcc636c37a210f4d02Patrick Bellasi        # TRAPpy Trace object
969c42a5ae0f2a627860e5cc68dc7626da0c5b232fPatrick Bellasi        self.ftrace = None
978e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
9834f5d28c5ef4415709f75afcc636c37a210f4d02Patrick Bellasi        # Trace format
9934f5d28c5ef4415709f75afcc636c37a210f4d02Patrick Bellasi        self.trace_format = trace_format
10034f5d28c5ef4415709f75afcc636c37a210f4d02Patrick Bellasi
101a8fdc5fe3719b498318e0451f5b4cdfac1f5015ePatrick Bellasi        # The time window used to limit trace parsing to
102a8fdc5fe3719b498318e0451f5b4cdfac1f5015ePatrick Bellasi        self.window = window
103a8fdc5fe3719b498318e0451f5b4cdfac1f5015ePatrick Bellasi
1048e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # Dynamically registered TRAPpy events
1058e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        self.trappy_cls = {}
1068e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
1078e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # Maximum timespan for all collected events
1088e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        self.time_range = 0
1098e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
110877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi        # Time the system was overutilzied
111877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi        self.overutilized_time = 0
112877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi        self.overutilized_prc = 0
113877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi
1148e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # The dictionary of tasks descriptors available in the dataset
1158e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        self.tasks = {}
1168e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
117e24e02a9565a8ca8f948e15b573eeb808b8ece86Patrick Bellasi        # List of events required by user
118e24e02a9565a8ca8f948e15b573eeb808b8ece86Patrick Bellasi        self.events = []
119e24e02a9565a8ca8f948e15b573eeb808b8ece86Patrick Bellasi
1208e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # List of events available in the parsed trace
1218e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        self.available_events = []
1228e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
1239e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio        # Cluster frequency coherency flag
1249e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio        self.freq_coherency = True
1259e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio
126f0ca15ec410a73b8f223a48e1be234c8561c6c2cPatrick Bellasi        # Folder containing all trace data
127f0ca15ec410a73b8f223a48e1be234c8561c6c2cPatrick Bellasi        self.data_dir = None
128f0ca15ec410a73b8f223a48e1be234c8561c6c2cPatrick Bellasi
129c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        # Setup logging
130c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        self._log = logging.getLogger('Trace')
131c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi
1328e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # Folder containing trace
133f0ca15ec410a73b8f223a48e1be234c8561c6c2cPatrick Bellasi        if not os.path.isdir(data_dir):
134f0ca15ec410a73b8f223a48e1be234c8561c6c2cPatrick Bellasi            self.data_dir = os.path.dirname(data_dir)
135881f14cdb8b78ad422f739fe28cbce2525020430Patrick Bellasi        else:
136f0ca15ec410a73b8f223a48e1be234c8561c6c2cPatrick Bellasi            self.data_dir = data_dir
1378e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
13869a445cdc130949aa54b84fb9b3dfbcb55fa2d53Patrick Bellasi        # By deafult, use the trace dir to save plots
13969a445cdc130949aa54b84fb9b3dfbcb55fa2d53Patrick Bellasi        self.plots_dir = plots_dir
14069a445cdc130949aa54b84fb9b3dfbcb55fa2d53Patrick Bellasi        if self.plots_dir is None:
14169a445cdc130949aa54b84fb9b3dfbcb55fa2d53Patrick Bellasi            self.plots_dir = self.data_dir
14269a445cdc130949aa54b84fb9b3dfbcb55fa2d53Patrick Bellasi        self.plots_prefix = plots_prefix
14369a445cdc130949aa54b84fb9b3dfbcb55fa2d53Patrick Bellasi
1449ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        # Cgroup info for sanitization
1459ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        self.cgroup_info = cgroup_info
1469ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
1474e260b2b7c2b8718781f2fd4b502391ed1465f5aJoel Fernandes        self.__registerTraceEvents(events) if events else None
148b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio        self.__parseTrace(data_dir, tasks, window, normalize_time,
149b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio                          trace_format)
1508e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        self.__computeTimeSpan()
1518e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
1528224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        # Minimum and Maximum x_time to use for all plots
1538224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        self.x_min = 0
1548224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        self.x_max = self.time_range
1558224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi
1568224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        # Reset x axis time range to full scale
1578224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        t_min = self.window[0]
1588224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        t_max = self.window[1]
1598224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        self.setXTimeRange(t_min, t_max)
1608224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi
161f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi        self.data_frame = TraceData()
162f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi        self._registerDataFrameGetters(self)
163f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi
1646721d173e18d3c56a33fd3f2eb9b50acd675ec27Patrick Bellasi        self.analysis = AnalysisRegister(self)
1656721d173e18d3c56a33fd3f2eb9b50acd675ec27Patrick Bellasi
166f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi    def _registerDataFrameGetters(self, module):
167f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
168f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Internal utility function that looks up getter functions with a "_dfg_"
169f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        prefix in their name and bounds them to the specified module.
170f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
171f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param module: module to which the function is added
172f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type module: class
173f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
174c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        self._log.debug('Registering [%s] local data frames', module)
175f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi        for func in dir(module):
176f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi            if not func.startswith('_dfg_'):
177f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi                continue
178f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi            dfg_name = func.replace('_dfg_', '')
179f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi            dfg_func = getattr(module, func)
180c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.debug('   %s', dfg_name)
181f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi            setattr(self.data_frame, dfg_name, dfg_func)
1828224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi
1838224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi    def setXTimeRange(self, t_min=None, t_max=None):
184f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
185f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Set x axis time range to the specified values.
186f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
187f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param t_min: lower bound
188f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type t_min: int or float
189f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
190f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param t_max: upper bound
191f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type t_max: int or float
192f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
1938224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        if t_min is None:
1948224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi            self.x_min = 0
1958224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        else:
1968224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi            self.x_min = t_min
1978224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        if t_max is None:
1988224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi            self.x_max = self.time_range
1998224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi        else:
2008224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi            self.x_max = t_max
201ba2538fac6e49e4cc1c8f4d525970c0f1ae41ed6Brendan Jackman        self._log.debug('Set plots time range to (%.6f, %.6f)[s]',
202c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                       self.x_min, self.x_max)
2038224360b6dbfe7e28c28ef7a06fa100b2556c656Patrick Bellasi
204e24e02a9565a8ca8f948e15b573eeb808b8ece86Patrick Bellasi    def __registerTraceEvents(self, events):
205f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
206f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Save a copy of the parsed events.
207e09821241ca9697b75c8a53fb0813212202d1c65Patrick Bellasi
208f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param events: single event name or list of events names
209f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type events: str or list(str)
210f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
211e24e02a9565a8ca8f948e15b573eeb808b8ece86Patrick Bellasi        if isinstance(events, basestring):
212e24e02a9565a8ca8f948e15b573eeb808b8ece86Patrick Bellasi            self.events = events.split(' ')
213e24e02a9565a8ca8f948e15b573eeb808b8ece86Patrick Bellasi        elif isinstance(events, list):
214e24e02a9565a8ca8f948e15b573eeb808b8ece86Patrick Bellasi            self.events = events
215e24e02a9565a8ca8f948e15b573eeb808b8ece86Patrick Bellasi        else:
216e24e02a9565a8ca8f948e15b573eeb808b8ece86Patrick Bellasi            raise ValueError('Events must be a string or a list of strings')
217602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        # Register devlib fake cpu_frequency events
218602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        if 'cpu_frequency' in events:
219602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio            self.events.append('cpu_frequency_devlib')
2208e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
221b4ab05a5cc9630963cdfafeab214dcf1661167c7Javi Merino    def __parseTrace(self, path, tasks, window, normalize_time, trace_format):
222f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
223f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Internal method in charge of performing the actual parsing of the
224f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        trace.
225f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
226f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param path: path to the trace folder (or trace file)
227f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type path: str
228f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
229f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param tasks: filter data for the specified tasks only
230f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type tasks: list(str)
231f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
232f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param window: time window to consider when parsing the trace
233f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type window: tuple(int, int)
234f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
235f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param normalize_time: normalize trace time stamps
236f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type normalize_time: bool
237f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
238f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param trace_format: format of the trace. Possible values are:
239f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio            - FTrace
240f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio            - SysTrace
241f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type trace_format: str
242f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
243c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        self._log.debug('Loading [sched] events from trace in [%s]...', path)
2444e260b2b7c2b8718781f2fd4b502391ed1465f5aJoel Fernandes        self._log.debug('Parsing events: %s', self.events if self.events else 'ALL')
24534f5d28c5ef4415709f75afcc636c37a210f4d02Patrick Bellasi        if trace_format.upper() == 'SYSTRACE' or path.endswith('html'):
246ba2538fac6e49e4cc1c8f4d525970c0f1ae41ed6Brendan Jackman            self._log.debug('Parsing SysTrace format...')
247edc53078c056df33a9872fd170d6a66c2acd82d8Javi Merino            trace_class = trappy.SysTrace
24834f5d28c5ef4415709f75afcc636c37a210f4d02Patrick Bellasi            self.trace_format = 'SysTrace'
24934f5d28c5ef4415709f75afcc636c37a210f4d02Patrick Bellasi        elif trace_format.upper() == 'FTRACE':
250ba2538fac6e49e4cc1c8f4d525970c0f1ae41ed6Brendan Jackman            self._log.debug('Parsing FTrace format...')
251edc53078c056df33a9872fd170d6a66c2acd82d8Javi Merino            trace_class = trappy.FTrace
25234f5d28c5ef4415709f75afcc636c37a210f4d02Patrick Bellasi            self.trace_format = 'FTrace'
253d02408964a1d1042b99f51ba01989e8cf02478faJavi Merino        else:
254d02408964a1d1042b99f51ba01989e8cf02478faJavi Merino            raise ValueError("Unknown trace format {}".format(trace_format))
2558e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
2564e260b2b7c2b8718781f2fd4b502391ed1465f5aJoel Fernandes        scope = 'custom' if self.events else 'all'
2574e260b2b7c2b8718781f2fd4b502391ed1465f5aJoel Fernandes        self.ftrace = trace_class(path, scope=scope, events=self.events,
258b4ab05a5cc9630963cdfafeab214dcf1661167c7Javi Merino                                  window=window, normalize_time=normalize_time)
259edc53078c056df33a9872fd170d6a66c2acd82d8Javi Merino
26079a153ae2cea469af80c9d5324c479365980a3adPatrick Bellasi        # Load Functions profiling data
26179a153ae2cea469af80c9d5324c479365980a3adPatrick Bellasi        has_function_stats = self._loadFunctionsStats(path)
26279a153ae2cea469af80c9d5324c479365980a3adPatrick Bellasi
2638e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # Check for events available on the parsed trace
2648e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        self.__checkAvailableEvents()
2658e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        if len(self.available_events) == 0:
26679a153ae2cea469af80c9d5324c479365980a3adPatrick Bellasi            if has_function_stats:
267c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                self._log.info('Trace contains only functions stats')
26879a153ae2cea469af80c9d5324c479365980a3adPatrick Bellasi                return
26979a153ae2cea469af80c9d5324c479365980a3adPatrick Bellasi            raise ValueError('The trace does not contain useful events '
27079a153ae2cea469af80c9d5324c479365980a3adPatrick Bellasi                             'nor function stats')
2718e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
2729ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        # Sanitize cgroup info if any
2739ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        self._sanitize_CgroupAttachTask()
2749ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
2751330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes        # Santization not possible if platform missing
2761330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes        if not self.platform:
2771330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes            # Setup internal data reference to interesting events/dataframes
2781330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes            self._sanitize_SchedLoadAvgCpu()
2791330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes            self._sanitize_SchedLoadAvgTask()
2801330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes            self._sanitize_SchedCpuCapacity()
2811330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes            self._sanitize_SchedBoostCpu()
2821330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes            self._sanitize_SchedBoostTask()
2831330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes            self._sanitize_SchedEnergyDiff()
2841330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes            self._sanitize_SchedOverutilized()
2851330ce42dd0ee40de1ffbf20a0d71b2d431c80e7Joel Fernandes            self._sanitize_CpuFrequency()
2868e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
2878e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        self.__loadTasksNames(tasks)
2888e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
289db6e903112f0f5b2c5c66a63a5d3cdf35d0a6f55Michele Di Giorgio        # Compute plot window
290db6e903112f0f5b2c5c66a63a5d3cdf35d0a6f55Michele Di Giorgio        if not normalize_time:
291db6e903112f0f5b2c5c66a63a5d3cdf35d0a6f55Michele Di Giorgio            start = self.window[0]
292db6e903112f0f5b2c5c66a63a5d3cdf35d0a6f55Michele Di Giorgio            if self.window[1]:
293db6e903112f0f5b2c5c66a63a5d3cdf35d0a6f55Michele Di Giorgio                duration = min(self.ftrace.get_duration(), self.window[1])
294db6e903112f0f5b2c5c66a63a5d3cdf35d0a6f55Michele Di Giorgio            else:
295db6e903112f0f5b2c5c66a63a5d3cdf35d0a6f55Michele Di Giorgio                duration = self.ftrace.get_duration()
296db6e903112f0f5b2c5c66a63a5d3cdf35d0a6f55Michele Di Giorgio            self.window = (self.ftrace.basetime + start,
297db6e903112f0f5b2c5c66a63a5d3cdf35d0a6f55Michele Di Giorgio                           self.ftrace.basetime + duration)
2988e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
29934f5d28c5ef4415709f75afcc636c37a210f4d02Patrick Bellasi    def __checkAvailableEvents(self, key=""):
300f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
301f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Internal method used to build a list of available events.
302f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
303f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param key: key to be used for TRAPpy filtering
304f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type key: str
305f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
30634f5d28c5ef4415709f75afcc636c37a210f4d02Patrick Bellasi        for val in self.ftrace.get_filters(key):
3079c42a5ae0f2a627860e5cc68dc7626da0c5b232fPatrick Bellasi            obj = getattr(self.ftrace, val)
3088e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            if len(obj.data_frame):
3098e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                self.available_events.append(val)
310c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        self._log.debug('Events found on trace:')
3118e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        for evt in self.available_events:
312c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.debug(' - %s', evt)
3138e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
3148e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi    def __loadTasksNames(self, tasks):
315f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
316f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Try to load tasks names using one of the supported events.
317f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
318aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman        :param tasks: list of task names. If None, load all tasks found.
319aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman        :type tasks: list(str) or NoneType
320f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
32167d14d05928174971c4b583efebe17e83fa7067aBrendan Jackman        def load(tasks, event, name_key, pid_key):
32267d14d05928174971c4b583efebe17e83fa7067aBrendan Jackman            df = self._dfg_trace_event(event)
323aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman            if tasks is None:
324aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman                tasks = df[name_key].unique()
32567d14d05928174971c4b583efebe17e83fa7067aBrendan Jackman            self.getTasks(df, tasks, name_key=name_key, pid_key=pid_key)
32667d14d05928174971c4b583efebe17e83fa7067aBrendan Jackman            self._scanTasks(df, name_key=name_key, pid_key=pid_key)
32712d7ce8db067749087819bf59d86b55d98779514Joel Fernandes            self._scanTgids(df)
32867d14d05928174971c4b583efebe17e83fa7067aBrendan Jackman
3298e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        if 'sched_switch' in self.available_events:
33067d14d05928174971c4b583efebe17e83fa7067aBrendan Jackman            load(tasks, 'sched_switch', 'next_comm', 'next_pid')
33167d14d05928174971c4b583efebe17e83fa7067aBrendan Jackman        elif 'sched_load_avg_task' in self.available_events:
33267d14d05928174971c4b583efebe17e83fa7067aBrendan Jackman            load(tasks, 'sched_load_avg_task', 'comm', 'pid')
33367d14d05928174971c4b583efebe17e83fa7067aBrendan Jackman        else:
334c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.warning('Failed to load tasks names from trace events')
3358e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
3368e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi    def hasEvents(self, dataset):
337f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
338f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Returns True if the specified event is present in the parsed trace,
339f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        False otherwise.
340f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
341f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param dataset: trace event name or list of trace events
342f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type dataset: str or list(str)
343f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
3448e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        if dataset in self.available_events:
3458e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            return True
3468e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        return False
3478e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
3488e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi    def __computeTimeSpan(self):
349f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
350f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Compute time axis range, considering all the parsed events.
351f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
3528e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        ts = sys.maxint
3538e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        te = 0
3548e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
35566b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi        for events in self.available_events:
356fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi            df = self._dfg_trace_event(events)
3578e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            if len(df) == 0:
3588e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                continue
3598e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            if (df.index[0]) < ts:
3608e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                ts = df.index[0]
3618e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            if (df.index[-1]) > te:
3628e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                te = df.index[-1]
3638e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            self.time_range = te - ts
3648e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
365ba2538fac6e49e4cc1c8f4d525970c0f1ae41ed6Brendan Jackman        self._log.debug('Collected events spans a %.3f [s] time interval',
366c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                       self.time_range)
3678e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
368877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi        # Build a stat on trace overutilization
369877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi        if self.hasEvents('sched_overutilized'):
370fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi            df = self._dfg_trace_event('sched_overutilized')
371877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi            self.overutilized_time = df[df.overutilized == 1].len.sum()
372877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi            self.overutilized_prc = 100. * self.overutilized_time / self.time_range
373877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi
374ba2538fac6e49e4cc1c8f4d525970c0f1ae41ed6Brendan Jackman            self._log.debug('Overutilized time: %.6f [s] (%.3f%% of trace time)',
375c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                           self.overutilized_time, self.overutilized_prc)
376877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi
37712d7ce8db067749087819bf59d86b55d98779514Joel Fernandes    def _scanTgids(self, df):
37812d7ce8db067749087819bf59d86b55d98779514Joel Fernandes        if not '__tgid' in df.columns:
37912d7ce8db067749087819bf59d86b55d98779514Joel Fernandes            return
38012d7ce8db067749087819bf59d86b55d98779514Joel Fernandes        df = df[['__pid', '__tgid']]
38112d7ce8db067749087819bf59d86b55d98779514Joel Fernandes        df = df.drop_duplicates(keep='first').set_index('__pid')
38212d7ce8db067749087819bf59d86b55d98779514Joel Fernandes        df.rename(columns = { '__pid': 'pid', '__tgid': 'tgid' },
38312d7ce8db067749087819bf59d86b55d98779514Joel Fernandes                              inplace=True)
38412d7ce8db067749087819bf59d86b55d98779514Joel Fernandes        self._pid_tgid = df
38512d7ce8db067749087819bf59d86b55d98779514Joel Fernandes
3863a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi    def _scanTasks(self, df, name_key='comm', pid_key='pid'):
387f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
388f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Extract tasks names and PIDs from the input data frame. The data frame
389f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        should contain a task name column and PID column.
390f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
391f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param df: data frame containing trace events from which tasks names
392f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio            and PIDs will be extracted
393f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type df: :mod:`pandas.DataFrame`
394f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
395b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio        :param name_key: The name of the dataframe columns containing task
396b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            names
397f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type name_key: str
398f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
399f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param pid_key: The name of the dataframe columns containing task PIDs
400f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type pid_key: str
401f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
4024caccc95d240baab7ffb21476de3dcf5a42ef6d6Joel Fernandes        df = df[[name_key, pid_key]].drop_duplicates()
4033a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi        self._tasks_by_name = df.set_index(name_key)
404b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio        self._tasks_by_pid = df.set_index(pid_key)
4053a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi
4063a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi    def getTaskByName(self, name):
407f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
408f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Get the PIDs of all tasks with the specified name.
409f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
410f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param name: task name
411f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type name: str
412f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
4133a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi        if name not in self._tasks_by_name.index:
4143a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi            return []
4153a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi        if len(self._tasks_by_name.ix[name].values) > 1:
4163a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi            return list({task[0] for task in
4173a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi                         self._tasks_by_name.ix[name].values})
4183a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi        return [self._tasks_by_name.ix[name].values[0]]
4193a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi
4203a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi    def getTaskByPid(self, pid):
421f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
422f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Get the names of all tasks with the specified PID.
423f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
424f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param name: task PID
425f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type name: int
426f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
4273a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi        if pid not in self._tasks_by_pid.index:
4283a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi            return []
4293a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi        if len(self._tasks_by_pid.ix[pid].values) > 1:
4303a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi            return list({task[0] for task in
4313a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi                         self._tasks_by_pid.ix[pid].values})
4323a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi        return [self._tasks_by_pid.ix[pid].values[0]]
4333a044edddce5a950d10a2ed95d96c9bd72d2363aPatrick Bellasi
43412d7ce8db067749087819bf59d86b55d98779514Joel Fernandes    def getTgidFromPid(self, pid):
43512d7ce8db067749087819bf59d86b55d98779514Joel Fernandes        return _pid_tgid.ix[pid].values[0]
43612d7ce8db067749087819bf59d86b55d98779514Joel Fernandes
4378e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi    def getTasks(self, dataframe=None,
438b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio                 task_names=None, name_key='comm', pid_key='pid'):
439f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
440f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Helper function to get PIDs of specified tasks.
441f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
44207785500fa9f857ba507fc3357ee13636c3e6129Brendan Jackman        This method can take a Pandas dataset in input to be used to fiter out
443f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        the PIDs of all the specified tasks. If a dataset is not provided,
444f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        previously filtered PIDs are returned.
445f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
44607785500fa9f857ba507fc3357ee13636c3e6129Brendan Jackman        If a list of task names is not provided, all tasks detected in the trace
44707785500fa9f857ba507fc3357ee13636c3e6129Brendan Jackman        will be used. The specified dataframe must provide at least two columns
44807785500fa9f857ba507fc3357ee13636c3e6129Brendan Jackman        reporting the task name and the task PID. The default values of this
44907785500fa9f857ba507fc3357ee13636c3e6129Brendan Jackman        colums could be specified using the provided parameters.
450f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
451aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman        :param dataframe: A Pandas dataframe containing at least 'name_key' and
452aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman            'pid_key' columns. If None, the all PIDs are returned.
453f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type dataframe: :mod:`pandas.DataFrame`
454f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
455aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman        :param task_names: The list of tasks to get the PID of (default: all
456aa9f74b7a17e863acbfa558aee6b5a3ac997c0b8Brendan Jackman            tasks)
457f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type task_names: list(str)
458f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
459b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio        :param name_key: The name of the dataframe columns containing task
460b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            names
461f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type name_key: str
462f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
463f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param pid_key: The name of the dataframe columns containing task PIDs
464f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type pid_key: str
465f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
4668e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        if task_names is None:
4678e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            task_names = self.tasks.keys()
468b196b8cd11030c4353903f18deb30905e406074fBrendan Jackman        if dataframe is None:
469b196b8cd11030c4353903f18deb30905e406074fBrendan Jackman            return {k: v for k, v in  self.tasks.iteritems() if k in task_names}
470b196b8cd11030c4353903f18deb30905e406074fBrendan Jackman        df = dataframe
471c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        self._log.debug('Lookup dataset for tasks...')
4728e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        for tname in task_names:
473c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.debug('Lookup for task [%s]...', tname)
474b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            results = df[df[name_key] == tname][[name_key, pid_key]]
475b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            if len(results) == 0:
476c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                self._log.error('  task %16s NOT found', tname)
4778e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                continue
4788e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            (name, pid) = results.head(1).values[0]
479b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            if name != tname:
480c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                self._log.error('  task %16s NOT found', tname)
4818e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                continue
482b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            if tname not in self.tasks:
4838e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                self.tasks[tname] = {}
4848e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            pids = list(results[pid_key].unique())
4858e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            self.tasks[tname]['pid'] = pids
486c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.debug('  task %16s found, pid: %s',
487c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                            tname, self.tasks[tname]['pid'])
4888e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        return self.tasks
4898e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
490fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi
491b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio###############################################################################
492fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi# DataFrame Getter Methods
493b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio###############################################################################
494fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi
4958e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi    def df(self, event):
496f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
497f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Get a dataframe containing all occurrences of the specified trace event
498f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        in the parsed trace.
499f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
500f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param event: Trace event name
501f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type event: str
502f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
503fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        warnings.simplefilter('always', DeprecationWarning) #turn off filter
504fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        warnings.warn("\n\tUse of Trace::df() is deprecated and will be soon removed."
505fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi                      "\n\tUse Trace::data_frame.trace_event(event_name) instead.",
506fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi                      category=DeprecationWarning)
507fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        warnings.simplefilter('default', DeprecationWarning) #reset filter
508fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        return self._dfg_trace_event(event)
509fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi
510fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi    def _dfg_trace_event(self, event):
5118e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        """
512f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Get a dataframe containing all occurrences of the specified trace event
513f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        in the parsed trace.
514f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
515f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param event: Trace event name
516f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type event: str
5178e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        """
518f0ca15ec410a73b8f223a48e1be234c8561c6c2cPatrick Bellasi        if self.data_dir is None:
5198e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            raise ValueError("trace data not (yet) loaded")
5209c42a5ae0f2a627860e5cc68dc7626da0c5b232fPatrick Bellasi        if self.ftrace and hasattr(self.ftrace, event):
5219c42a5ae0f2a627860e5cc68dc7626da0c5b232fPatrick Bellasi            return getattr(self.ftrace, event).data_frame
522b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio        raise ValueError('Event [{}] not supported. '
523b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio                         'Supported events are: {}'
52466b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi                         .format(event, self.available_events))
5258e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
526ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi    def _dfg_functions_stats(self, functions=None):
527ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        """
528ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        Get a DataFrame of specified kernel functions profile data
529ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi
530ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        For each profiled function a DataFrame is returned which reports stats
531ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        on kernel functions execution time. The reported stats are per-CPU and
532ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        includes: number of times the function has been executed (hits),
533ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        average execution time (avg), overall execution time (time) and samples
534ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        variance (s_2).
535ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        By default returns a DataFrame of all the functions profiled.
536ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi
537ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        :param functions: the name of the function or a list of function names
538ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi                          to report
539f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type functions: str or list(str)
540ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        """
541ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        if not hasattr(self, '_functions_stats_df'):
542ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi            return None
543ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        df = self._functions_stats_df
544ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        if not functions:
545ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi            return df
546ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        return df.loc[df.index.get_level_values(1).isin(listify(functions))]
547ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi
5480e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes    # cgroup_attach_task with just merged fake and real events
5490e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes    def _cgroup_attach_task(self):
5509ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        cgroup_events = ['cgroup_attach_task', 'cgroup_attach_task_devlib']
5519ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        df = None
5529ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
553d0253ba206620c79c0abf2adfb27fc921ca05d81Joel Fernandes        if set(cgroup_events).isdisjoint(set(self.available_events)):
554d0253ba206620c79c0abf2adfb27fc921ca05d81Joel Fernandes            self._log.error('atleast one of {} is needed for cgroup_attach_task event generation'.format(cgroup_events))
555d0253ba206620c79c0abf2adfb27fc921ca05d81Joel Fernandes            return None
556d0253ba206620c79c0abf2adfb27fc921ca05d81Joel Fernandes
5579ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        for cev in cgroup_events:
5589ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            if not cev in self.available_events:
5599ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                continue
5609ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            cdf = self._dfg_trace_event(cev)
561e82d56694c9f909009d7348f8c9265fd4810eea9Joel Fernandes            cdf = cdf[['__line', 'pid', 'controller', 'cgroup']]
5629ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            if not isinstance(df, pd.DataFrame):
5639ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                df = cdf
5649ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            else:
5659ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                df = pd.concat([cdf, df])
5669ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
5670e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        # Always drop na since this DF is used as secondary
5680e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        df.dropna(inplace=True, how='any')
5699ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        return df
5709ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
571dbe282d7a48bbb8b93ff1e2ee944f6ff4fbcfebfJoel Fernandes    @memoized
5720e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes    def _dfg_cgroup_attach_task(self, controllers = ['schedtune', 'cpuset']):
5730e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        # Since fork doesn't result in attach events, generate fake attach events
5740e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        # The below mechanism doesn't work to propogate nested fork levels:
5750e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        # For ex:
5760e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        # cgroup_attach_task: pid=1166
5770e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        # fork: pid=1166 child_pid=2222  <-- fake attach generated
5780e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        # fork: pid=2222 child_pid=3333  <-- fake attach not generated
5790e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        def fork_add_cgroup(fdf, cdf, controller):
5800e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes            cdf = cdf[cdf['controller'] == controller]
5810e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes            ret_df = trappy.utils.merge_dfs(fdf, cdf, pivot='pid')
5820e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes            return ret_df
5830e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes
584d0253ba206620c79c0abf2adfb27fc921ca05d81Joel Fernandes        if not 'sched_process_fork' in self.available_events:
585d0253ba206620c79c0abf2adfb27fc921ca05d81Joel Fernandes            self._log.error('sched_process_fork is mandatory to get proper cgroup_attach events')
586d0253ba206620c79c0abf2adfb27fc921ca05d81Joel Fernandes            return None
5870e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        fdf = self._dfg_trace_event('sched_process_fork')
5880e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes
5890e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        forks_len = len(fdf)
5900e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        forkdf = fdf
5910e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        cdf = self._cgroup_attach_task()
5920e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        for idx, c in enumerate(controllers):
5930e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes            fdf = fork_add_cgroup(fdf, cdf, c)
5940e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes            if (idx != (len(controllers) - 1)):
595304c111f5a33409220adf13bb9c69cd2727f5aa0Joel Fernandes                fdf = pd.concat([fdf, forkdf]).sort_values(by='__line')
5960e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes
5970e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        fdf = fdf[['__line', 'child_pid', 'controller', 'cgroup']]
5980e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        fdf.rename(columns = { 'child_pid': 'pid' }, inplace=True)
5990e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes
6000e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        # Always drop na since this DF is used as secondary
6010e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        fdf.dropna(inplace=True, how='any')
6020e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes
6030e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        new_forks_len = len(fdf) / len(controllers)
6040e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes
605304c111f5a33409220adf13bb9c69cd2727f5aa0Joel Fernandes        fdf = pd.concat([fdf, cdf]).sort_values(by='__line')
6060e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes
6070e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        if new_forks_len < forks_len:
6080e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes            dropped = forks_len - new_forks_len
6090e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes            self._log.info("Couldn't attach all forks cgroup with-attach events ({} dropped)".format(dropped))
6100e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes        return fdf
6110e2f35b9031b3f2d2701ebfb7c7a73bb81b2111bJoel Fernandes
612dbe282d7a48bbb8b93ff1e2ee944f6ff4fbcfebfJoel Fernandes    @memoized
61365039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes    def _dfg_sched_switch_cgroup(self, controllers = ['schedtune', 'cpuset']):
61465039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes        def sched_switch_add_cgroup(sdf, cdf, controller, direction):
61565039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes            cdf = cdf[cdf['controller'] == controller]
61665039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes
61765039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes            ret_df = sdf.rename(columns = { direction + '_pid': 'pid' })
61865039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes            ret_df = trappy.utils.merge_dfs(ret_df, cdf, pivot='pid')
61965039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes            ret_df.rename(columns = { 'pid': direction + '_pid' }, inplace=True)
62065039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes
62165039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes            ret_df.drop('controller', axis=1, inplace=True)
62265039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes            ret_df.rename(columns = { 'cgroup': direction + '_' + controller }, inplace=True)
62365039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes            return ret_df
62465039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes
625d0253ba206620c79c0abf2adfb27fc921ca05d81Joel Fernandes        if not 'sched_switch' in self.available_events:
626d0253ba206620c79c0abf2adfb27fc921ca05d81Joel Fernandes            self._log.error('sched_switch is mandatory to generate sched_switch_cgroup event')
627d0253ba206620c79c0abf2adfb27fc921ca05d81Joel Fernandes            return None
62865039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes        sdf = self._dfg_trace_event('sched_switch')
62965039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes        cdf = self._dfg_cgroup_attach_task()
63065039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes
63165039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes        for c in controllers:
63265039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes            sdf = sched_switch_add_cgroup(sdf, cdf, c, 'next')
63365039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes            sdf = sched_switch_add_cgroup(sdf, cdf, c, 'prev')
63465039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes
63512d7ce8db067749087819bf59d86b55d98779514Joel Fernandes        # Augment with TGID information
63612d7ce8db067749087819bf59d86b55d98779514Joel Fernandes        sdf = sdf.join(self._pid_tgid, on='next_pid').rename(columns = {'tgid': 'next_tgid'})
63712d7ce8db067749087819bf59d86b55d98779514Joel Fernandes        sdf = sdf.join(self._pid_tgid, on='prev_pid').rename(columns = {'tgid': 'prev_tgid'})
638063cbf3392db88e1dbe454be1f25db4640b63423Joel Fernandes
639063cbf3392db88e1dbe454be1f25db4640b63423Joel Fernandes        df = self._tasks_by_pid.rename(columns = { 'next_comm': 'comm' })
640063cbf3392db88e1dbe454be1f25db4640b63423Joel Fernandes        sdf = sdf.join(df, on='next_tgid').rename(columns = {'comm': 'next_tgid_comm'})
641063cbf3392db88e1dbe454be1f25db4640b63423Joel Fernandes        sdf = sdf.join(df, on='prev_tgid').rename(columns = {'comm': 'prev_tgid_comm'})
64265039ca96253a8791c682fd35910ca40b3e8cc5bJoel Fernandes        return sdf
6435824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi
644b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio###############################################################################
645fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi# Trace Events Sanitize Methods
646b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio###############################################################################
647fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi
648c5d46e8caeb638b88b75557a30262a6912a86629Patrick Bellasi    def _sanitize_SchedCpuCapacity(self):
649f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
650f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Add more columns to cpu_capacity data frame if the energy model is
651f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        available.
652f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
65366b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi        if not self.hasEvents('cpu_capacity') \
65466b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi           or 'nrg_model' not in self.platform:
655680ad8b9c70754064b686e1fcea6db022bc5e152Patrick Bellasi            return
656680ad8b9c70754064b686e1fcea6db022bc5e152Patrick Bellasi
657fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        df = self._dfg_trace_event('cpu_capacity')
65866b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi
6598e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # Add column with LITTLE and big CPUs max capacities
6608e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        nrg_model = self.platform['nrg_model']
6618e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        max_lcap = nrg_model['little']['cpu']['cap_max']
6628e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        max_bcap = nrg_model['big']['cpu']['cap_max']
6638e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        df['max_capacity'] = np.select(
6648e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                [df.cpu.isin(self.platform['clusters']['little'])],
6658e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                [max_lcap], max_bcap)
6668e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # Add LITTLE and big CPUs "tipping point" threshold
6678e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        tip_lcap = 0.8 * max_lcap
6688e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        tip_bcap = 0.8 * max_bcap
6698e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        df['tip_capacity'] = np.select(
6708e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                [df.cpu.isin(self.platform['clusters']['little'])],
6718e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                [tip_lcap], tip_bcap)
6728e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
673997f0ca86a715c99bd1adce42c1812d650ea516cPatrick Bellasi    def _sanitize_SchedLoadAvgCpu(self):
674f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
675f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        If necessary, rename certain signal names from v5.0 to v5.1 format.
676f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
67766b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi        if not self.hasEvents('sched_load_avg_cpu'):
67866b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi            return
679fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        df = self._dfg_trace_event('sched_load_avg_cpu')
680997f0ca86a715c99bd1adce42c1812d650ea516cPatrick Bellasi        if 'utilization' in df:
681b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            df.rename(columns={'utilization': 'util_avg'}, inplace=True)
682b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            df.rename(columns={'load': 'load_avg'}, inplace=True)
683997f0ca86a715c99bd1adce42c1812d650ea516cPatrick Bellasi
684c5d46e8caeb638b88b75557a30262a6912a86629Patrick Bellasi    def _sanitize_SchedLoadAvgTask(self):
685f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
686f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        If necessary, rename certain signal names from v5.0 to v5.1 format.
687f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
68866b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi        if not self.hasEvents('sched_load_avg_task'):
68966b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi            return
690fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        df = self._dfg_trace_event('sched_load_avg_task')
69117c7721066c57695ba43bac0c4170c324093d693Patrick Bellasi        if 'utilization' in df:
692b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            df.rename(columns={'utilization': 'util_avg'}, inplace=True)
693b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            df.rename(columns={'load': 'load_avg'}, inplace=True)
694b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            df.rename(columns={'avg_period': 'period_contrib'}, inplace=True)
695b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            df.rename(columns={'runnable_avg_sum': 'load_sum'}, inplace=True)
696b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            df.rename(columns={'running_avg_sum': 'util_sum'}, inplace=True)
6978e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        df['cluster'] = np.select(
6988e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                [df.cpu.isin(self.platform['clusters']['little'])],
6998e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi                ['LITTLE'], 'big')
7009d69537e219bc1052a28be114cb14ad47cc22c8fPatrick Bellasi        # Add a column which represents the max capacity of the smallest
7019d69537e219bc1052a28be114cb14ad47cc22c8fPatrick Bellasi        # clustre which can accomodate the task utilization
702b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio        little_cap = self.platform['nrg_model']['little']['cpu']['cap_max']
703b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio        big_cap = self.platform['nrg_model']['big']['cpu']['cap_max']
7049d69537e219bc1052a28be114cb14ad47cc22c8fPatrick Bellasi        df['min_cluster_cap'] = df.util_avg.map(
705b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            lambda util_avg: big_cap if util_avg > little_cap else little_cap
706b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio        )
7078e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
708c5d46e8caeb638b88b75557a30262a6912a86629Patrick Bellasi    def _sanitize_SchedBoostCpu(self):
709f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
710f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Add a boosted utilization signal as the sum of utilization and margin.
711f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
712f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Also, if necessary, rename certain signal names from v5.0 to v5.1
713f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        format.
714f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
71566b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi        if not self.hasEvents('sched_boost_cpu'):
71666b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi            return
717fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        df = self._dfg_trace_event('sched_boost_cpu')
71817c7721066c57695ba43bac0c4170c324093d693Patrick Bellasi        if 'usage' in df:
719b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            df.rename(columns={'usage': 'util'}, inplace=True)
72017c7721066c57695ba43bac0c4170c324093d693Patrick Bellasi        df['boosted_util'] = df['util'] + df['margin']
72117c7721066c57695ba43bac0c4170c324093d693Patrick Bellasi
722c5d46e8caeb638b88b75557a30262a6912a86629Patrick Bellasi    def _sanitize_SchedBoostTask(self):
723f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
724f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Add a boosted utilization signal as the sum of utilization and margin.
725f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
726f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Also, if necessary, rename certain signal names from v5.0 to v5.1
727f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        format.
728f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
72966b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi        if not self.hasEvents('sched_boost_task'):
73066b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi            return
731fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        df = self._dfg_trace_event('sched_boost_task')
732418cfb6405256f744cc3023fd7a5fcbff1f757a7Patrick Bellasi        if 'utilization' in df:
733418cfb6405256f744cc3023fd7a5fcbff1f757a7Patrick Bellasi            # Convert signals name from to v5.1 format
734b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            df.rename(columns={'utilization': 'util'}, inplace=True)
735418cfb6405256f744cc3023fd7a5fcbff1f757a7Patrick Bellasi        df['boosted_util'] = df['util'] + df['margin']
7368e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
737c5d46e8caeb638b88b75557a30262a6912a86629Patrick Bellasi    def _sanitize_SchedEnergyDiff(self):
738f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
739f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        If a energy model is provided, some signals are added to the
740f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        sched_energy_diff trace event data frame.
7415439b256782dc2a902146b5bea9b957b9dbdea58Brendan Jackman
7425439b256782dc2a902146b5bea9b957b9dbdea58Brendan Jackman        Also convert between existing field name formats for sched_energy_diff
743f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
74466b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi        if not self.hasEvents('sched_energy_diff') \
74566b843f6127403f8a6933f8d9f90576a11ac998dPatrick Bellasi           or 'nrg_model' not in self.platform:
746680ad8b9c70754064b686e1fcea6db022bc5e152Patrick Bellasi            return
7478e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        nrg_model = self.platform['nrg_model']
7488e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        em_lcluster = nrg_model['little']['cluster']
7498e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        em_bcluster = nrg_model['big']['cluster']
7508e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        em_lcpu = nrg_model['little']['cpu']
7518e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        em_bcpu = nrg_model['big']['cpu']
7528e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        lcpus = len(self.platform['clusters']['little'])
7538e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        bcpus = len(self.platform['clusters']['big'])
7548e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        SCHED_LOAD_SCALE = 1024
7558e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
7568e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        power_max = em_lcpu['nrg_max'] * lcpus + em_bcpu['nrg_max'] * bcpus + \
757b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            em_lcluster['nrg_max'] + em_bcluster['nrg_max']
7588f7f28b61f6f4bf6d43f2919886a4bc51578a659Brendan Jackman        self._log.debug(
7598f7f28b61f6f4bf6d43f2919886a4bc51578a659Brendan Jackman            "Maximum estimated system energy: {0:d}".format(power_max))
7608e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
761fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        df = self._dfg_trace_event('sched_energy_diff')
7625439b256782dc2a902146b5bea9b957b9dbdea58Brendan Jackman
7633e798bfa116035c12353783e803516880794976aPatrick Bellasi        translations = {'nrg_d' : 'nrg_diff',
7643e798bfa116035c12353783e803516880794976aPatrick Bellasi                        'utl_d' : 'usage_delta',
7653e798bfa116035c12353783e803516880794976aPatrick Bellasi                        'payoff' : 'nrg_payoff'
7663e798bfa116035c12353783e803516880794976aPatrick Bellasi        }
7675439b256782dc2a902146b5bea9b957b9dbdea58Brendan Jackman        df.rename(columns=translations, inplace=True)
7685439b256782dc2a902146b5bea9b957b9dbdea58Brendan Jackman
7698e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        df['nrg_diff_pct'] = SCHED_LOAD_SCALE * df.nrg_diff / power_max
7708e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
7718e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # Tag columns by usage_delta
7728e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        ccol = df.usage_delta
7738e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        df['usage_delta_group'] = np.select(
7748e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            [ccol < 150, ccol < 400, ccol < 600],
7758e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            ['< 150', '< 400', '< 600'], '>= 600')
7768e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
7778e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        # Tag columns by nrg_payoff
7788e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        ccol = df.nrg_payoff
7798e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi        df['nrg_payoff_group'] = np.select(
7808e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi            [ccol > 2e9, ccol > 0, ccol > -2e9],
781b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            ['Optimal Accept', 'SchedTune Accept', 'SchedTune Reject'],
782b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio            'Suboptimal Reject')
7838e6284e58870ec8b4e5fd02186668934287a22f5Patrick Bellasi
784877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi    def _sanitize_SchedOverutilized(self):
785f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """ Add a column with overutilized status duration. """
786877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi        if not self.hasEvents('sched_overutilized'):
787877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi            return
788fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        df = self._dfg_trace_event('sched_overutilized')
789877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi        df['start'] = df.index
790877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi        df['len'] = (df.start - df.start.shift()).fillna(0).shift(-1)
791877f3408c7f7705157f6c533957c30275e6972a2Patrick Bellasi        df.drop('start', axis=1, inplace=True)
7929e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio
7939ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes    # Sanitize cgroup information helper
7949ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes    def _helper_sanitize_CgroupAttachTask(self, df, allowed_cgroups, controller_id_name):
795b27ac8f8ba1027eebe95725edc45aeaac1bd9e27Joel Fernandes        # Drop rows that aren't in the root-id -> name map
796b27ac8f8ba1027eebe95725edc45aeaac1bd9e27Joel Fernandes        df = df[df['dst_root'].isin(controller_id_name.keys())]
797b27ac8f8ba1027eebe95725edc45aeaac1bd9e27Joel Fernandes
7989ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        def get_cgroup_name(path, valid_names):
7999ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            name = os.path.basename(path)
8009ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            name = 'root' if not name in valid_names else name
8019ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            return name
8029ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
8039ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        def get_cgroup_names(rows):
8049ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            ret = []
8059ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            for r in rows.iterrows():
8069ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                 ret.append(get_cgroup_name(r[1]['dst_path'], allowed_cgroups))
8079ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            return ret
8089ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
8099ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        def get_controller_names(rows):
8109ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            ret = []
8119ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            for r in rows.iterrows():
8129ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                 ret.append(controller_id_name[r[1]['dst_root']])
8139ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            return ret
8149ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
8159ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        # Sanitize cgroup names
8169ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        # cgroup column isn't in mainline, add it in
8179ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        # its already added for some out of tree kernels so check first
8189ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        if not 'cgroup' in df.columns:
8199ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            if not 'dst_path' in df.columns:
8209ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                raise RuntimeError('Cant santize cgroup DF, need dst_path')
8219ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            df = df.assign(cgroup = get_cgroup_names)
8229ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
8239ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        # Sanitize controller names
8249ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        if not 'controller' in df.columns:
8259ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            if not 'dst_root' in df.columns:
8269ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                raise RuntimeError('Cant santize cgroup DF, need dst_path')
8279ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            df = df.assign(controller = get_controller_names)
8289ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
8299ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        return df
8309ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
8319ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes    def _sanitize_CgroupAttachTask(self):
8329ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        def sanitize_cgroup_event(name):
8339ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            if not name in self.available_events:
8349ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                return
8355bb44a7d61d5bb160d8207cf085ea5375bfe8f2dJoel Fernandes
8369ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            df = self._dfg_trace_event(name)
8375bb44a7d61d5bb160d8207cf085ea5375bfe8f2dJoel Fernandes
8385bb44a7d61d5bb160d8207cf085ea5375bfe8f2dJoel Fernandes            if len(df.groupby(level=0).filter(lambda x: len(x) > 1)) > 0:
8395bb44a7d61d5bb160d8207cf085ea5375bfe8f2dJoel Fernandes                self._log.warning('Timstamp Collisions seen in {} event!'.format(name))
8405bb44a7d61d5bb160d8207cf085ea5375bfe8f2dJoel Fernandes
8419ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            df = self._helper_sanitize_CgroupAttachTask(df, self.cgroup_info['cgroups'],
8429ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes                                              self.cgroup_info['controller_ids'])
8439ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes            getattr(self.ftrace, name).data_frame = df
8449ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        sanitize_cgroup_event('cgroup_attach_task')
8459ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes        sanitize_cgroup_event('cgroup_attach_task_devlib')
8469ceeb1caf9293d1b5e8f5b6d23c0ed5079dfd9c8Joel Fernandes
84747895783a9a4b581c1c7aa15caf2a407201948dbPatrick Bellasi    def _chunker(self, seq, size):
848f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
849f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Given a data frame or a series, generate a sequence of chunks of the
850f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        given size.
851f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
852f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param seq: data to be split into chunks
853f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type seq: :mod:`pandas.Series` or :mod:`pandas.DataFrame`
854f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
855f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param size: size of each chunk
856f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type size: int
857f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
8589e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio        return (seq.iloc[pos:pos + size] for pos in range(0, len(seq), size))
8599e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio
86047895783a9a4b581c1c7aa15caf2a407201948dbPatrick Bellasi    def _sanitize_CpuFrequency(self):
861f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
862f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Verify that all platform reported clusters are frequency coherent (i.e.
863f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        frequency scaling is performed at a cluster level).
864f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
865602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        if not self.hasEvents('cpu_frequency_devlib'):
8669e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio            return
867602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio
868602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        devlib_freq = self._dfg_trace_event('cpu_frequency_devlib')
869602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        devlib_freq.rename(columns={'cpu_id':'cpu'}, inplace=True)
870602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        devlib_freq.rename(columns={'state':'frequency'}, inplace=True)
871602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio
872fae44f68c443723378b83ab146fc20bc5a844a7dPatrick Bellasi        df = self._dfg_trace_event('cpu_frequency')
8739e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio        clusters = self.platform['clusters']
874602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio
875602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        # devlib always introduces fake cpu_frequency events, in case the
876602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        # OS has not generated cpu_frequency envets there are the only
877602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        # frequency events to report
878602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        if len(df) == 0:
879602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio            # Register devlib injected events as 'cpu_frequency' events
880602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio            setattr(self.ftrace.cpu_frequency, 'data_frame', devlib_freq)
881602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio            df = devlib_freq
882602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio            self.available_events.append('cpu_frequency')
883602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio
884602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        # make sure fake cpu_frequency events are never interleaved with
885602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        # OS generated events
886602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        else:
887602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio            if len(devlib_freq) > 0:
88893f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi
88993f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                # Frequencies injection is done in a per-cluster based.
89093f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                # This is based on the assumption that clusters are
89193f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                # frequency choerent.
89293f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                # For each cluster we inject devlib events only if
89393f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                # these events does not overlaps with os-generated ones.
89493f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi
89593f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                # Inject "initial" devlib frequencies
89693f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                os_df = df
89793f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                dl_df = devlib_freq.iloc[:self.platform['cpus_count']]
89893f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                for _,c in self.platform['clusters'].iteritems():
89993f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    dl_freqs = dl_df[dl_df.cpu.isin(c)]
90093f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    os_freqs = os_df[os_df.cpu.isin(c)]
90193f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    self._log.debug("First freqs for %s:\n%s", c, dl_freqs)
90293f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    # All devlib events "before" os-generated events
90393f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    self._log.debug("Min os freq @: %s", os_freqs.index.min())
90493f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    if os_freqs.empty or \
90593f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                       os_freqs.index.min() > dl_freqs.index.max():
90693f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                        self._log.debug("Insert devlib freqs for %s", c)
90793f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                        df = pd.concat([dl_freqs, df])
90893f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi
90993f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                # Inject "final" devlib frequencies
91093f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                os_df = df
91193f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                dl_df = devlib_freq.iloc[self.platform['cpus_count']:]
91293f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                for _,c in self.platform['clusters'].iteritems():
91393f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    dl_freqs = dl_df[dl_df.cpu.isin(c)]
91493f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    os_freqs = os_df[os_df.cpu.isin(c)]
91593f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    self._log.debug("Last freqs for %s:\n%s", c, dl_freqs)
91693f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    # All devlib events "after" os-generated events
91793f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    self._log.debug("Max os freq @: %s", os_freqs.index.max())
91893f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                    if os_freqs.empty or \
91993f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                       os_freqs.index.max() < dl_freqs.index.min():
92093f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                        self._log.debug("Append devlib freqs for %s", c)
92193f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                        df = pd.concat([df, dl_freqs])
92293f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi
92393f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi                df.sort_index(inplace=True)
92493f88632840e58b03a2600d1956ea064a13f69e5Patrick Bellasi
925602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio            setattr(self.ftrace.cpu_frequency, 'data_frame', df)
926602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio
927602f6c82b6cf07634b51451c59dbb23bd768a8b9Michele Di Giorgio        # Frequency Coherency Check
928b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio        for _, cpus in clusters.iteritems():
9299e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio            cluster_df = df[df.cpu.isin(cpus)]
93047895783a9a4b581c1c7aa15caf2a407201948dbPatrick Bellasi            for chunk in self._chunker(cluster_df, len(cpus)):
9319e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio                f = chunk.iloc[0].frequency
9329e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio                if any(chunk.frequency != f):
933c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                    self._log.warning('Cluster Frequency is not coherent! '
934c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                                      'Failure in [cpu_frequency] events at:')
935c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                    self._log.warning(chunk)
9369e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio                    self.freq_coherency = False
9379e7a48bf52c1e0cf136dfdce5ba7ecc12768a092Michele Di Giorgio                    return
938c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        self._log.info('Platform clusters verified to be Frequency coherent')
93947895783a9a4b581c1c7aa15caf2a407201948dbPatrick Bellasi
940b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio###############################################################################
941f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi# Utility Methods
942b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio###############################################################################
943f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi
9445824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi    def integrate_square_wave(self, sq_wave):
9455824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        """
9465824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        Compute the integral of a square wave time series.
9475824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi
9485824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        :param sq_wave: square wave assuming only 1.0 and 0.0 values
9495824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        :type sq_wave: :mod:`pandas.Series`
9505824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        """
9515824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        sq_wave.iloc[-1] = 0.0
9525824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        # Compact signal to obtain only 1-0-1-0 sequences
9535824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        comp_sig = sq_wave.loc[sq_wave.shift() != sq_wave]
9545824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        # First value for computing the difference must be a 1
9555824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        if comp_sig.iloc[0] == 0.0:
9565824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi            return sum(comp_sig.iloc[2::2].index - comp_sig.iloc[1:-1:2].index)
9575824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi        else:
9585824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi            return sum(comp_sig.iloc[1::2].index - comp_sig.iloc[:-1:2].index)
9595824ef082910363b2258cedecdabc60f6a2ea4c7Patrick Bellasi
960ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi    def _loadFunctionsStats(self, path='trace.stats'):
961f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
962f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        Read functions profiling file and build a data frame containing all
963f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        relevant data.
964f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio
965f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :param path: path to the functions profiling trace file
966f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        :type path: str
967f93efbe21b68cf8f422a221d81fcd723a2ced356Michele Di Giorgio        """
968ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        if os.path.isdir(path):
969ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi            path = os.path.join(path, 'trace.stats')
970ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        if path.endswith('dat') or path.endswith('html'):
971ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi            pre, ext = os.path.splitext(path)
972ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi            path = pre + '.stats'
973ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        if not os.path.isfile(path):
974ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi            return False
975ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi
976ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        # Opening functions profiling JSON data file
977c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        self._log.debug('Loading functions profiling data from [%s]...', path)
978ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        with open(os.path.join(path), 'r') as fh:
979ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi            trace_stats = json.load(fh)
980ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi
981ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        # Build DataFrame of function stats
982ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        frames = {}
983ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        for cpu, data in trace_stats.iteritems():
984ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi            frames[int(cpu)] = pd.DataFrame.from_dict(data, orient='index')
985ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi
986ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        # Build and keep track of the DataFrame
987b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio        self._functions_stats_df = pd.concat(frames.values(),
988b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio                                             keys=frames.keys())
989ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi
990ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi        return len(self._functions_stats_df) > 0
991ff49ae1f0c3dcf5eba2b063c3571b692355f5320Patrick Bellasi
992691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio    @memoized
993691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio    def getCPUActiveSignal(self, cpu):
994691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        """
995691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        Build a square wave representing the active (i.e. non-idle) CPU time,
996691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        i.e.:
99724494084386ca0664120df7aeccd7f3c9bde5057Brendan Jackman
99824494084386ca0664120df7aeccd7f3c9bde5057Brendan Jackman          cpu_active[t] == 1 if the CPU is reported to be non-idle by cpuidle at
99924494084386ca0664120df7aeccd7f3c9bde5057Brendan Jackman          time t
100024494084386ca0664120df7aeccd7f3c9bde5057Brendan Jackman          cpu_active[t] == 0 otherwise
1001691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1002691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        :param cpu: CPU ID
1003691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        :type cpu: int
1004691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1005ac4e391db53c274712f31e7a1048a27bb1a5d7cfBrendan Jackman        :returns: A :mod:`pandas.Series` or ``None`` if the trace contains no
1006ac4e391db53c274712f31e7a1048a27bb1a5d7cfBrendan Jackman                  "cpu_idle" events
1007691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        """
1008691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        if not self.hasEvents('cpu_idle'):
1009c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.warning('Events [cpu_idle] not found, '
1010c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                              'cannot compute CPU active signal!')
1011691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio            return None
1012691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1013691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        idle_df = self._dfg_trace_event('cpu_idle')
1014691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        cpu_df = idle_df[idle_df.cpu_id == cpu]
1015691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1016691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        cpu_active = cpu_df.state.apply(
1017691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio            lambda s: 1 if s == NON_IDLE_STATE else 0
1018691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        )
1019691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1020691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        start_time = 0.0
1021691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        if not self.ftrace.normalized_time:
1022691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio            start_time = self.ftrace.basetime
102320a1ac25fb898bd3b045b3aecf894df04c93fc80Brendan Jackman
102420a1ac25fb898bd3b045b3aecf894df04c93fc80Brendan Jackman        if cpu_active.empty:
102520a1ac25fb898bd3b045b3aecf894df04c93fc80Brendan Jackman            cpu_active = pd.Series([0], index=[start_time])
102620a1ac25fb898bd3b045b3aecf894df04c93fc80Brendan Jackman        elif cpu_active.index[0] != start_time:
1027691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio            entry_0 = pd.Series(cpu_active.iloc[0] ^ 1, index=[start_time])
1028691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio            cpu_active = pd.concat([entry_0, cpu_active])
1029691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1030d1ed4c86118d4a2231231d185b423e90ac38df56Patrick Bellasi        # Fix sequences of wakeup/sleep events reported with the same index
1031d1ed4c86118d4a2231231d185b423e90ac38df56Patrick Bellasi        return handle_duplicate_index(cpu_active)
1032d1ed4c86118d4a2231231d185b423e90ac38df56Patrick Bellasi
1033691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1034691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio    @memoized
1035691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio    def getClusterActiveSignal(self, cluster):
1036691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        """
1037691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        Build a square wave representing the active (i.e. non-idle) cluster
1038691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        time, i.e.:
103924494084386ca0664120df7aeccd7f3c9bde5057Brendan Jackman
104024494084386ca0664120df7aeccd7f3c9bde5057Brendan Jackman          cluster_active[t] == 1 if at least one CPU is reported to be non-idle
104124494084386ca0664120df7aeccd7f3c9bde5057Brendan Jackman          by CPUFreq at time t
104224494084386ca0664120df7aeccd7f3c9bde5057Brendan Jackman          cluster_active[t] == 0 otherwise
1043691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1044691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        :param cluster: list of CPU IDs belonging to a cluster
1045691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        :type cluster: list(int)
1046691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1047ac4e391db53c274712f31e7a1048a27bb1a5d7cfBrendan Jackman        :returns: A :mod:`pandas.Series` or ``None`` if the trace contains no
1048ac4e391db53c274712f31e7a1048a27bb1a5d7cfBrendan Jackman                  "cpu_idle" events
1049691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        """
1050691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        if not self.hasEvents('cpu_idle'):
1051c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.warning('Events [cpu_idle] not found, '
1052c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                              'cannot compute cluster active signal!')
1053691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio            return None
1054691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1055403a36f57d676a98b3632847e5655f9128a50216Michele Di Giorgio        active = self.getCPUActiveSignal(cluster[0]).to_frame(name=cluster[0])
1056403a36f57d676a98b3632847e5655f9128a50216Michele Di Giorgio        for cpu in cluster[1:]:
1057403a36f57d676a98b3632847e5655f9128a50216Michele Di Giorgio            active = active.join(
1058403a36f57d676a98b3632847e5655f9128a50216Michele Di Giorgio                self.getCPUActiveSignal(cpu).to_frame(name=cpu),
1059403a36f57d676a98b3632847e5655f9128a50216Michele Di Giorgio                how='outer'
1060403a36f57d676a98b3632847e5655f9128a50216Michele Di Giorgio            )
1061691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1062691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        active.fillna(method='ffill', inplace=True)
1063691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1064691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        # Cluster active is the OR between the actives on each CPU
1065691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        # belonging to that specific cluster
1066691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        cluster_active = reduce(
1067691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio            operator.or_,
1068691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio            [cpu_active.astype(int) for _, cpu_active in
1069691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio             active.iteritems()]
1070691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        )
1071691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1072691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio        return cluster_active
1073691fc118e5dff4df885a039cc855a746de4b7556Michele Di Giorgio
1074b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio
1075f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasiclass TraceData:
1076b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio    """ A DataFrame collector exposed to Trace's clients """
1077f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi    pass
1078f493b3388f11c03a2ad4522d69aaf49c82da787fPatrick Bellasi
1079b9c02fe3f7682ceb9e1c703d218bb4c9449358a1Michele Di Giorgio# vim :set tabstop=4 shiftwidth=4 expandtab
1080