diff --git a/doc/plot_conf.yml b/doc/plot_conf.yml index 024f509601d159d6cbab8af9065d83795f836269..4e31d929830b6803fd19ae7c907302fd70848e97 100644 --- a/doc/plot_conf.yml +++ b/doc/plot_conf.yml @@ -165,6 +165,10 @@ doc-plot-conf: trace: !call:lisa.trace.Trace trace_path: !env:interpolate $LISA_HOME/doc/traces/trace_pixel6.dat + Pixel9Analysis.plot_power_meter: + trace: !call:lisa.trace.Trace + trace_path: !env:interpolate $LISA_HOME/doc/traces/trace_pixel6.dat + NotebookAnalysis.plot_event_field: kwargs: event: cpu_frequency diff --git a/lisa/analysis/_pixel.py b/lisa/analysis/_pixel.py new file mode 100644 index 0000000000000000000000000000000000000000..0f70f2a213ba7071c681db2c8d62ed3b5029b5ce --- /dev/null +++ b/lisa/analysis/_pixel.py @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2024, 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. +# +""" +Pixel phones specific analysis. +""" + +import pandas as pd +import holoviews as hv +from holoviews import opts + +from lisa.datautils import df_add_delta +from lisa.analysis.base import TraceAnalysisBase +from lisa.trace import requires_events +from lisa.notebook import plot_signal + + +class PixelAnalysis(TraceAnalysisBase): + """ + Support for Pixel phones specific data analysis + + :param trace: input Trace object + :type trace: lisa.trace.Trace + """ + + ############################################################################### + # DataFrame Getter Methods + ############################################################################### + @TraceAnalysisBase.df_method + @requires_events('pixel6_emeter') + def df_power_meter(self): + """ + Get the power meter readings across the trace. + + :returns: A :class:`pandas.DataFrame` with: + + * A ``channel`` column (name of the power meter channel) + * A ``power`` column (average power usage in mW since the last measurement) + """ + df = self.trace.df_event('pixel6_emeter') + df = df[df['chan_name'].isin(self.EMETER_CHAN_NAMES)] + grouped = df.groupby('chan_name', observed=True, group_keys=False) + + def make_chan_df(df): + energy_diff = df_add_delta(df, col='energy_diff', src_col='value', window=self.trace.window)['energy_diff'] + ts_diff = df_add_delta(df, col='ts_diff', src_col='ts', window=self.trace.window)['ts_diff'] + power = energy_diff / ts_diff + df = pd.DataFrame(dict(power=power, channel=df['chan_name'])) + return df.dropna() + + df = grouped[df.columns].apply(make_chan_df) + df['channel'] = df['channel'].astype('category').cat.rename_categories(self.EMETER_CHAN_NAMES) + + return df + + ############################################################################### + # Plotting methods + ############################################################################### + @TraceAnalysisBase.plot_method + @df_power_meter.used_events + def plot_power_meter(self, channels=None): + """ + Plot the power meter readings from various channels. + + :param channels: List of channels to plot + :type channels: list(str) + + The channels needs to correspond to values in the ``channel`` column of df_power_meter(). + """ + df = self.df_power_meter() + + channels = list(channels or df['channel'].unique()) + if any(channel not in df['channel'].cat.categories for channel in channels): + raise ValueError('Specified channel not found') + + channel_data = dict(iter(df[df['channel'].isin(channels)].groupby('channel', group_keys=False, observed=True))) + return hv.Overlay([ + plot_signal(channel_data[channel]['power'], name=channel, vdim=hv.Dimension('power', label='Power', unit='mW')) + for channel in channels + ]).opts(title='Power usage per channel over time') diff --git a/lisa/analysis/pixel6.py b/lisa/analysis/pixel6.py index 2286083a273d47e270fa6325f788269a15090265..ce68f56df478d8baa76b0036ee46523ebae4f346 100644 --- a/lisa/analysis/pixel6.py +++ b/lisa/analysis/pixel6.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # -# Copyright (C) 2023, ARM Limited and contributors. +# Copyright (C) 2024, 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. @@ -18,22 +18,12 @@ Pixel 6-specific analysis. """ -import pandas as pd -import holoviews as hv -from holoviews import opts +from lisa.analysis._pixel import PixelAnalysis -from lisa.datautils import df_add_delta -from lisa.analysis.base import TraceAnalysisBase -from lisa.trace import requires_events -from lisa.notebook import plot_signal - -class Pixel6Analysis(TraceAnalysisBase): +class Pixel6Analysis(PixelAnalysis): """ Support for Pixel 6-specific data analysis - - :param trace: input Trace object - :type trace: lisa.trace.Trace """ name = 'pixel6' @@ -44,60 +34,3 @@ class Pixel6Analysis(TraceAnalysisBase): 'S2M_VDD_CPUCL2': 'CPU-Big', 'S2S_VDD_G3D': 'GPU', } - -############################################################################### -# DataFrame Getter Methods -############################################################################### - @TraceAnalysisBase.df_method - @requires_events('pixel6_emeter') - def df_power_meter(self): - """ - Get the power meter readings across the trace. - - :retuns: A :class:`pandas.DataFrame` with: - - * A ``channel`` column (name of the power meter channel) - * A ``power`` column (average power usage in mW since the last measurement) - """ - df = self.trace.df_event('pixel6_emeter') - df = df[df['chan_name'].isin(Pixel6Analysis.EMETER_CHAN_NAMES)] - grouped = df.groupby('chan_name', observed=True, group_keys=False) - - def make_chan_df(df): - energy_diff = df_add_delta(df, col='energy_diff', src_col='value', window=self.trace.window)['energy_diff'] - ts_diff = df_add_delta(df, col='ts_diff', src_col='ts', window=self.trace.window)['ts_diff'] - power = energy_diff / ts_diff - df = pd.DataFrame(dict(power=power, channel=df['chan_name'])) - return df.dropna() - - df = grouped[df.columns].apply(make_chan_df) - df['channel'] = df['channel'].astype('category').cat.rename_categories(Pixel6Analysis.EMETER_CHAN_NAMES) - - return df - -############################################################################### -# Plotting methods -############################################################################### - - @TraceAnalysisBase.plot_method - @df_power_meter.used_events - def plot_power_meter(self, channels=None): - """ - Plot the power meter readings from various channels. - - :param channels: List of channels to plot - :type channels: list(str) - - The channels needs to correspond to values in the ``channel`` column of df_power_meter(). - """ - df = self.df_power_meter() - - channels = list(channels or df['channel'].unique()) - if any(channel not in df['channel'].cat.categories for channel in channels): - raise ValueError('Specified channel not found') - - channel_data = dict(iter(df[df['channel'].isin(channels)].groupby('channel', group_keys=False, observed=True))) - return hv.Overlay([ - plot_signal(channel_data[channel]['power'], name=channel, vdim=hv.Dimension('power', label='Power', unit='mW')) - for channel in channels - ]).opts(title='Power usage per channel over time') diff --git a/lisa/analysis/pixel9.py b/lisa/analysis/pixel9.py new file mode 100644 index 0000000000000000000000000000000000000000..fbca7ad7569983dfe3bbfcb35b94e30747291dc4 --- /dev/null +++ b/lisa/analysis/pixel9.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2024, 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. +# +""" +Pixel 9-specific analysis. +""" + +from lisa.analysis._pixel import PixelAnalysis + + +class Pixel9Analysis(PixelAnalysis): + """ + Support for Pixel 9-specific data analysis + """ + + name = 'pixel9' + + """ + Power rail vs CPU/GPU cluster mapping + + Power rail information is extracted from + https://cs.android.com/android/platform/superproject/main/+/main:device/google/caimito/thermal/thermal_info_config_tokay.json;l=628 + """ + EMETER_CHAN_NAMES = { + 'S4M_VDD_CPUCL0': 'CPU-Little', + 'S2M_VDD_CPUCL1': 'CPU-Mid', + 'S3M_VDD_CPUCL2': 'CPU-Big', + 'S2S_VDD_G3D': 'GPU', + } diff --git a/target_conf.yml b/target_conf.yml index 24dd76f702216b2cbad0732f93f54792dc2bf4aa..a44e9ae14c91252b11d9c4c4c307bb8ae4f3c6a5 100644 --- a/target_conf.yml +++ b/target_conf.yml @@ -67,10 +67,10 @@ target-conf: # Optional devlib configuration # devlib: - # Devlib modules names to enable/disbale for all the experiment + # Devlib modules names to enable/disable for all the experiment # excluded-modules: [] # - # devlib Platform sublcass to use, with the keyword arguments to use it + # devlib Platform subclass to use, with the keyword arguments to use it # platform: # Defaults to devlib.platform.Platform # class: devlib.platform.Platform