Esempio n. 1
0
        def plotter(axes, local_fig):
            for cpu in cpus:
                axis = axes[cpu] if len(cpus) > 1 else axes

                # Add CPU utilization
                axis.set_title('CPU{}'.format(cpu))

                for signal in signals:
                    df = self.df_cpus_signal(signal)
                    df = df[df['cpu'] == cpu]
                    df = df_refit_index(df, start, end)
                    df[signal].plot(ax=axis, drawstyle='steps-post', alpha=0.4)

                self.trace.analysis.cpus.plot_orig_capacity(cpu, axis=axis)

                # Add capacities data if available
                if self.trace.has_events('cpu_capacity'):
                    df = self.trace.df_events('cpu_capacity')
                    df = df[df["__cpu"] == cpu]
                    if len(df):
                        data = df[['capacity', 'tip_capacity']]
                        data = df_refit_index(data, start, end)
                        data.plot(ax=axis,
                                  style=['m', '--y'],
                                  drawstyle='steps-post')

                # Add overutilized signal to the plot
                plot_overutilized = self.trace.analysis.status.plot_overutilized
                if self.trace.has_events(plot_overutilized.used_events):
                    plot_overutilized(axis=axis)

                axis.set_ylim(0, 1100)
                axis.set_xlim(start, end)
                axis.legend()
Esempio n. 2
0
        def plotter(axes, local_fig):
            axes = axes if len(cpus) > 1 else itertools.repeat(axes)
            for cpu, axis in zip(cpus, axes):
                # Add CPU utilization
                axis.set_title(f'CPU{cpu}')

                for signal in signals:
                    df = self.df_cpus_signal(signal, cpus=[cpu])
                    df = df_refit_index(df, window=window)
                    df[signal].plot(ax=axis, drawstyle='steps-post', alpha=0.4)

                self.trace.analysis.cpus.plot_orig_capacity(cpu, axis=axis)

                # Add capacities data if available
                try:
                    df = self.df_cpus_signal('capacity', cpus=[cpu])
                except MissingTraceEventError:
                    pass
                else:
                    if len(df):
                        data = df[['capacity']]
                        data = df_refit_index(data, window=window)
                        data.plot(ax=axis,
                                  style=['m', '--y'],
                                  drawstyle='steps-post')

                # Add overutilized signal to the plot
                plot_overutilized = self.trace.analysis.status.plot_overutilized
                if self.trace.has_events(plot_overutilized.used_events):
                    plot_overutilized(axis=axis)

                axis.set_ylim(0, 1100)
                axis.legend()
Esempio n. 3
0
    def plot_tasks_wakeups_heatmap(self, xbins=100, colormap=None, axis=None,
            local_fig=None, **kwargs):
        """
        Plot tasks wakeups heatmap

        :param xbins: Number of x-axis bins, i.e. in how many slices should
          time be arranged
        :type xbins: int

        :param colormap: The name of a colormap (see
          https://matplotlib.org/users/colormaps.html), or a Colormap object
        :type colormap: str or matplotlib.colors.Colormap
        """

        df = self.trace.df_events("sched_wakeup")
        df = df_refit_index(df, self.trace.start, self.trace.end)

        fig, axis = self._plot_cpu_heatmap(
            df.index, df.target_cpu, xbins, "Number of wakeups",
            cmap=colormap,
            **kwargs,
        )

        axis.set_title("Tasks wakeups over time")
        return axis
Esempio n. 4
0
    def plot_cpu_cooling_states(self, cpu: CPU, axis, local_fig):
        """
        Plot the state evolution of a cpufreq cooling device

        :param cpu: The CPU. Whole clusters can be controlled as
          a single cooling device, they will be plotted as long this CPU
          belongs to the cluster.
        :type cpu: int
        """
        window = self.trace.window

        df = self.df_cpufreq_cooling_state([cpu])
        df = df_refit_index(df, window=window)
        cdev_name = f"CPUs {mask_to_list(df.cpus.unique()[0])}"

        series = series_refit_index(df['cdev_state'], window=window)
        series.plot(drawstyle="steps-post", ax=axis,
                           label=f"\"{cdev_name}\"")

        axis.legend()

        if local_fig:
            axis.grid(True)
            axis.set_title("cpufreq cooling devices status")
            axis.yaxis.set_major_locator(MaxNLocator(integer=True))
            axis.grid(axis='y')
