Source code for lisa.analysis.thermal

# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2017, Arm Limited and contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#


from devlib.utils.misc import list_to_mask, mask_to_list

from lisa.analysis.base import TraceAnalysisBase
from lisa.utils import memoized
from lisa.trace import requires_events, CPU
from lisa.datautils import df_refit_index, series_refit_index
from lisa.notebook import plot_signal


[docs] class ThermalAnalysis(TraceAnalysisBase): """ Support for plotting Thermal Analysis data :param trace: input Trace object :type trace: lisa.trace.Trace """ name = 'thermal'
[docs] @requires_events("thermal_temperature") def df_thermal_zones_temperature(self): """ Get the temperature of the thermal zones :returns: a :class:`pandas.DataFrame` with: * An ``id`` column (The thermal zone ID) * A ``thermal_zone`` column (The thermal zone name) * A ``temp`` column (The reported temperature) """ df = self.trace.df_event("thermal_temperature") df = df[['id', 'thermal_zone', 'temp']] return df
[docs] @TraceAnalysisBase.df_method @requires_events("thermal_power_cpu_limit") def df_cpufreq_cooling_state(self, cpus=None): """ Get cpufreq cooling device states :param cpus: The CPUs to consider (all by default) :type cpus: list(int) :returns: a :class:`pandas.DataFrame` with: * An ``cpus`` column (The CPUs affected by the cooling device) * A ``freq`` column (The frequency limit) * A ``cdev_state`` column (The cooling device state index) """ df = self.trace.df_event("thermal_power_cpu_limit") df = df[['cpus', 'freq', 'cdev_state']] if cpus is not None: # Find masks that match the requested CPUs # This can include other CPUs masks = self._matching_masks(cpus) df = df[df.cpus.isin(masks)] return df
[docs] @TraceAnalysisBase.df_method @requires_events("thermal_power_devfreq_limit") def df_devfreq_cooling_state(self, devices=None): """ Get devfreq cooling device states :param devices: The devfreq devices to consider (all by default) :type device: list(str) :returns: a :class:`pandas.DataFrame` with: * An ``cpus`` column (The CPUs affected by the cooling device) * A ``freq`` column (The frequency limit) * A ``cdev_state`` column (The cooling device state index) """ df = self.trace.df_event("devfreq_out_power") df = df[['type', 'freq', 'cdev_state']] if devices is not None: df = df[df.type.isin(devices)] return df
@property @memoized @df_thermal_zones_temperature.used_events def thermal_zones(self): """ Get thermal zone ids that appear in the trace """ df = self.df_thermal_zones_temperature() return df["thermal_zone"].unique().tolist() @property @memoized @df_cpufreq_cooling_state.used_events def cpufreq_cdevs(self): """ Get cpufreq cooling devices that appear in the trace """ df = self.df_cpufreq_cooling_state() res = df['cpus'].unique().tolist() return [mask_to_list(mask) for mask in res] @property @memoized @df_devfreq_cooling_state.used_events def devfreq_cdevs(self): """ Get devfreq cooling devices that appear in the trace """ df = self.df_devfreq_cooling_state() return df['type'].unique().tolist() ############################################################################### # Plotting Methods ###############################################################################
[docs] @TraceAnalysisBase.plot_method @df_thermal_zones_temperature.used_events 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)' )
[docs] @TraceAnalysisBase.plot_method @df_cpufreq_cooling_state.used_events 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' )
[docs] @TraceAnalysisBase.plot_method 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' )
############################################################################### # Utility Methods ############################################################################### def _matching_masks(self, cpus): df = self.trace.df_event('thermal_power_cpu_limit') global_mask = list_to_mask(cpus) cpumasks = df['cpus'].unique().tolist() return [m for m in cpumasks if m & global_mask]
# vim :set tabstop=4 shiftwidth=4 expandtab textwidth=80