Exemplo n.º 1
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')
Exemplo n.º 2
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)")
Exemplo n.º 3
0
    def plot_tasks_forks(self, target_cpus: TypedList[CPU]=None, window: float=0.01, per_sec: bool=False,
                         axis=None, local_fig=None):
        """
        Plot task forks over time

        :param target_cpus:
        :type target_cpus:

        :param window: The rolling window size for fork counts.
        :type window: float

        :param per_sec: Display wakeups per second if True, else wakeup counts
          within the window
        :type per_sec: bool
        """

        df = self.trace.df_event("sched_wakeup_new")

        if target_cpus:
            df = df[df.target_cpu.isin(target_cpus)]

        series = series_rolling_apply(df["target_cpu"],
                                      lambda x: x.count() / (window if per_sec else 1),
                                      window, window_float_index=False, center=True)

        series = series_refit_index(series, window=self.trace.window)
        series.plot(ax=axis, legend=False)

        if per_sec:
            axis.set_title("Number of task forks per second ({}s windows)".format(window))
        else:
            axis.set_title("Number of task forks within {}s windows".format(window))
Exemplo n.º 4
0
    def plot_task_residency(self, task: TaskID, axis, local_fig):
        """
        Plot on which CPUs the task ran on over time

        :param task: Task to track
        :type task: int or str or tuple(int, str)
        """

        task_id = self.trace.get_task_id(task, update=False)

        sw_df = self.trace.df_event("sched_switch")
        sw_df = df_filter_task_ids(sw_df, [task_id],
                                   pid_col='next_pid',
                                   comm_col='next_comm')

        if "freq-domains" in self.trace.plat_info:
            # If we are aware of frequency domains, use one color per domain
            for domain in self.trace.plat_info["freq-domains"]:
                series = sw_df[sw_df["__cpu"].isin(domain)]["__cpu"]

                if series.empty:
                    # Cycle the colours to stay consistent
                    self.cycle_colors(axis)
                else:
                    series = series_refit_index(series,
                                                window=self.trace.window)
                    series.plot(
                        ax=axis,
                        style='+',
                        label="Task running in domain {}".format(domain))
        else:
            series = series_refit_index(sw_df['__cpu'],
                                        window=self.trace.window)
            series.plot(ax=axis, style='+')

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

        # Add an extra CPU lane to make room for the legend
        axis.set_ylim(-0.95, self.trace.cpus_count - 0.05)

        axis.set_title("CPU residency of task \"{}\"".format(task))
        axis.set_ylabel('CPUs')
        axis.grid(True)
        axis.legend()
Exemplo n.º 5
0
        def plot_residency():
            if "freq-domains" in self.trace.plat_info:
                # If we are aware of frequency domains, use one color per domain
                for domain in self.trace.plat_info["freq-domains"]:
                    series = sw_df[sw_df["__cpu"].isin(domain)]["__cpu"]
                    series = series_refit_index(series, window=self.trace.window)

                    if series.empty:
                        return _hv_neutral()
                    else:
                        return self._plot_markers(
                            series,
                            label=f"Task running in domain {domain}"
                        )
            else:
                self._plot_markers(
                    series_refit_index(sw_df['__cpu'], window=self.trace.window)
                )
Exemplo n.º 6
0
    def get_average_cpu_frequency(self, cpu):
        """
        Get the average frequency for a given CPU

        :param cpu: The CPU to analyse
        :type cpu: int
        """
        df = self.df_cpu_frequency(cpu)
        freq = series_refit_index(df['frequency'], window=self.trace.window)
        return series_mean(freq)
Exemplo n.º 7
0
 def plot_placement(cols, placement_df):
     placement = cols['placement']
     series = df["cpu"]
     series = series_refit_index(series, window=self.trace.window)
     series.name = 'cpu'
     return hv.Scatter(
         series,
         label=placement,
     ).options(
         marker='+',
         yticks=list(range(nr_cpus)),
     )
Exemplo n.º 8
0
 def plot_capacity(cpu):
     try:
         df = self.df_cpus_signal('capacity', cpus=[cpu])
     except MissingTraceEventError:
         return _hv_neutral()
     else:
         if df.empty:
             return _hv_neutral()
         else:
             return plot_signal(series_refit_index(df['capacity'],
                                                   window=window),
                                name='capacity')