Esempio n. 5
0
    def plot_thermal_zone_temperature(self, thermal_zone_id: int, axis, local_fig):
        """
        Plot temperature of thermal zones (all by default)

        :param thermal_zone_id: ID of the zone
        :type thermal_zone_id: int
        """
        window = self.trace.window

        df = self.df_thermal_zones_temperature()
        df = df[df.id == thermal_zone_id]
        df = df_refit_index(df, window=window)

        tz_name = df.thermal_zone.unique()[0]

        series = series_refit_index(df['temp'], window=window)
        series.plot(drawstyle="steps-post", ax=axis,
                     label=f"Thermal zone \"{tz_name}\"")

        axis.legend()

        if local_fig:
            axis.grid(True)
            axis.set_title("Temperature evolution")
            axis.set_ylabel("Temperature (°C.10e3)")
Esempio n. 6
0
    def plot_thermal_zone_temperature(self, thermal_zone_id, axis, local_fig):
        """
        Plot temperature of thermal zones (all by default)

        :param thermal_zone_id: ID of the zone
        :type thermal_zone_id: int
        """
        start = self.trace.start
        end = self.trace.end

        df = self.df_thermal_zones_temperature()
        df = df[df.id == thermal_zone_id]
        df = df_refit_index(df, start, end)

        tz_name = df.thermal_zone.unique()[0]

        df.temp.plot(drawstyle="steps-post",
                     ax=axis,
                     label="Thermal zone \"{}\"".format(tz_name))

        axis.legend()

        if local_fig:
            axis.grid(True)
            axis.set_title("Temperature evolution")
            axis.set_ylabel("Temperature (°C.10e3)")
            axis.set_xlim(start, end)
Esempio n. 7
0
    def plot_overutilized(self, axis, local_fig):
        """
        Draw the system's overutilized status as colored bands
        """

        df = self.df_overutilized()
        if not df.empty:
            df = df_refit_index(df, window=self.trace.window)

            # Compute intervals in which the system is reported to be overutilized
            bands = [(t, df['len'][t], df['overutilized'][t])
                     for t in df.index]

            color = self.get_next_color(axis)
            label = "Overutilized"
            for (start, delta, overutilized) in bands:
                if not overutilized:
                    continue

                end = start + delta
                axis.axvspan(start,
                             end,
                             alpha=0.2,
                             facecolor=color,
                             label=label)

                if label:
                    label = None

            axis.legend()

        if local_fig:
            axis.set_title("System-wide overutilized status")
Esempio n. 8
0
    def plot_task_required_capacity(self, task, **kwargs):
        """
        Plot the minimum required capacity of a task

        :param task: The name or PID of the task
        :type task: str or int
        """

        pid = self.trace.get_task_pid(task)
        start = self.trace.start
        end = self.trace.end

        df = self.df_tasks_signal('required_capacity')
        df = df[df.pid == pid]
        df = df_refit_index(df, start, end)

        # Build task names (there could be multiple, during the task lifetime)
        task_name = 'Task ({}:{})'.format(pid, self.trace.get_task_by_pid(pid))

        def plotter(axis, local_fig):
            df["required_capacity"].plot(drawstyle='steps-post', ax=axis)

            axis.legend()
            axis.grid(True)

            if local_fig:
                axis.set_title(task_name)
                axis.set_ylim(0, 1100)
                axis.set_xlim(start, end)
                axis.set_ylabel('Utilization')
                axis.set_xlabel('Time (s)')

        return self.do_plot(plotter, height=8, **kwargs)
Esempio n. 9
0
    def _get_trace_df(self):
        task = self.rtapp_task_ids_map['task'][0]

        # There is no CPU selection when we're going back from preemption.
        # Setting preempted_value=1 ensures that it won't count as a new
        # activation.
        df = self.trace.ana.tasks.df_task_activation(task, preempted_value=1)
        df = df_refit_index(df, window=self.trace.window)
        df = df[['active', 'cpu']]
        df['activation_start'] = df['active'] == 1

        df_freq = self.trace.ana.frequency.df_cpus_frequency()
        df_freq = df_freq[['cpu', 'frequency']]
        df_freq = df_freq.pivot(index=None, columns='cpu', values='frequency')
        df_freq.reset_index(inplace=True)
        df_freq.set_index('Time', inplace=True)

        df = df.merge(df_freq, how='outer', left_index=True, right_index=True)

        # Merge with df_freq will bring NaN in the activation column. We do not
        # want to ffill() them.
        df['activation_start'].fillna(value=False, inplace=True)

        # Ensures that frequency values are propogated through the entire
        # DataFrame, as it is possible that no frequency event occur
        # during a phase.
        df.ffill(inplace=True)

        return df
