From a5e184e453016c8ea81a4ab0421da9f050b59192 Mon Sep 17 00:00:00 2001 From: Douglas RAILLARD Date: Tue, 12 Feb 2019 15:25:28 +0000 Subject: [PATCH 1/2] lisa: PerfAnalysis: cleanup * Move to lisa.analysis.rta * Give "action" name to methods where appropriate ('get_' prefix) * turn tasks() into a property * Streamline __init__ so it takes the common denominator between all ways of building the object. * Add alternative factories: * from_log_files * from_task_names * from_dir note: from_dir() has a similar behavior to the former constructor. * Rename camelcase methods to PEP-8 naming style like the rest of the code * Remove leading double undescores from method names to avoid name mangling. * Split plot_perf into 3 separate plot methods, like it is done in other analysis classes * Remove dead code here and there * Use pandas.read_csv instead of read_table to avoid FutureWarning Changes to AnalysisBase: * Split into AnalysisHelpers and AnalysisBase * Improve AnalysisHelpers.save_to() to infer the image format from filepath. --- lisa/analysis/base.py | 82 ++++++--- lisa/analysis/rta.py | 243 ++++++++++++++++++++++++++ lisa/perf_analysis.py | 193 -------------------- lisa/tests/scheduler/eas_behaviour.py | 13 +- tests/test_wlgen_rtapp.py | 6 +- 5 files changed, 311 insertions(+), 226 deletions(-) create mode 100644 lisa/analysis/rta.py delete mode 100644 lisa/perf_analysis.py diff --git a/lisa/analysis/base.py b/lisa/analysis/base.py index 531fe870c..d71b32f74 100644 --- a/lisa/analysis/base.py +++ b/lisa/analysis/base.py @@ -15,9 +15,9 @@ # limitations under the License. # -import functools import os import inspect +import mimetypes import matplotlib.pyplot as plt from cycler import cycler @@ -32,25 +32,16 @@ COLOR_CYCLES = [ plt.rcParams['axes.prop_cycle'] = cycler(color=COLOR_CYCLES) -class AnalysisBase(Loggable): +class AnalysisHelpers(Loggable): """ - Base class for Analysis modules. - - :param trace: input Trace object - :type trace: :class:`trace.Trace` + Helper methods class for Analysis modules. :Design notes: - Method depending on certain trace events *must* be decorated with - :meth:`lisa.trace.requires_events` - Plotting methods *must* return the :class:`matplotlib.axes.Axes` instance used by the plotting method. This lets users further modify them. """ - def __init__(self, trace): - self.trace = trace - @classmethod def setup_plot(cls, width=16, height=4, ncols=1, nrows=1, **kwargs): """ @@ -127,7 +118,22 @@ class AnalysisBase(Loggable): # plot all data from a dataframe in the same color. return next(axis._get_lines.prop_cycler)['color'] - def save_plot(self, figure, filepath=None, img_format="png"): + def _save_plot(self, figure, default_dir, filepath=None, img_format=None, wrapper_level=2): + if filepath is None: + img_format = img_format or 'png' + module = self.__module__ + caller = inspect.stack()[1 + wrapper_level][3] + filepath = os.path.join( + default_dir, + "{}.{}.{}".format(module, caller, img_format)) + else: + mime_type = mimetypes.guess_type(filepath, strict=False)[0] + guessed_format = mime_type.split('/')[1].split('.', 1)[-1].split('+')[0] + img_format = img_format or guessed_format + + figure.savefig(filepath, format=img_format) + + def save_plot(self, figure, filepath=None, img_format=None): """ Save the plot stored in the ``figure`` @@ -136,20 +142,52 @@ class AnalysisBase(Loggable): :param filepath: The path of the file into which the plot will be saved. If ``None``, a path based on the trace directory and the calling method - will be used. + will be used. The filepath is also used to deduct the image format. :type filepath: str - :param img_format: The image format to generate + :param img_format: The image format to generate. Defaults to using + filepath to guess the type, or "png" if no filepath is given. :type img_format: str """ - if filepath is None: - module = self.__module__ - caller = inspect.stack()[1][3] - filepath = os.path.join( - self.trace.plots_dir, - "{}.{}.{}".format(module, caller, img_format)) + default_dir = '.' + return self._save_plot(figure, default_dir, filepath, img_format) - figure.savefig(filepath, format=img_format) + def _do_plot(self, plotter, filepath, axis): + """ + Simple helper for consistent behavior across methods. + """ + + local_fig = not axis + if local_fig: + fig, axis = self.setup_plot() + + plotter(axis) + + if local_fig: + self.save_plot(fig, filepath) + return axis + +class AnalysisBase(AnalysisHelpers): + """ + Base class for Analysis modules. + + :param trace: input Trace object + :type trace: :class:`trace.Trace` + :Design notes: + + Method depending on certain trace events *must* be decorated with + :meth:`lisa.trace.requires_events` + """ + + def __init__(self, trace): + self.trace = trace + + def save_plot(self, figure, filepath=None, img_format=None): + """ + See :meth:`AnalysisHelpers.save_plot` + """ + default_dir = self.trace.plots_dir + return self._save_plot(figure, default_dir, filepath, img_format) # vim :set tabstop=4 shiftwidth=4 expandtab textwidth=80 diff --git a/lisa/analysis/rta.py b/lisa/analysis/rta.py new file mode 100644 index 000000000..c4f377e63 --- /dev/null +++ b/lisa/analysis/rta.py @@ -0,0 +1,243 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2018, 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 os +import re +import glob + +import pandas as pd + +from lisa.utils import Loggable +from lisa.analysis.base import AnalysisHelpers + +class PerfAnalysis(AnalysisHelpers): + """ + Parse and analyse a set of RTApp log files + + :param task_log_map: Mapping of task names to log files + :type task_log_map: dict + + .. note:: That is not a subclass of + :class:`lisa.analysis.base.AnalysisBase` since it does not uses traces. + """ + + RTA_LOG_PATTERN = 'rt-app-{task}.log' + "Filename pattern matching RTApp log files" + + def __init__(self, task_log_map): + """ + Load peformance data of an rt-app workload + """ + logger = self.get_logger() + + if not task_log_map: + raise ValueError('No tasks in the task log mapping') + + for task_name, logfile in task_log_map.items(): + logger.debug('rt-app task [{}] logfile: {}'.format( + task_name, logfile + )) + + self.perf_data = { + task_name: { + 'logfile': logfile, + 'df': self._parse_df(logfile), + } + for task_name, logfile in task_log_map.items() + } + + @classmethod + def from_log_files(cls, rta_logs): + """ + Build a :class:`PerfAnalysis` from a sequence of RTApp log files + + :param rta_logs: sequence of path to log files + :type rta_logs: list(str) + """ + + def find_task_name(logfile): + logfile = os.path.basename(logfile) + regex = cls.RTA_LOG_PATTERN.format(task=r'(.+)-[0-9]+') + match = re.search(regex, logfile) + if not match: + raise ValueError('The logfile [{}] is not from rt-app'.format(logfile)) + return match.group(1) + + task_log_map = { + find_task_name(logfile): logfile + for logfile in rta_logs + } + return cls(task_log_map) + + @classmethod + def from_dir(cls, log_dir): + """ + Build a :class:`PerfAnalysis` from a folder path + + :param log_dir: Folder containing RTApp log files + :type log_dir: str + """ + rta_logs = glob.glob(os.path.join( + log_dir, cls.RTA_LOG_PATTERN.format(task='*'), + )) + return cls.from_log_files(rta_logs) + + @classmethod + def from_task_names(cls, task_names, log_dir): + """ + Build a :class:`PerfAnalysis` from a list of task names + + :param task_names: List of task names to look for + :type task_names: list(str) + + :param log_dir: Folder containing RTApp log files + :type log_dir: str + """ + def find_log_file(task_name, log_dir): + log_file = os.path.join(log_dir, cls.RTA_LOG_PATTERN.format(task_name)) + if not os.path.isfile(log_file): + raise ValueError('No rt-app logfile found for task [{}]'.format( + task_name + )) + return log_file + + task_log_map = { + task_name: find_log_file(task, log_dir) + for task_name in tasks + } + return cls(task_log_map) + + @staticmethod + def _parse_df(logfile): + df = pd.read_csv(logfile, + sep='\s+', + skiprows=1, + header=0, + usecols=[1,2,3,4,7,8,9,10], + names=[ + 'Cycles', 'Run' ,'Period', 'Timestamp', + 'Slack', 'CRun', 'CPeriod', 'WKPLatency' + ]) + # Normalize time to [s] with origin on the first event + start_time = df['Timestamp'][0]/1e6 + df['Time'] = df['Timestamp']/1e6 - start_time + df.set_index(['Time'], inplace=True) + # Add performance metrics column, performance is defined as: + # slack + # perf = ------------- + # period - run + df['PerfIndex'] = df['Slack'] / (df['CPeriod'] - df['CRun']) + + return df + + @property + def tasks(self): + """ + List of tasks for which performance data have been loaded + """ + return sorted(self.perf_data.keys()) + + def get_log_file(self, task): + """ + Return the logfile for the specified task + + :param task: Name of the task that we want the logfile of. + :type task: str + """ + return self.perf_data[task]['logfile'] + + def get_df(self, task): + """ + Return the pandas dataframe with the performance data for the + specified task + + :param task: Name of the task that we want the performance dataframe of. + :type task: str + """ + return self.perf_data[task]['df'] + + def save_plot(self, figure, filepath=None, img_format=None): + # If all logfiles are located in the same folder, use that folder + # and the default_filename + dirnames = { + os.path.realpath(os.path.dirname(perf_data['logfile'])) + for perf_data in self.perf_data.values() + } + if not filepath and len(dirnames) != 1: + raise ValueError('Please specify filepath or axis, since a default folder cannot be inferred from logfiles location') + + default_dir = dirnames.pop() + return self._save_plot(figure, default_dir, filepath, img_format) + + def plot_perf(self, task, filepath=None, axis=None): + """ + Plot the performance Index + + :param axis: If specified, the axis to use for plotting + :type axis: matplotlib.axes.Axes + + :param filepath: If no axis is specified, the figure will be saved to + that path + """ + def plotter(axis): + axis.set_title('Task [{0:s}] Performance Index'.format(task)) + data = self.get_df(task)[['PerfIndex',]] + data.plot(ax=axis, drawstyle='steps-post') + axis.set_ylim(0, 2) + + return self._do_plot(plotter, filepath, axis) + + def plot_latency(self, task, filepath=None, axis=None): + """ + Plot the Latency/Slack and Performance data for the specified task. + + :param axis: If specified, the axis to use for plotting + :type axis: matplotlib.axes.Axes + + :param filepath: If no axis is specified, the figure will be saved to + that path + """ + def plotter(axis): + axis.set_title('Task [{0:s}] (start) Latency and (completion) Slack'\ + .format(task)) + data = self.get_df(task)[['Slack', 'WKPLatency']] + data.plot(ax=axis, drawstyle='steps-post') + + return self._do_plot(plotter, filepath, axis) + + def plot_slack_histogram(self, task, filepath=None, axis=None): + """ + Plot the Slack Histogram + + :param axis: If specified, the axis to use for plotting + :type axis: matplotlib.axes.Axes + + :param filepath: If no axis is specified, the figure will be saved to + that path + """ + def plotter(axis): + data = self.get_df(task)[['PerfIndex',]] + data.hist(bins=30, ax=axis, alpha=0.4) + pindex_avg = data.mean()[0] + pindex_std = data.std()[0] + self.get_logger().info('PerfIndex, Task [%s] avg: %.2f, std: %.2f', + task, pindex_avg, pindex_std) + axis.axvline(pindex_avg, linestyle='--', linewidth=2) + + return self._do_plot(plotter, filepath, axis) + +# vim :set tabstop=4 shiftwidth=4 textwidth=80 expandtab diff --git a/lisa/perf_analysis.py b/lisa/perf_analysis.py deleted file mode 100644 index 652871d48..000000000 --- a/lisa/perf_analysis.py +++ /dev/null @@ -1,193 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# -# Copyright (C) 2015, 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 glob -import matplotlib.gridspec as gridspec -import matplotlib.pyplot as plt -import numpy as np -import os -import os.path -import pandas as pd -import pylab as pl -import re -import sys -import trappy -import logging - -from lisa.utils import Loggable - -# Regexp to match an rt-app generated logfile -TASK_NAME_RE = re.compile('.*\/rt-app-(.+)-[0-9]+.log') - -#TODO: Should that class be renamed ? It is not an Analysis class like the one -# in the lisa.analysis package, so it may be better to avoid that name here -class PerfAnalysis(Loggable): - - def __init__(self, datadir, tasks=None): - # Dataframe of all tasks performance data - self.perf_data = {} - - # Folder containing all rt-app data - self.datadir = None - - # Load performance data generated by rt-app workloads - self.__loadRTAData(datadir, tasks) - - # Keep track of the datadir from where data have been loaded - if len(self.perf_data) == 0: - raise ValueError('No performance data found on folder [{0:s}]'\ - .format(datadir)) - - self.datadir = datadir - - #TODO: remove the leading "__" since they are mangled and basically break - # inheritance and discovery of the API - def __taskNameFromLog(self, logfile): - tname_match = re.search(TASK_NAME_RE, logfile) - if tname_match is None: - raise ValueError('The logfile [{0:s}] is not from rt-app'\ - .format(logfile)) - return tname_match.group(1) - - def __logfileFromTaskName(self, taskname): - for logfile in glob.glob( - '{0:s}/rt-app-{1:s}.log'.format(self.datadir, taskname)): - return logfile - raise ValueError('No rt-app logfile found for task [{0:s}]'\ - .format(taskname)) - - def tasks(self): - """ - Return the list of tasks for which performance data have been loaded - """ - if self.datadir is None: - raise ValueError("rt-app performance data not (yet) loaded") - return list(self.perf_data.keys()) - - def logfile(self, task): - """ - Return the logfile for the specified task - """ - if task not in self.perf_data: - raise ValueError('No logfile loaded for task [{0:s}]'\ - .format(task)) - return self.perf_data[task]['logfile'] - - def df(self, task): - """ - Return the PANDAS dataframe with the performance data for the - specified task - """ - if self.datadir is None: - raise ValueError("rt-app performance data not (yet) loaded") - if task not in self.perf_data: - raise ValueError('No dataframe loaded for task [{0:s}]'\ - .format(task)) - return self.perf_data[task]['df'] - - def __loadRTAData(self, datadir, tasks): - """ - Load peformance data of an rt-app workload - """ - logger = self.get_logger() - - if tasks is None: - # Lookup for all rt-app logfile into the specified datadir - for logfile in glob.glob('{0:s}/rt-app-*.log'.format(datadir)): - task_name = self.__taskNameFromLog(logfile) - self.perf_data[task_name] = {} - self.perf_data[task_name]['logfile'] = logfile - logger.debug('Found rt-app logfile for task [%s]', task_name) - else: - # Lookup for specified rt-app task logfile into specified datadir - for task in tasks: - logfile = self.__logfileFromTaskName(task) - self.perf_data[task_name] = {} - self.perf_data[task_name]['logfile'] = logfile - logger.debug('Found rt-app logfile for task [%s]', task_name) - - # Load all the found logfile into a dataset - for task in list(self.perf_data.keys()): - logger.debug('Loading dataframe for task [%s]...', task) - df = pd.read_table(self.logfile(task), - sep='\s+', - skiprows=1, - header=0, - usecols=[1,2,3,4,7,8,9,10], - names=[ - 'Cycles', 'Run' ,'Period', 'Timestamp', - 'Slack', 'CRun', 'CPeriod', 'WKPLatency' - ]) - # Normalize time to [s] with origin on the first event - start_time = df['Timestamp'][0]/1e6 - df['Time'] = df['Timestamp']/1e6 - start_time - df.set_index(['Time'], inplace=True) - # Add performance metrics column, performance is defined as: - # slack - # perf = ------------- - # period - run - df['PerfIndex'] = df['Slack'] / (df['CPeriod'] - df['CRun']) - - # Keep track of the loaded dataframe - self.perf_data[task]['df'] = df - - def plotPerf(self, task, title=None): - """ - Plot the Latency/Slack and Performance data for the specified task - """ - logger = self.get_logger() - # Grid - gs = gridspec.GridSpec(2, 2, height_ratios=[4,1], width_ratios=[3,1]); - gs.update(wspace=0.1, hspace=0.1); - # Figure - plt.figure(figsize=(16, 2*6)); - if title: - plt.suptitle(title, y=.97, fontsize=16, - horizontalalignment='center'); - # Plot: Slack and Latency - axes = plt.subplot(gs[0,0]); - axes.set_title('Task [{0:s}] (start) Latency and (completion) Slack'\ - .format(task)); - data = self.df(task)[['Slack', 'WKPLatency']] - data.plot(ax=axes, drawstyle='steps-post', style=['b', 'g']); - # axes.set_xlim(x_min, x_max); - axes.xaxis.set_visible(False); - # Plot: Performance - axes = plt.subplot(gs[1,0]); - axes.set_title('Task [{0:s}] Performance Index'.format(task)); - data = self.df(task)[['PerfIndex',]] - data.plot(ax=axes, drawstyle='steps-post'); - axes.set_ylim(0, 2); - # axes.set_xlim(x_min, x_max); - # Plot: Slack Histogram - axes = plt.subplot(gs[0:2,1]); - data = self.df(task)[['PerfIndex',]] - data.hist(bins=30, ax=axes, alpha=0.4); - # axes.set_xlim(x_min, x_max); - pindex_avg = data.mean()[0]; - pindex_std = data.std()[0]; - logger.info('PerfIndex, Task [%s] avg: %.2f, std: %.2f', - task, pindex_avg, pindex_std) - axes.axvline(pindex_avg, color='b', linestyle='--', linewidth=2); - - - # Save generated plots into datadir - figname = '{}/task_perf_{}.png'.format(self.datadir, task) - pl.savefig(figname, bbox_inches='tight') - - -# vim :set tabstop=4 shiftwidth=4 textwidth=80 expandtab diff --git a/lisa/tests/scheduler/eas_behaviour.py b/lisa/tests/scheduler/eas_behaviour.py index cd21a8176..ae30c239c 100644 --- a/lisa/tests/scheduler/eas_behaviour.py +++ b/lisa/tests/scheduler/eas_behaviour.py @@ -28,12 +28,12 @@ from bart.common.Utils import area_under_curve from devlib.target import KernelVersion from lisa.wlgen.rta import Periodic, Ramp, Step +from lisa.analysis.rta import PerfAnalysis from lisa.tests.base import ResultBundle, CannotCreateError, RTATestBundle from lisa.env import TestEnv from lisa.utils import ArtifactPath from lisa.energy_model import EnergyModel from lisa.trace import requires_events -from lisa.perf_analysis import PerfAnalysis class EASBehaviour(RTATestBundle, abc.ABC): """ @@ -365,15 +365,12 @@ class EASBehaviour(RTATestBundle, abc.ABC): was negative). Assert that this happened less than ``negative_slack_allowed_pct`` percent of the time. """ - pa = PerfAnalysis(self.res_dir) + analysis = PerfAnalysis.from_dir(self.res_dir) - bad_activations = {} - - # Data is only collected for rt-app tasks, so it's safe to iterate over - # all of them passed = True - for task in pa.tasks(): - slack = pa.df(task)["Slack"] + bad_activations = {} + for task in analysis.tasks: + slack = analysis.get_df(task)["Slack"] bad_activations_pct = len(slack[slack < 0]) * 100 / len(slack) if bad_activations_pct > negative_slack_allowed_pct: diff --git a/tests/test_wlgen_rtapp.py b/tests/test_wlgen_rtapp.py index b20c7209f..cf29ba54d 100644 --- a/tests/test_wlgen_rtapp.py +++ b/tests/test_wlgen_rtapp.py @@ -19,8 +19,8 @@ from collections import OrderedDict, namedtuple import json import os -from lisa.perf_analysis import PerfAnalysis from lisa.wlgen.rta import RTA, Periodic, Ramp, Step, RunAndSync +from lisa.analysis.rta import PerfAnalysis from .utils import StorageTestCase, create_local_testenv @@ -52,8 +52,8 @@ class RTABase(StorageTestCase): def assert_can_read_logfile(self, exp_tasks): """Assert that the perf_analysis module understands the log output""" - pa = PerfAnalysis(self.res_dir) - self.assertSetEqual(set(exp_tasks), set(pa.tasks())) + analysis = PerfAnalysis.from_dir(self.res_dir) + self.assertSetEqual(set(exp_tasks), set(analysis.tasks)) class TestRTAProfile(RTABase): def _do_test(self, profile, exp_phases): -- GitLab From ab82fc050d61cdc5eeab975f1a7037444a460af8 Mon Sep 17 00:00:00 2001 From: Douglas RAILLARD Date: Tue, 26 Feb 2019 17:16:16 +0000 Subject: [PATCH 2/2] lisa.analysis: Rename AnalysisBase into TraceAnalysisBase This makes it clear that this base class is only relevant for analysis based on a lisa.trace.Trace. Other kind of analysis classes can inherit from AnalysisHelpers. --- lisa/analysis/__init__.py | 2 +- lisa/analysis/base.py | 2 +- lisa/analysis/cpus.py | 4 ++-- lisa/analysis/frequency.py | 4 ++-- lisa/analysis/functions.py | 4 ++-- lisa/analysis/idle.py | 4 ++-- lisa/analysis/latency.py | 4 ++-- lisa/analysis/load_tracking.py | 4 ++-- lisa/analysis/proxy.py | 4 ++-- lisa/analysis/rta.py | 2 +- lisa/analysis/status.py | 4 ++-- lisa/analysis/tasks.py | 4 ++-- lisa/analysis/thermal.py | 4 ++-- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lisa/analysis/__init__.py b/lisa/analysis/__init__.py index 2ab88e2db..f6bff51f9 100644 --- a/lisa/analysis/__init__.py +++ b/lisa/analysis/__init__.py @@ -20,7 +20,7 @@ import pkgutil # Import all the submodules before they are asked for by user code, since we # need to create the *Analysis classes in order for them to be registered -# against AnalysisBase +# against TraceAnalysisBase __all__ = [] for loader, module_name, is_pkg in pkgutil.walk_packages(__path__): __all__.append(module_name) diff --git a/lisa/analysis/base.py b/lisa/analysis/base.py index d71b32f74..6cefaae33 100644 --- a/lisa/analysis/base.py +++ b/lisa/analysis/base.py @@ -167,7 +167,7 @@ class AnalysisHelpers(Loggable): self.save_plot(fig, filepath) return axis -class AnalysisBase(AnalysisHelpers): +class TraceAnalysisBase(AnalysisHelpers): """ Base class for Analysis modules. diff --git a/lisa/analysis/cpus.py b/lisa/analysis/cpus.py index ce20b4615..5f66e66ca 100644 --- a/lisa/analysis/cpus.py +++ b/lisa/analysis/cpus.py @@ -20,11 +20,11 @@ import pandas as pd from lisa.utils import memoized -from lisa.analysis.base import AnalysisBase +from lisa.analysis.base import TraceAnalysisBase from lisa.trace import requires_events -class CpusAnalysis(AnalysisBase): +class CpusAnalysis(TraceAnalysisBase): """ Support for CPUs signals analysis """ diff --git a/lisa/analysis/frequency.py b/lisa/analysis/frequency.py index 21dab7ccf..a93a7660f 100644 --- a/lisa/analysis/frequency.py +++ b/lisa/analysis/frequency.py @@ -25,11 +25,11 @@ from matplotlib.ticker import FuncFormatter import pandas as pd import pylab as pl -from lisa.analysis.base import AnalysisBase +from lisa.analysis.base import TraceAnalysisBase from lisa.utils import memoized from lisa.trace import requires_events -class FrequencyAnalysis(AnalysisBase): +class FrequencyAnalysis(TraceAnalysisBase): """ Support for plotting Frequency Analysis data diff --git a/lisa/analysis/functions.py b/lisa/analysis/functions.py index 434d30c0b..f3abc8091 100644 --- a/lisa/analysis/functions.py +++ b/lisa/analysis/functions.py @@ -19,10 +19,10 @@ from trappy.utils import listify -from lisa.analysis.base import AnalysisBase +from lisa.analysis.base import TraceAnalysisBase -class FunctionsAnalysis(AnalysisBase): +class FunctionsAnalysis(TraceAnalysisBase): """ Support for kernel functions profiling and analysis diff --git a/lisa/analysis/idle.py b/lisa/analysis/idle.py index 3aaafcf22..5c438cd25 100644 --- a/lisa/analysis/idle.py +++ b/lisa/analysis/idle.py @@ -23,11 +23,11 @@ import pandas as pd from trappy.utils import handle_duplicate_index from lisa.utils import memoized -from lisa.analysis.base import AnalysisBase +from lisa.analysis.base import TraceAnalysisBase from lisa.trace import requires_events -class IdleAnalysis(AnalysisBase): +class IdleAnalysis(TraceAnalysisBase): """ Support for plotting Idle Analysis data diff --git a/lisa/analysis/latency.py b/lisa/analysis/latency.py index 2748e3708..a7e3a2e73 100644 --- a/lisa/analysis/latency.py +++ b/lisa/analysis/latency.py @@ -18,11 +18,11 @@ import pandas as pd import numpy as np -from lisa.analysis.base import AnalysisBase, COLOR_CYCLES +from lisa.analysis.base import TraceAnalysisBase, COLOR_CYCLES from lisa.analysis.tasks import TaskState, TasksAnalysis from lisa.utils import memoized -class LatencyAnalysis(AnalysisBase): +class LatencyAnalysis(TraceAnalysisBase): """ Support for plotting Latency Analysis data diff --git a/lisa/analysis/load_tracking.py b/lisa/analysis/load_tracking.py index 6f4ad9754..d52299509 100644 --- a/lisa/analysis/load_tracking.py +++ b/lisa/analysis/load_tracking.py @@ -19,10 +19,10 @@ import pandas as pd -from lisa.analysis.base import AnalysisBase +from lisa.analysis.base import TraceAnalysisBase -class LoadTrackingAnalysis(AnalysisBase): +class LoadTrackingAnalysis(TraceAnalysisBase): """ Support for scheduler load tracking analysis diff --git a/lisa/analysis/proxy.py b/lisa/analysis/proxy.py index 65537ee4f..e7904e3ac 100644 --- a/lisa/analysis/proxy.py +++ b/lisa/analysis/proxy.py @@ -24,7 +24,7 @@ import logging import inspect import itertools -from lisa.analysis.base import AnalysisBase +from lisa.analysis.base import TraceAnalysisBase from lisa.utils import Loggable, get_subclasses class AnalysisProxy(Loggable): @@ -41,7 +41,7 @@ class AnalysisProxy(Loggable): # will have had a chance to get registered at that point self._class_map = { cls.name: cls - for cls in get_subclasses(AnalysisBase) + for cls in get_subclasses(TraceAnalysisBase) # Classes without a "name" attribute directly defined in their # scope will not get registered. That allows having unnamed # intermediate base classes that are not meant to be exposed. diff --git a/lisa/analysis/rta.py b/lisa/analysis/rta.py index c4f377e63..2f7e1e25b 100644 --- a/lisa/analysis/rta.py +++ b/lisa/analysis/rta.py @@ -32,7 +32,7 @@ class PerfAnalysis(AnalysisHelpers): :type task_log_map: dict .. note:: That is not a subclass of - :class:`lisa.analysis.base.AnalysisBase` since it does not uses traces. + :class:`lisa.analysis.base.TraceAnalysisBase` since it does not uses traces. """ RTA_LOG_PATTERN = 'rt-app-{task}.log' diff --git a/lisa/analysis/status.py b/lisa/analysis/status.py index aa9d665e0..de923448b 100644 --- a/lisa/analysis/status.py +++ b/lisa/analysis/status.py @@ -19,10 +19,10 @@ """ System Status Analaysis Module """ -from lisa.analysis.base import AnalysisBase +from lisa.analysis.base import TraceAnalysisBase from lisa.trace import requires_events -class StatusAnalysis(AnalysisBase): +class StatusAnalysis(TraceAnalysisBase): """ Support for System Status analysis diff --git a/lisa/analysis/tasks.py b/lisa/analysis/tasks.py index 4be53f7ec..fa652521a 100644 --- a/lisa/analysis/tasks.py +++ b/lisa/analysis/tasks.py @@ -20,7 +20,7 @@ from enum import Enum import numpy as np import pandas as pd -from lisa.analysis.base import AnalysisBase +from lisa.analysis.base import TraceAnalysisBase from lisa.utils import memoized from lisa.trace import requires_events @@ -109,7 +109,7 @@ class TaskState(StateInt, Enum): return res -class TasksAnalysis(AnalysisBase): +class TasksAnalysis(TraceAnalysisBase): """ Support for Tasks signals analysis. diff --git a/lisa/analysis/thermal.py b/lisa/analysis/thermal.py index 0b49964b6..6a84a16f2 100644 --- a/lisa/analysis/thermal.py +++ b/lisa/analysis/thermal.py @@ -19,12 +19,12 @@ from matplotlib.ticker import MaxNLocator from devlib.utils.misc import list_to_mask, mask_to_list -from lisa.analysis.base import AnalysisBase +from lisa.analysis.base import TraceAnalysisBase from lisa.utils import memoized from lisa.trace import requires_events -class ThermalAnalysis(AnalysisBase): +class ThermalAnalysis(TraceAnalysisBase): """ Support for plotting Thermal Analysis data -- GitLab