Exemplo n.º 1
0
    def get_simulated_pelt(self, task, signal_name):
        """
        Simulate a PELT signal for a given task.

        :param task: task to look for in the trace.
        :type task: int or str or tuple(int, str)

        :param signal_name: Name of the PELT signal to simulate.
        :type signal_name: str

        :return: A :class:`pandas.DataFrame` with a ``simulated`` column
            containing the simulated signal, along with the column of the
            signal as found in the trace.
        """
        logger = self.get_logger()
        trace = self.trace
        task = trace.get_task_id(task)
        cpus = trace.analysis.tasks.cpus_of_tasks([task])

        # Capacity lower than 1024 will create some time-scaling artifacts that
        # are not currently simulated
        assert all(
            self.plat_info["cpu-capacities"][cpu] == UTIL_SCALE
            for cpu in cpus
        )

        df_activation = trace.analysis.tasks.df_task_activation(task)
        df = trace.analysis.load_tracking.df_tasks_signal(signal_name)
        df = df_filter_task_ids(df, [task])

        # Ignore the first activation, as its signals are incorrect
        df_activation = df_activation.iloc[2:]

        # Make sure the activation df does not start before the dataframe of
        # signal values, otherwise we cannot provide a sensible init value
        df_activation = df_activation[df.index[0]:]

        # Get the initial signal value matching the first activation we will care about
        init_iloc = df.index.get_loc(df_activation.index[0], method='ffill')
        init = df[signal_name].iloc[init_iloc]

        try:
            # PELT clock in nanoseconds
            clock = df['update_time'] * 1e-9
        except KeyError:
            logger.warning('PELT clock is not available, ftrace timestamp will be used at the expense of accuracy')
            clock = None

        df['simulated'] = simulate_pelt(df_activation['active'], index=df.index, init=init, clock=clock)
        df['error'] = df[signal_name] - df['simulated']

        df = df.dropna()
        return df
Exemplo n.º 2
0
    def get_simulated_pelt(self, task, signal_name):
        """
        Simulate a PELT signal for a given task.

        :param task: task to look for in the trace.
        :type task: int or str or tuple(int, str)

        :param signal_name: Name of the PELT signal to simulate.
        :type signal_name: str

        :return: A :class:`pandas.DataFrame` with a ``simulated`` column
            containing the simulated signal, along with the column of the
            signal as found in the trace.
        """
        logger = self.logger
        trace = self.trace
        task = trace.get_task_id(task)

        df_activation = trace.ana.tasks.df_task_activation(
            task,
            # Util only takes into account times where the task is actually
            # executing
            preempted_value=0,
        )

        pinned_cpus = sorted(self.cpus)
        assert len(pinned_cpus) == 1
        df = self._get_trace_signal(task, pinned_cpus, signal_name)

        df = df.copy(deep=False)

        # Ignore the first activation, as its signals are incorrect
        df_activation = df_activation.iloc[2:]

        # Make sure the activation df does not start before the dataframe of
        # signal values, otherwise we cannot provide a sensible init value
        df_activation = df_activation[df.index[0]:]

        # Get the initial signal value matching the first activation we will care about
        init_iloc = df.index.get_indexer([df_activation.index[0]],
                                         method='ffill')[0]
        init = df[signal_name].iloc[init_iloc]

        try:
            # PELT clock in nanoseconds
            clock = df['update_time'] * 1e-9
        except KeyError:
            if any(self.plat_info['cpu-capacities']['rtapp'][cpu] != UTIL_SCALE
                   for phase in self.wlgen_task.phases
                   for cpu in phase['cpus']):
                ResultBundle.raise_skip(
                    'PELT time scaling can only be simulated when the PELT clock is available from the trace'
                )

            logger.warning(
                'PELT clock is not available, ftrace timestamp will be used at the expense of accuracy'
            )
            clock = None

        try:
            cpus = trace.ana.tasks.cpus_of_tasks([task])
            capacity = trace.ana.load_tracking.df_cpus_signal('capacity', cpus)
        except MissingTraceEventError:
            capacity = None
        else:
            capacity = capacity[['cpu', 'capacity_curr']]
            # We are interested in the current CPU capacity as seen by CFS.
            # This takes into account:
            # * The frequency
            # * The capacity of other sched classes (RT, IRQ etc)
            capacity = capacity.rename(columns={'capacity_curr': 'capacity'})

            # Reshape the capacity dataframe so that we get one column per CPU
            capacity = capacity.pivot(columns=['cpu'])
            capacity.columns = capacity.columns.droplevel(0)
            capacity.ffill(inplace=True)
            capacity = df_refit_index(capacity,
                                      window=(df_activation.index[0],
                                              df_activation.index[-1]))
            # Make sure we end up with the timestamp at which the capacity
            # changes, rather than the timestamps at which the task is enqueued
            # or dequeued.
            activation_cpu = df_activation['cpu'].reindex(capacity.index,
                                                          method='ffill')
            capacity = series_dereference(activation_cpu, capacity)

        df['simulated'] = simulate_pelt(
            df_activation['active'],
            index=df.index,
            init=init,
            clock=clock,
            capacity=capacity,
        )

        # Since load is now CPU invariant in recent kernel versions, we don't
        # rescale it back. To match the old behavior, that line is
        # needed:
        #  df['simulated'] /= self.plat_info['cpu-capacities']['rtapp'][cpu] / UTIL_SCALE
        kernel_version = self.plat_info['kernel']['version']
        if (signal_name == 'load' and kernel_version.parts[:2] < (5, 1)):
            logger().warning(
                f'Load signal is assumed to be CPU invariant, which is true for recent mainline kernels, but may be wrong for {kernel_version}'
            )

        df['error'] = df[signal_name] - df['simulated']
        df = df.dropna()
        return df