Esempio n. 10
0
    def plot_task_signals(self,
                          task,
                          axis,
                          local_fig,
                          signals=['util', 'load']):
        """
        Plot the task-related load-tracking signals

        :param task: The name or PID of the task
        :type task: str or int

        :param signals: List of signals to plot.
        :type signals: list(str)
        """
        pid = self.trace.get_task_pid(task)
        start = self.trace.start
        end = self.trace.end

        for signal in signals:
            df = self.df_tasks_signal(signal)
            df = df[df.pid == pid]
            df = df_refit_index(df, start, end)
            df[signal].plot(ax=axis, drawstyle='steps-post', alpha=0.4)

        plot_overutilized = self.trace.analysis.status.plot_overutilized
        if self.trace.has_events(plot_overutilized.used_events):
            plot_overutilized(axis=axis)

        axis.set_title('Load-tracking signals of task "{}"'.format(task))
        axis.legend()
        axis.grid(True)
        axis.set_xlim(start, end)
Esempio n. 11
0
 def make_fig(name, df_getter, label):
     df = df_getter(task)
     if df.empty:
         self.logger.warning(f"No data to plot for {name}")
     else:
         df = df_refit_index(df, window=self.trace.window)
         return self._plot_markers(df, label)
Esempio n. 12
0
    def plot_dev_freq_cooling_states(self, device, axis, local_fig):
        """
        Plot the state evolution of a devfreq cooling device

        :param device: The devfreq devices to consider
        :type device: str
        """
        start = self.trace.start
        end = self.trace.end

        df = self.df_devfreq_cooling_state([device])
        df = df_refit_index(df, start, end)

        df.cdev_state.plot(drawstyle="steps-post",
                           ax=axis,
                           label="Device \"{}\"".format(device))

        axis.legend()

        if local_fig:
            axis.grid(True)
            axis.set_title("devfreq cooling devices status")
            axis.yaxis.set_major_locator(MaxNLocator(integer=True))
            axis.grid(axis='y')
            axis.set_xlim(start, end)
Esempio n. 13
0
    def df_tasks_runtime(self):
        """
        DataFrame of the time each task spent in TASK_ACTIVE (:class:`TaskState`)

        :returns: a :class:`pandas.DataFrame` with:

          * PIDs as index
          * A ``comm`` column (the name of the task)
          * A ``runtime`` column (the time that task spent running)
        """

        runtimes = {}
        for task, pid_df in self._df_tasks_states():
            pid = task.pid
            # Make sure to only look at the relevant portion of the dataframe
            # with the window, since we are going to make a time-based sum
            pid_df = df_refit_index(pid_df, window=self.trace.window)
            pid_df = df_add_delta(pid_df)
            # Resolve the comm to the last name of the PID in that window
            comms = pid_df['comm'].unique()
            comm = comms[-1]
            pid_df = pid_df[pid_df['curr_state'] == TaskState.TASK_ACTIVE]
            runtimes[pid] = (pid_df['delta'].sum(skipna=True), comm)

        df = pd.DataFrame.from_dict(runtimes,
                                    orient="index",
                                    columns=["runtime", 'comm'])

        df.index.name = "pid"
        df.sort_values(by="runtime", ascending=False, inplace=True)

        return df
Esempio n. 14
0
    def plot_cpu_cooling_states(self, cpu, axis, local_fig):
        """
        Plot the state evolution of a cpufreq cooling device

        :param cpu: The CPU. Whole clusters can be controlled as
          a single cooling device, they will be plotted as long this CPU
          belongs to the cluster.
        :type cpu: int
        """
        start = self.trace.start
        end = self.trace.end

        df = self.df_cpufreq_cooling_state([cpu])
        df = df_refit_index(df, start, end)
        cdev_name = "CPUs {}".format(mask_to_list(df.cpus.unique()[0]))

        df.cdev_state.plot(drawstyle="steps-post",
                           ax=axis,
                           label="\"{}\"".format(cdev_name))

        axis.legend()

        if local_fig:
            axis.grid(True)
            axis.set_title("cpufreq cooling devices status")
            axis.yaxis.set_major_locator(MaxNLocator(integer=True))
            axis.grid(axis='y')
            axis.set_xlim(start, end)
