diff --git a/libs/utils/analysis_tool.py b/libs/utils/analysis_tool.py new file mode 100644 index 0000000000000000000000000000000000000000..846c6a6c4269cebc380518c1299dd085a1816516 --- /dev/null +++ b/libs/utils/analysis_tool.py @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2017, ARM Limited, Google 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 argparse +import logging +import os +import select +from trace import Trace +from conf import LisaLogging +from devlib.utils.misc import memoized + +class AnalysisTool(object): + """ + A base class for LISA analysis command-line tools. + + This class is intended to be subclassed in order to create a custom + analysis tool. + + :param options_parser: A parser object passed by the specific tool + to parse additional options if any (optional). + :type options_parser: ArgumentParser + """ + + def __init__(self, options_parser=None): + self.options_parser = options_parser + + def _setup_logging(self): + """ + Setup logging for the analysis tool. + + Analysis tools shouldn't output to standard output as their + output can be used by other tools. Configure the logger to + output only error messages. + """ + self._log = logging.getLogger('AnalysisTool') + level = logging.INFO if self.args.log_stdout else logging.ERROR + LisaLogging.setup(level=level) + + def _parse_args(self): + """ + Parse command line arguments given to the tool, also use the parser + object if one is passed by the more specific class so that the + tool specific options are also parsed. + """ + parser = self.options_parser + if not parser: + parser = argparse.ArgumentParser( + description='LISA Analysis tool configuration') + + # General options that all tools accept + parser.add_argument('--trace', type=str, + help='Path of the trace file or directory to be used', + required=True) + + parser.add_argument('--log-stdout', action='store_true', + help='Output logging to stdout', + required=False) + + self.args = parser.parse_args() + + def run_analysis(self): + """ + Run an analysis and store results + + The specific analysis tool should override this and perform + analysis and provide results in self.output. + """ + raise NotImplementedError('Analysis tool needs to define run_analysis') + + def run_main(self): + """ + The main function that sets up, runs the analysis and provides + output to the user. + + The specific analysis tool should call this function to start the analysis + which will perform analysis and report the result. The tool should also + override run_analysis to provide custom analysis and optionally override + run_output to provide custom output. + """ + + self._parse_args() + self._setup_logging() + + # Create trace object + self._log.info('Creating trace object...') + self.trace = Trace(None, self.args.trace) + + self.run_analysis() diff --git a/libs/utils/trace.py b/libs/utils/trace.py index 80b6133d7fa959dea58dc3268ce677eff4807e72..1a7e823539fec8905a3a25636a104c858cc1f345 100644 --- a/libs/utils/trace.py +++ b/libs/utils/trace.py @@ -69,7 +69,7 @@ class Trace(object): :type plots_prefix: str """ - def __init__(self, platform, data_dir, events, + def __init__(self, platform, data_dir, events=None, window=(0, None), normalize_time=True, trace_format='FTrace', @@ -125,7 +125,7 @@ class Trace(object): self.plots_dir = self.data_dir self.plots_prefix = plots_prefix - self.__registerTraceEvents(events) + self.__registerTraceEvents(events) if events else None self.__parseTrace(data_dir, window, normalize_time, trace_format) self.__computeTimeSpan() @@ -219,7 +219,7 @@ class Trace(object): :type trace_format: str """ self._log.debug('Loading [sched] events from trace in [%s]...', path) - self._log.debug('Parsing events: %s', self.events) + self._log.debug('Parsing events: %s', self.events if self.events else 'ALL') if trace_format.upper() == 'SYSTRACE' or path.endswith('html'): self._log.debug('Parsing SysTrace format...') trace_class = trappy.SysTrace @@ -231,7 +231,8 @@ class Trace(object): else: raise ValueError("Unknown trace format {}".format(trace_format)) - self.ftrace = trace_class(path, scope="custom", events=self.events, + scope = 'custom' if self.events else 'all' + self.ftrace = trace_class(path, scope=scope, events=self.events, window=window, normalize_time=normalize_time) # Load Functions profiling data diff --git a/tools/analysis/android_gfx.py b/tools/analysis/android_gfx.py new file mode 100755 index 0000000000000000000000000000000000000000..71fa6fa88f801f989b733730c947452dc23bc85e --- /dev/null +++ b/tools/analysis/android_gfx.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# Copyright 2017 ARM Limited, Google 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 pandas as pd +from time import sleep +from devlib.exception import TargetError +from analysis_tool import AnalysisTool + +class AndroidGfx(AnalysisTool): + + def get_frame_drops(self, df): + if not 'func' in df.columns: + return 0 + return len(df[(df.func == 'FrameMissed') & (df.data == '1')]) + + def run_analysis(self): + df = self.trace.data_frame.trace_event('tracing_mark_write') + print {'frame_drops': self.get_frame_drops(df)} + +# Run the analysis and provide results +if __name__ == "__main__": + AndroidGfx().run_main()