Exemplo n.º 3
0
    def get_simulated_pelt(self, task, signal_name):
        """
        Simulate a PELT signal for a given task.

        :param task: task to look for in the trace.
        :type task: int or str or tuple(int, str)

        :param signal_name: Name of the PELT signal to simulate.
        :type signal_name: str

        :return: A :class:`pandas.DataFrame` with a ``simulated`` column
            containing the simulated signal, along with the column of the
            signal as found in the trace.
        """
        logger = self.get_logger()
        trace = self.trace
        task = trace.get_task_id(task)
        cpus = trace.analysis.tasks.cpus_of_tasks([task])

        df_activation = trace.analysis.tasks.df_task_activation(
            task,
            # Util only takes into account times where the task is actually
            # executing
            preempted_value=0,
        )
        df = trace.analysis.load_tracking.df_tasks_signal(signal_name)
        df = df_filter_task_ids(df, [task])
        df = df.copy(deep=False)

        # Ignore the first activation, as its signals are incorrect
        df_activation = df_activation.iloc[2:]

        # Make sure the activation df does not start before the dataframe of
        # signal values, otherwise we cannot provide a sensible init value
        df_activation = df_activation[df.index[0]:]

        # Get the initial signal value matching the first activation we will care about
        init_iloc = df.index.get_loc(df_activation.index[0], method='ffill')
        init = df[signal_name].iloc[init_iloc]

        try:
            # PELT clock in nanoseconds
            clock = df['update_time'] * 1e-9
        except KeyError:
            if any(self.plat_info['cpu-capacities']['rtapp'][cpu] != UTIL_SCALE
                   for phase in self.wlgen_task.phases for cpu in phase.cpus):
                raise CannotCreateError(
                    'PELT time scaling can only be simulated when the PELT clock is available from the trace'
                )

            logger.warning(
                'PELT clock is not available, ftrace timestamp will be used at the expense of accuracy'
            )
            clock = None

        df['simulated'] = simulate_pelt(df_activation['active'],
                                        index=df.index,
                                        init=init,
                                        clock=clock)

        # Since load is now CPU invariant in recent kernel versions, we don't
        # rescale it back. To match the old behavior, that line is
        # needed:
        #  df['simulated'] /= self.plat_info['cpu-capacities']['rtapp'][cpu] / UTIL_SCALE
        kernel_version = self.plat_info['kernel']['version']
        if (signal_name == 'load' and kernel_version.parts[:2] < (5, 1)):
            logger().warning(
                f'Load signal is assumed to be CPU invariant, which is true for recent mainline kernels, but may be wrong for {kernel_version}'
            )

        df['error'] = df[signal_name] - df['simulated']
        df = df.dropna()
        return df