Esempio n. 15
0
    def plot_task_required_capacity(self, task: TaskID, axis=None, **kwargs):
        """
        Plot the minimum required capacity of a task

        :param task: The name or PID of the task, or a tuple ``(pid, comm)``
        :type task: str or int or tuple
        """
        window = self.trace.window

        task_ids = self.trace.get_task_ids(task)
        df = self.df_tasks_signal('required_capacity')
        df = df_filter_task_ids(df, task_ids)
        df = df_refit_index(df, window=window)

        # Build task names (there could be multiple, during the task lifetime)
        task_name = f"Task ({', '.join(map(str, task_ids))})"

        def plotter(axis, local_fig):
            df["required_capacity"].plot(drawstyle='steps-post', ax=axis)

            axis.legend()
            axis.grid(True)

            if local_fig:
                axis.set_title(task_name)
                axis.set_ylim(0, 1100)
                axis.set_ylabel('Utilization')
                axis.set_xlabel('Time (s)')

        return self.do_plot(plotter, height=8, axis=axis, **kwargs)
Esempio n. 16
0
    def plot_task_signals(self,
                          task: TaskID,
                          axis,
                          local_fig,
                          signals: TypedList[str] = ['util', 'load']):
        """
        Plot the task-related load-tracking signals

        :param task: The name or PID of the task, or a tuple ``(pid, comm)``
        :type task: str or int or tuple

        :param signals: List of signals to plot.
        :type signals: list(str)
        """
        window = self.trace.window
        task = self.trace.get_task_id(task, update=False)

        for signal in signals:
            df = self.df_task_signal(task, signal)
            df = df_refit_index(df, window=window)
            df[signal].plot(ax=axis, drawstyle='steps-post', alpha=0.4)

        plot_overutilized = self.trace.analysis.status.plot_overutilized
        if self.trace.has_events(plot_overutilized.used_events):
            plot_overutilized(axis=axis)

        axis.set_title(f'Load-tracking signals of task {task}')
        axis.legend()
        axis.grid(True)
Esempio n. 17
0
    def _get_task_cpu_df(self):
        """
        Get a DataFrame mapping task names to the CPU they ran on

        Use the sched_switch trace event to find which CPU each task ran
        on. Does not reflect idleness - tasks not running are shown as running
        on the last CPU they woke on.

        :returns: A Pandas DataFrame with a column for each task, showing the
                  CPU that the task was "on" at each moment in time
        """
        def task_cpu(task):
            return task.comm, self.trace.ana.tasks.df_task_activation(
                task=task)['cpu']

        df = pd.DataFrame(
            dict(
                task_cpu(task_ids[0])
                for task, task_ids in self.rtapp_task_ids_map.items()))
        df.fillna(method='ffill', inplace=True)
        df.dropna(inplace=True)
        df = df_deduplicate(df, consecutives=True, keep='first')

        # Ensure the index is refitted so that integrals work as expected
        df = df_refit_index(df, window=self.trace.window)
        return df
Esempio n. 18
0
    def df_cpu_idle_state_residency(self, cpu):
        """
        Compute time spent by a given CPU in each idle state.

        :param cpu: CPU ID
        :type cpu: int

        :returns: a :class:`pandas.DataFrame` with:

          * Idle states as index
          * A ``time`` column (The time spent in the idle state)
        """
        idle_df = self.df_cpu_idle(cpu)

        # Ensure accurate time-based sum of state deltas
        idle_df = df_refit_index(idle_df, window=self.trace.window)

        # For each state, sum the time spent in it
        idle_df = df_add_delta(idle_df)

        residency = {
            cols['state']: state_df['delta'].sum()
            for cols, state_df in df_split_signals(idle_df, ['state'])
        }
        df = pd.DataFrame.from_dict(residency,
                                    orient='index',
                                    columns=['time'])
        df.index.name = 'idle_state'
        return df
Esempio n. 19
0
 def _plot_signal(signal):
     df = self.df_task_signal(task, signal)
     df = df_refit_index(df, window=window)
     return plot_signal(
         df[signal],
         name=signal,
     ).options(dict(Curve=dict(alpha=0.5)), )