Exemplo n.º 9
0
    def _plot_util(self):
        trace = self.trace
        analysis = trace.analysis.load_tracking
        fig, axes = analysis.setup_plot(nrows=len(self.rtapp_tasks))
        for task, axis in zip(self.rtapp_tasks, axes):
            analysis.plot_task_signals(task, signals=['util'], axis=axis)
            trace.analysis.rta.plot_phases(task,
                                           axis=axis,
                                           wlgen_profile=self.rtapp_profile)

            activation_axis = axis.twinx()
            trace.analysis.tasks.plot_task_activation(task,
                                                      duty_cycle=True,
                                                      overlay=True,
                                                      alpha=0.2,
                                                      axis=activation_axis)

            df_activations = trace.analysis.tasks.df_task_activation(task)
            df_util = analysis.df_task_signal(task, 'util')

            def compute_means(row):
                start = row.name
                end = start + row['duration']
                phase_activations = df_window(df_activations, (start, end))
                phase_util = df_window(df_util, (start, end))
                series = pd.Series({
                    'Phase duty cycle average':
                    series_mean(phase_activations['duty_cycle']),
                    'Phase util tunnel average':
                    kernel_util_mean(
                        phase_util['util'],
                        plat_info=self.plat_info,
                    ),
                })
                return series

            df_means = trace.analysis.rta.df_phases(task).apply(compute_means,
                                                                axis=1)
            df_means = series_refit_index(df_means, window=trace.window)
            df_means['Phase duty cycle average'].plot(drawstyle='steps-post',
                                                      ax=activation_axis)
            df_means['Phase util tunnel average'].plot(drawstyle='steps-post',
                                                       ax=axis)
            activation_axis.legend()
            axis.legend()

        filepath = ArtifactPath.join(self.res_dir, 'tasks_util.png')
        analysis.save_plot(fig, filepath=filepath)

        filepath = ArtifactPath.join(self.res_dir, 'cpus_util.png')
        cpus = sorted(self.cpus)
        analysis.plot_cpus_signals(cpus, signals=['util'], filepath=filepath)
Exemplo n.º 10
0
    def plot_task_placement(self, task, axis, local_fig):
        """
        Plot the CPU placement of the task

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

        # Get all utilization update events
        df = self.df_task_signal(task_id, 'required_capacity')
        cpu_capacities = self.trace.plat_info["cpu-capacities"]

        def evaluate_placement(cpu, required_capacity):
            capacity = cpu_capacities[cpu]

            if capacity < required_capacity:
                return "CPU capacity < required capacity"
            elif capacity == required_capacity:
                return "CPU capacity == required capacity"
            else:
                return "CPU capacity > required capacity"

        df["placement"] = df.apply(lambda row: evaluate_placement(
            row["cpu"], row["required_capacity"]),
                                   axis=1)

        for stat in df["placement"].unique():
            series = df[df.placement == stat]["cpu"]
            series = series_refit_index(series, self.trace.start,
                                        self.trace.end)
            series.plot(ax=axis, style="+", label=stat)

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

        axis.set_title("Utilization vs placement of task \"{}\"".format(task))

        axis.grid(True)
        axis.legend()
Exemplo n.º 11
0
    def _plot_tasks_X(self, event, name, target_cpus, window, per_sec):
        df = self.trace.df_event(event)

        if target_cpus:
            df = df[df['target_cpu'].isin(target_cpus)]

        series = series_rolling_apply(
            df["target_cpu"],
            lambda x: x.count() / (window if per_sec else 1),
            window,
            window_float_index=False,
            center=True
        )

        if per_sec:
            label = f"Number of task {name} per second ({window}s windows)"
        else:
            label = f"Number of task {name} within {window}s windows"
        series = series_refit_index(series, window=self.trace.window)
        series.name = name
        return plot_signal(series, name=label)
Exemplo n.º 12
0
    def plot_cpu_cooling_states(self, cpu: CPU):
        """
        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)
        series = series_refit_index(df['cdev_state'], window=window)
        cdev_name = f"CPUs {mask_to_list(df.cpus.unique()[0])}"
        return plot_signal(
            series,
            name=cdev_name,
        ).options(
            title='cpufreq cooling devices status'
        )
Exemplo n.º 13
0
    def plot_task_placement(self, task: TaskID, axis, local_fig):
        """
        Plot the CPU placement of the task

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

        # Get all utilization update events
        df = self.df_task_signal(task_id, 'required_capacity').copy()
        cpu_capacities = self.trace.plat_info["cpu-capacities"]['orig']

        df['capacity'] = df['cpu'].map(cpu_capacities)

        def add_placement(df, comp, comp_str):
            placement = "CPU capacity {} required capacity".format(comp_str)
            condition = comp(df['capacity'], df['required_capacity'])
            df.loc[condition, 'placement'] = placement

        add_placement(df, operator.lt, '<')
        add_placement(df, operator.gt, '>')
        add_placement(df, operator.eq, '==')

        for cols, placement_df in df_split_signals(df, ['placement']):
            placement = cols['placement']
            series = df["cpu"]
            series = series_refit_index(series, window=self.trace.window)
            series.plot(ax=axis, style="+", label=placement)

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

        if local_fig:
            axis.set_title(
                'Utilization vs placement of task "{}"'.format(task))

            axis.grid(True)
            axis.legend()
Exemplo n.º 14
0
    def plot_thermal_zone_temperature(self, thermal_zone_id: int):
        """
        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]

        return plot_signal(
            series_refit_index(df['temp'], window=window),
            name=f'Thermal zone "{tz_name}"',
        ).options(
            title='Temperature evolution',
            ylabel='Temperature (°C.10e3)'
        )
