diff --git a/doc/plot_conf.yml b/doc/plot_conf.yml index bc2289bec4f0653dab70dcb5bde7e850468faabc..bca119ddb324c43b91b6bbfa8d65a512fbfa91af 100644 --- a/doc/plot_conf.yml +++ b/doc/plot_conf.yml @@ -118,6 +118,9 @@ doc-plot-conf: kwargs: cpu: *cpu0 + Pixel6Analysis.plot_power_meter: + trace: !call:lisa.trace.Trace + trace_path: !env:interpolate $LISA_HOME/doc/traces/trace_pixel6.dat NotebookAnalysis.plot_event_field: kwargs: diff --git a/doc/trace_analysis.rst b/doc/trace_analysis.rst index c822d6768dac28b531e3311ecdf28b2b11fa6b0c..e90275360660c4cda3a8da58a5c64f0983e23bcf 100644 --- a/doc/trace_analysis.rst +++ b/doc/trace_analysis.rst @@ -150,6 +150,12 @@ Thermal .. automodule:: lisa.analysis.thermal :members: +Pixel 6 ++++++++ + +.. automodule:: lisa.analysis.pixel6 + :members: + Function profiling ++++++++++++++++++ diff --git a/doc/traces/trace_pixel6.dat b/doc/traces/trace_pixel6.dat new file mode 100644 index 0000000000000000000000000000000000000000..7eaa1e60800378a40efef477e260743d954b174f Binary files /dev/null and b/doc/traces/trace_pixel6.dat differ diff --git a/lisa/analysis/pixel6.py b/lisa/analysis/pixel6.py new file mode 100644 index 0000000000000000000000000000000000000000..86e709de4e93561aad1898a9a90095c65377061c --- /dev/null +++ b/lisa/analysis/pixel6.py @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2023, 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. +# + +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 Pixel6Analysis(TraceAnalysisBase): + """ + Support for Pixel 6-specific data analysis + + :param trace: input Trace object + :type trace: lisa.trace.Trace + """ + + name = 'pixel6' + + EMETER_CHAN_NAMES = { + 'S4M_VDD_CPUCL0': 'little', + 'S3M_VDD_CPUCL1': 'mid', + 'S2M_VDD_CPUCL2': 'big', + } + +############################################################################### +# DataFrame Getter Methods +############################################################################### + @TraceAnalysisBase.cache + @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 (description of the power usage channel) + * A ``value`` 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)] + df = df.groupby(['chan_name'], observed=True).apply( + lambda x: df_add_delta(x, col='value_diff', src_col='value', window=self.trace.window)['value_diff'] / df_add_delta(x, col='ts_diff', src_col='ts', window=self.trace.window)['ts_diff'] + ).reset_index().rename(columns={0:'value', 'chan_name':'channel'}).dropna().set_index('Time') + 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 = channels if channels is not None else list(df['channel'].cat.categories) + 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'], observed=True))) + return hv.Overlay([ + plot_signal(channel_data[channel]['value'], name=channel, vdim=hv.Dimension('value', label='Power', unit='mW')) + for channel in channels + ]).opts(title='Power usage per channel over time') diff --git a/lisa/notebook.py b/lisa/notebook.py index 04feab1bbc69bb2529accc788362f06a9ca80e52..396d3a2d2432e3f5a3ccdb779667574d75755eb7 100644 --- a/lisa/notebook.py +++ b/lisa/notebook.py @@ -376,7 +376,7 @@ def make_figure(width, height, nrows, ncols, interactive=None, **kwargs): return (figure, axes) -def plot_signal(series, name=None, interpolation=None, add_markers=True): +def plot_signal(series, name=None, interpolation=None, add_markers=True, vdim=None): """ Plot a signal using ``holoviews`` library. @@ -393,6 +393,9 @@ def plot_signal(series, name=None, interpolation=None, add_markers=True): :param add_markers: Add markers to the plot. :type add_markers: bool + + :param vdim: Value axis dimension. + :type vdim: holoviews.Dimension """ if isinstance(series, pd.DataFrame): try: @@ -409,12 +412,16 @@ def plot_signal(series, name=None, interpolation=None, add_markers=True): # We don't set the unit as this will prevent shared_axes to work if # the other plots do not set the unit, which is what usually # happens, since only name/label is taken from pandas index names. - hv.Dimension('Time'), + hv.Dimension('Time', unit='s'), ] + vdims = [ + vdim + ] if vdim is not None else None fig = hv.Curve( series, label=label, kdims=kdims, + vdims=vdims, ).opts( interpolation=interpolation, title=label, @@ -429,6 +436,7 @@ def plot_signal(series, name=None, interpolation=None, add_markers=True): label=label, group='marker', kdims=kdims, + vdims=vdims, ) return fig