Esempio n. 20
0
    def _get_expected_task_utils_df(self):
        """
        Get a DataFrame with the *expected* utilization of each task over time.

        :param nrg_model: EnergyModel used to computed the expected utilization
        :type nrg_model: EnergyModel

        :returns: A Pandas DataFrame with a column for each task, showing how
                  the utilization of that task varies over time

        .. note:: The timestamps to match the beginning and end of each rtapp
            phase are taken from the trace.
        """
        tasks_map = self.rtapp_tasks_map
        rtapp_profile = self.rtapp_profile

        def task_util(task, wlgen_task):
            task_list = tasks_map[task]
            assert len(task_list) == 1
            task = task_list[0]

            df = self.trace.analysis.rta.df_phases(task, wlgen_profile=rtapp_profile)
            df = df[df['properties'].transform(lambda phase: phase['meta']['from_test'])]

            def get_phase_max_util(phase):
                wload = phase['wload']
                # Take into account the duty cycle of the phase
                avg = wload.unscaled_duty_cycle_pct(
                    plat_info=self.plat_info,
                ) * PELT_SCALE / 100
                # Also take into account the period and the swing of PELT
                # around its "average"
                swing = pelt_swing(
                    period=wload.period,
                    duty_cycle=wload.duty_cycle_pct / 100,
                    kind='above',
                )
                return avg + swing

            phases_util = {
                phase.get('name'): get_phase_max_util(phase)
                for phase in wlgen_task.phases
                if phase['meta']['from_test']
            }

            expected_util = df['phase'].map(phases_util)
            return task, expected_util

        cols = dict(
            task_util(task, wlgen_task)
            for task, wlgen_task in rtapp_profile.items()
        )
        df = pd.DataFrame(cols)
        df.fillna(method='ffill', inplace=True)
        df.dropna(inplace=True)

        # Ensure the index is refitted so that integrals work as expected
        df = df_refit_index(df, window=self.trace.window)
        return df
Esempio n. 21
0
        def plotter(axis, local_fig):
            freq_axis, state_axis = axis
            freq_axis.get_figure().suptitle('Peripheral frequency', y=.97, fontsize=16, horizontalalignment='center')

            freq = self.df_peripheral_clock_effective_rate(clk)
            freq = df_refit_index(freq, start, end)

            # Plot frequency information (set rate)
            freq_axis.set_title("Clock frequency for " + clk)
            set_rate = freq['rate'].dropna()

            rate_axis_lib = 0
            if len(set_rate) > 0:
                rate_axis_lib = set_rate.max()
                set_rate.plot(style=['b--'], ax=freq_axis, drawstyle='steps-post', alpha=0.4, label="clock_set_rate value")
                freq_axis.hlines(set_rate.iloc[-1], set_rate.index[-1], end, linestyle='--', color='b', alpha=0.4)
            else:
                logger.warning('No clock_set_rate events to plot')

            # Plot frequency information (effective rate)
            eff_rate = freq['effective_rate'].dropna()
            if len(eff_rate) > 0 and eff_rate.max() > 0:
                rate_axis_lib = max(rate_axis_lib, eff_rate.max())
                eff_rate.plot(style=['b-'], ax=freq_axis, drawstyle='steps-post', alpha=1.0, label="Effective rate (with on/off)")
                freq_axis.hlines(eff_rate.iloc[-1], eff_rate.index[-1], end, linestyle='-', color='b', alpha=1.0)
            else:
                logger.warning('No effective frequency events to plot')

            freq_axis.set_ylim(0, rate_axis_lib * 1.1)
            freq_axis.set_xlim(start, end)
            freq_axis.set_xlabel('')
            freq_axis.grid(True)
            freq_axis.legend()
            def mhz(x, pos):
                return '{:1.2f} MHz'.format(x*1e-6)
            freq_axis.get_yaxis().set_major_formatter(FuncFormatter(mhz))

            on = freq[freq.state == 1]
            state_axis.hlines([0] * len(on),
                              on['start'], on['start'] + on['len'],
                              linewidth = 10.0, label='clock on', color='green')
            off = freq[freq.state == 0]
            state_axis.hlines([0] * len(off),
                              off['start'], off['start'] + off['len'],
                              linewidth = 10.0, label='clock off', color='red')


            # Plot time period that the clock state was unknown from the trace
            indeterminate = pd.concat([on, off]).sort_index()
            if indeterminate.empty:
                indet_range_max = end
            else:
                indet_range_max = indeterminate.index[0]
            state_axis.hlines(0, 0, indet_range_max, linewidth = 1.0, label='indeterminate clock state', linestyle='--')
            state_axis.legend(bbox_to_anchor=(0., 1.02, 1., 0.102), loc=3, ncol=3, mode='expand')
            state_axis.set_yticks([])
            state_axis.set_xlabel('seconds')
            state_axis.set_xlim(start, end)