Exemplo n.º 15
0
    def plot_peripheral_frequency(self, clk_name: str, average: bool = True):
        """
        Plot frequency for the specified peripheral clock frequency

        :param clk_name: The clock name for which to plot frequency
        :type clk_name: str

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

        """
        df = self.df_peripheral_clock_effective_rate(clk_name)
        freq = df['effective_rate']
        freq = series_refit_index(freq, window=self.trace.window)

        fig = plot_signal(freq, name=f'Frequency of {clk_name} (Hz)')

        if average:
            avg = series_mean(freq)
            if avg > 0:
                fig *= hv.HLine(avg, group='average').opts(color='red')

        return fig
Exemplo n.º 16
0
    def get_expected_cpu_util(self):
        """
        Get the per-phase average CPU utilization expected from the duty cycle
        of the tasks found in the trace.

        :returns: A dict of the shape {cpu : {phase_id : expected_util}}

        .. note:: This is more robust than just looking at the duty cycle in
            the task profile, since rtapp might not reproduce accurately the
            duty cycle it was asked.
        """
        cpu_capacities = self.plat_info['cpu-capacities']['rtapp']
        cpu_util = {}
        cpu_freqs = self.plat_info['freqs']

        try:
            freq_df = self.trace.analysis.frequency.df_cpus_frequency()
        except MissingTraceEventError:
            cpus_rel_freq = None
        else:
            cpus_rel_freq = {
                # Frequency, normalized according to max frequency on that CPU
                cols['cpu']: df['frequency'] / max(cpu_freqs[cols['cpu']])
                for cols, df in df_split_signals(freq_df, ['cpu'])
            }

        for task in self.rtapp_task_ids:
            df = self.trace.analysis.tasks.df_task_activation(task)

            for row in self.trace.analysis.rta.df_phases(task).itertuples():
                phase = row.phase
                duration = row.duration
                start = row.Index
                end = start + duration
                # Ignore the first quarter of the util signal of each phase, since
                # it's impacted by the phase change, and util can be affected
                # (rtapp does some bookkeeping at the beginning of phases)
                # start += duration / 4

                # readjust the duration to take into account the modification of start
                duration = end - start
                window = (start, end)
                phase_df = df_window(df, window, clip_window=True)

                for cpu in self.cpus:

                    if cpus_rel_freq is None:
                        rel_freq_mean = 1
                    else:
                        phase_freq_series = df_window(cpus_rel_freq[cpu],
                                                      window=window,
                                                      clip_window=True)
                        # # We might not have frequency data at the beginning of the
                        # # trace, or if not frequency transition happened at all.
                        if phase_freq_series.empty:
                            rel_freq_mean = 1
                        else:
                            # If we lack freq data at the beginning of the
                            # window, assume the frequency was right.
                            if phase_freq_series.index[0] > start:
                                phase_freq_series = pd.concat([
                                    pd.Series([1.0], index=[start]),
                                    phase_freq_series
                                ])

                            # Extend the frequency to the right so that the mean
                            # takes into account all the data we have
                            freq_window = (phase_freq_series.index[0], end)
                            rel_freq_mean = series_mean(
                                series_refit_index(phase_freq_series,
                                                   window=freq_window))

                    cpu_phase_df = phase_df[phase_df['cpu'] == cpu].dropna()
                    if cpu_phase_df.empty:
                        duty_cycle = 0
                        cpu_residency = 0
                    else:
                        duty_cycle = series_mean(
                            df_refit_index(cpu_phase_df['duty_cycle'],
                                           window=window))
                        cpu_residency = end - max(cpu_phase_df.index[0], start)

                    phase_util = UTIL_SCALE * duty_cycle * (
                        cpu_capacities[cpu] / UTIL_SCALE)
                    # Pro-rata with the time spent on that CPU, so we get
                    # the correct average.
                    phase_util *= cpu_residency / duration

                    # We might not have run at max freq, e.g. because of
                    # thermal capping, so take that into account
                    phase_util *= rel_freq_mean

                    cpu_util.setdefault(cpu, {}).setdefault(phase, 0)
                    cpu_util[cpu][phase] += phase_util

        return cpu_util
Exemplo n.º 17
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, window=window)

            # Plot frequency information (set rate)
            freq_axis.set_title("Clock frequency for " + clk)
            set_rate = freq['state'].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()
            eff_rate = series_refit_index(eff_rate, window=window)
            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_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)