From 7c094ba78c268d62e429a671faf5d7354a47e9b9 Mon Sep 17 00:00:00 2001 From: markbellamy <49994920+markbellamy@users.noreply.github.com> Date: Thu, 25 Apr 2019 13:06:01 -0500 Subject: [PATCH 1/2] Add support for "perf record" and "perf report" to WA --- wa/instruments/perf.py | 51 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/wa/instruments/perf.py b/wa/instruments/perf.py index c5179cdfa..610bba9f5 100644 --- a/wa/instruments/perf.py +++ b/wa/instruments/perf.py @@ -77,6 +77,17 @@ class PerfInstrument(Instrument): description="""Provides labels for pref output. If specified, the number of labels must match the number of ``optionstring``\ s. """), + Parameter('perf_mode', kind=str, default='stat', allowed_values=['stat', 'record'], + global_alias='perf_mode', + description=""" + Choose between 'perf stat' and 'perf record'. If 'perf record' is selected + 'perf report' will also be run. 'report_optionstring' can be used to generate + custom report from the trace. + """), + Parameter('report_optionstring', kind=list_or_string, default='', + global_alias='report_perf_options', + description="""Specifies options to be used for the 'perf report' command used + with 'perf_mode=record'. """), Parameter('force_install', kind=bool, default=False, description=""" always install perf binary even if perf is already present on the device. @@ -92,6 +103,8 @@ def initialize(self, context): self.events, self.optionstring, self.labels, + self.perf_mode, + self.report_optionstring, self.force_install) def setup(self, context): @@ -103,11 +116,10 @@ def start(self, context): def stop(self, context): self.collector.stop() - def update_output(self, context): - self.logger.info('Extracting reports from target...') + def _update_output_stat(self, context): outdir = os.path.join(context.output_directory, 'perf') self.collector.get_trace(outdir) - + self.logger.info('Processing perf stat reports.') for host_file in os.listdir(outdir): label = host_file.split('.out')[0] host_file_path = os.path.join(outdir, host_file) @@ -134,5 +146,38 @@ def update_output(self, context): metric = '{}_{}'.format(label, match.group(3)) context.add_metric(metric, count, classifiers=classifiers) + def _update_output_record(self, context): + outdir = os.path.join(context.output_directory, 'perf') + self.collector.get_trace(outdir) + + self.logger.info('Processing perf report reports.') + for host_file in os.listdir(outdir): + label = host_file.split('.rpt')[0] + host_file_path = os.path.join(outdir, host_file) + context.add_artifact(label, host_file_path, 'raw') + with open(host_file_path) as fh: + for line in fh: + words = line.split() + if len(words) >= 1: + if words[0] == '#' and len(words) == 6: + if words[4] == 'event': + event_type = words[5] + event_type = event_type.strip("'") + if words[0] != '#' and '%' in words[0] and len(words) == 5: + metric = 'perf/{}/{}/{}'.format(event_type, words[1], words[2].strip("[]")) + temp = words[0] + count = temp.strip('%') + context.add_metric(metric, count, '%') + + def update_output(self, context): + self.logger.info('Extracting reports from target...') + + # Extract data for 'perf stat' + if self.perf_mode == 'stat': + self._update_output_stat(context) + # Extract data for 'perf record' + elif self.perf_mode == 'record': + self._update_output_record(context) + def teardown(self, context): self.collector.reset() From bbe97ebc070658d30b0c7e94f1fb6c2520b9aae8 Mon Sep 17 00:00:00 2001 From: markbellamy <49994920+markbellamy@users.noreply.github.com> Date: Mon, 29 Apr 2019 16:09:45 -0500 Subject: [PATCH 2/2] Updates based on feedback --- wa/instruments/perf.py | 73 ++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/wa/instruments/perf.py b/wa/instruments/perf.py index 610bba9f5..f84fafefb 100644 --- a/wa/instruments/perf.py +++ b/wa/instruments/perf.py @@ -1,3 +1,4 @@ + # Copyright 2013-2015 ARM Limited # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +23,7 @@ from wa import Instrument, Parameter from wa.utils.types import list_or_string, list_of_strs +from wa.utils.types import numeric PERF_COUNT_REGEX = re.compile(r'^(CPU\d+)?\s*(\d+)\s*(.*?)\s*(\[\s*\d+\.\d+%\s*\])?\s*$') @@ -77,8 +79,8 @@ class PerfInstrument(Instrument): description="""Provides labels for pref output. If specified, the number of labels must match the number of ``optionstring``\ s. """), - Parameter('perf_mode', kind=str, default='stat', allowed_values=['stat', 'record'], - global_alias='perf_mode', + Parameter('mode', kind=str, default='stat', allowed_values=['stat', 'record'], + global_alias='mode', description=""" Choose between 'perf stat' and 'perf record'. If 'perf record' is selected 'perf report' will also be run. 'report_optionstring' can be used to generate @@ -86,8 +88,10 @@ class PerfInstrument(Instrument): """), Parameter('report_optionstring', kind=list_or_string, default='', global_alias='report_perf_options', - description="""Specifies options to be used for the 'perf report' command used - with 'perf_mode=record'. """), + description=""" + Specifies options to be used for the 'perf report' command used + with 'mode=record'. + """), Parameter('force_install', kind=bool, default=False, description=""" always install perf binary even if perf is already present on the device. @@ -103,7 +107,7 @@ def initialize(self, context): self.events, self.optionstring, self.labels, - self.perf_mode, + self.mode, self.report_optionstring, self.force_install) @@ -116,12 +120,22 @@ def start(self, context): def stop(self, context): self.collector.stop() - def _update_output_stat(self, context): + def update_output(self, context): outdir = os.path.join(context.output_directory, 'perf') self.collector.get_trace(outdir) + + # Extract data for 'perf stat' + if self.mode == 'stat': + self._update_output_stat(context, outdir) + # Extract data for 'perf record' + elif self.mode == 'record': + self._update_output_record(context, outdir) + + def _update_output_stat(self, context, outdir): self.logger.info('Processing perf stat reports.') + for host_file in os.listdir(outdir): - label = host_file.split('.out')[0] + label = os.path.splitext(host_file)[0] host_file_path = os.path.join(outdir, host_file) context.add_artifact(label, host_file_path, 'raw') with open(host_file_path) as fh: @@ -146,38 +160,27 @@ def _update_output_stat(self, context): metric = '{}_{}'.format(label, match.group(3)) context.add_metric(metric, count, classifiers=classifiers) - def _update_output_record(self, context): - outdir = os.path.join(context.output_directory, 'perf') - self.collector.get_trace(outdir) + def _update_output_record(self, context, outdir): + self.logger.info('Processing "perf report" reports.') - self.logger.info('Processing perf report reports.') for host_file in os.listdir(outdir): - label = host_file.split('.rpt')[0] + label, ext = os.path.splitext(host_file) host_file_path = os.path.join(outdir, host_file) context.add_artifact(label, host_file_path, 'raw') - with open(host_file_path) as fh: - for line in fh: - words = line.split() - if len(words) >= 1: - if words[0] == '#' and len(words) == 6: - if words[4] == 'event': - event_type = words[5] - event_type = event_type.strip("'") - if words[0] != '#' and '%' in words[0] and len(words) == 5: - metric = 'perf/{}/{}/{}'.format(event_type, words[1], words[2].strip("[]")) - temp = words[0] - count = temp.strip('%') - context.add_metric(metric, count, '%') - - def update_output(self, context): - self.logger.info('Extracting reports from target...') - - # Extract data for 'perf stat' - if self.perf_mode == 'stat': - self._update_output_stat(context) - # Extract data for 'perf record' - elif self.perf_mode == 'record': - self._update_output_record(context) + if ext == '.rpt': + with open(host_file_path) as fh: + for line in fh: + words = line.split() + if len(words) >= 1: + if words[0] == '#' and len(words) == 6: + if words[4] == 'event': + event_type = words[5] + event_type = event_type.strip("'") + if words[0] != '#' and '%' in words[0] and len(words) == 5: + metric = 'perf/{}/{}/{}'.format(event_type, words[1], words[2].strip("[]")) + count = numeric(words[0].strip('%')) + context.add_metric(metric, count, '%', + classifiers={'Perf Event': event_type, 'Command': words[1], 'Shared Object' : words[2]}) def teardown(self, context): self.collector.reset()