Esempio n. 22
0
 def ensure_last_rectangle(df):
     # Make sure we will draw the last rectangle, which could be
     # critical for tasks that are never sleeping
     if df.empty:
         return df
     else:
         return df_refit_index(df,
                               window=(None, df.index[-1] +
                                       df['duration'].iat[-1]))
Esempio n. 23
0
        def plot_bands(df, column, label):
            df = df_refit_index(df, self.trace.start, self.trace.end)
            bands = [(t, df[column][t]) for t in df.index]
            color = self.get_next_color(axis)
            for idx, (start, duration) in enumerate(bands):
                if idx > 0:
                    label = None

                end = start + duration
                axis.axvspan(start, end, facecolor=color, alpha=0.5,
                             label=label)
Esempio n. 24
0
    def plot_cpu_frequencies(self,
                             cpu: CPU,
                             axis,
                             local_fig,
                             average: bool = True):
        """
        Plot frequency for the specified CPU

        :param cpu: The CPU for which to plot frequencies
        :type cpus: int

        :param average: If ``True``, add a horizontal line which is the
            frequency average.
        :type average: bool

        If ``sched_overutilized`` events are available, the plots will also
        show the intervals of time where the system was overutilized.
        """
        logger = self.get_logger()
        df = self.df_cpu_frequency(cpu)

        if "freqs" in self.trace.plat_info:
            frequencies = self.trace.plat_info['freqs'][cpu]
        else:
            logger.info("Estimating CPU{} frequencies from trace".format(cpu))
            frequencies = sorted(list(df.frequency.unique()))
            logger.debug("Estimated frequencies: {}".format(frequencies))

        avg = self.get_average_cpu_frequency(cpu)
        logger.info("Average frequency for CPU{} : {:.3f} GHz".format(
            cpu, avg / 1e6))

        df = df_refit_index(df, window=self.trace.window)
        df['frequency'].plot(ax=axis, drawstyle='steps-post')

        if average and avg > 0:
            axis.axhline(avg,
                         color=self.get_next_color(axis),
                         linestyle='--',
                         label="average")

        plot_overutilized = self.trace.analysis.status.plot_overutilized
        if self.trace.has_events(plot_overutilized.used_events):
            plot_overutilized(axis=axis)

        axis.set_ylabel('Frequency (Hz)')
        axis.set_ylim(frequencies[0] * 0.9, frequencies[-1] * 1.1)
        axis.legend()
        if local_fig:
            axis.set_xlabel('Time')
            axis.set_title('Frequency of CPU{}'.format(cpu))
            axis.grid(True)
Esempio n. 25
0
    def _get_expected_task_utils_df(self):
        """
        Get a DataFrame with the *expected* utilization of each task over time.

        :param nrg_model: EnergyModel used to computed the expected utilization
        :type nrg_model: EnergyModel

        :returns: A Pandas DataFrame with a column for each task, showing how
                  the utilization of that task varies over time

        .. note:: The timestamps to match the beginning and end of each rtapp
            phase are taken from the trace.
        """
        tasks_map = self.rtapp_tasks_map

        def task_util(task, wlgen_task):
            task_list = tasks_map[task]
            assert len(task_list) == 1
            task = task_list[0]

            df = self.trace.analysis.rta.df_phases(task)
            phases = wlgen_task.phases

            duty_cycles = {
                i: phase.duty_cycle_pct
                for i, phase in enumerate(phases)
            }

            # TODO: remove that once we have named phases to skip the buffer phase
            # Shift by one to take into account the buffer phase
            duty_cycles = {
                i+1: duty_cycle
                for i, duty_cycle in duty_cycles.items()
            }
            # The buffer phase inherits its duty cycle from the next phase
            duty_cycles[0] = duty_cycles[1]

            expected_util = df['phase'].map(duty_cycles)
            expected_util *= PELT_SCALE / 100
            return task, expected_util

        cols = dict(
            task_util(task, wlgen_task)
            for task, wlgen_task in self.rtapp_profile.items()
        )
        df = pd.DataFrame(cols)
        df.fillna(method='ffill', inplace=True)
        df.dropna(inplace=True)

        # Ensure the index is refitted so that integrals work as expected
        df = df_refit_index(df, window=self.trace.window)
        return df
Esempio n. 26
0
    def df_cluster_idle_state_residency(self, cluster):
        """
        Compute time spent by a given cluster in each idle state.

        :param cluster: list of CPU IDs
        :type cluster: list(int)

        :returns: a :class:`pandas.DataFrame` with:

          * Idle states as index
          * A ``time`` column (The time spent in the idle state)
        """
        idle_df = self.df_cpu_idle()

        # Create a dataframe with a column per CPU
        cols = {
            cpu: group['state']
            for cpu, group in idle_df.groupby(
                'cpu',
                sort=False,
                observed=True,
            ) if cpu in cluster
        }
        cpus_df = pd.DataFrame(cols, index=idle_df.index)
        cpus_df.fillna(method='ffill', inplace=True)

        # Ensure accurate time-based sum of state deltas. This will extrapolate
        # the known cluster_state both to the left and the right.
        cpus_df = df_refit_index(cpus_df, window=self.trace.window)

        # Each core in a cluster can be in a different idle state, but the
        # cluster lies in the idle state with lowest ID, that is the shallowest
        # idle state among the idle states of its CPUs
        cluster_state = cpus_df.min(axis='columns')
        cluster_state.name = 'cluster_state'
        df = cluster_state.to_frame()

        # For each state transition, sum the time spent in it
        df_add_delta(df, inplace=True)

        # For each cluster state, take the sum of the delta column.
        # The resulting dataframe is indexed by group keys (cluster_state).
        residency = df.groupby('cluster_state', sort=False,
                               observed=True)['delta'].sum()
        residency.name = 'time'

        residency = residency.to_frame()
        residency.index.name = 'idle_state'
        return residency
Esempio n. 27
0
    def plot_dev_freq_cooling_states(self, device: str):
        """
        Plot the state evolution of a devfreq cooling device

        :param device: The devfreq devices to consider
        :type device: str
        """
        df = self.df_devfreq_cooling_state([device])
        df = df_refit_index(df, window=self.trace.window)

        return plot_signal(
            df['cdev_state'],
            name=f'Device "{device}"',
        ).options(
            title='devfreq cooling devices status'
        )
Esempio n. 28
0
    def plot_runtimes(self, task: TaskID):
        """
        Plot the :meth:`lisa.analysis.latency.LatencyAnalysis.df_runtimes` of a task

        :param task: The task's name or PID
        :type task: int or str or tuple(int, str)
        """
        df = self.df_runtimes(task)
        df = df_refit_index(df, window=self.trace.window)

        name = f'Per-activation runtimes of task {task}'
        return (
            self._plot_markers(df, name) *
            self._plot_overutilized()
        ).options(
            title=name,
        )
Esempio n. 29
0
        def plot_bands(df, column, label):
            df = df_refit_index(df, window=self.trace.window)
            if df.empty:
                return _hv_neutral()

            return hv.Overlay(
                [
                    hv.VSpan(
                        start,
                        start + duration,
                        label=label,
                    ).options(
                        alpha=0.5,
                    )
                    for start, duration in df[[column]].itertuples()
                ]
            )
Esempio n. 30
0
    def plot_runtimes(self, task: TaskID, axis, local_fig):
        """
        Plot the :meth:`lisa.analysis.latency.LatencyAnalysis.df_runtimes` of a task

        :param task: The task's name or PID
        :type task: int or str or tuple(int, str)
        """
        df = self.df_runtimes(task)
        df = df_refit_index(df, window=self.trace.window)

        df.plot(style='+', ax=axis)

        plot_overutilized = self.trace.analysis.status.plot_overutilized
        if self.trace.has_events(plot_overutilized.used_events):
            plot_overutilized(axis=axis)

        axis.set_title('Per-activation runtimes of task "{}"'.format(task))