From 16481a21cbf55ae459f9e970a249d74f82bff5a7 Mon Sep 17 00:00:00 2001 From: markbellamy <49994920+markbellamy@users.noreply.github.com> Date: Thu, 25 Apr 2019 13:16:30 -0500 Subject: [PATCH 1/2] Add support for 'perf record and 'perf report' to devlib --- devlib/trace/perf.py | 54 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/devlib/trace/perf.py b/devlib/trace/perf.py index 5d5ac9bdd..af70258a2 100644 --- a/devlib/trace/perf.py +++ b/devlib/trace/perf.py @@ -1,3 +1,4 @@ + # Copyright 2018 ARM Limited # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,7 +24,8 @@ from devlib.utils.misc import ensure_file_directory_exists as _f -PERF_COMMAND_TEMPLATE = '{} stat {} {} sleep 1000 > {} 2>&1 ' +PERF_COMMAND_TEMPLATE = '{} {} {} {} sleep 1000 > {} 2>&1 ' +PERF_REPORT_COMMAND_TEMPLATE = '{} report {} -i {} > {} 2>&1 ' PERF_COUNT_REGEX = re.compile(r'^(CPU\d+)?\s*(\d+)\s*(.*?)\s*(\[\s*\d+\.\d+%\s*\])?\s*$') @@ -69,6 +71,8 @@ def __init__(self, target, events=None, optionstring=None, labels=None, + perf_mode='stat', + report_optionstring=None, force_install=False): super(PerfCollector, self).__init__(target) self.events = events if events else DEFAULT_EVENTS @@ -82,10 +86,20 @@ def __init__(self, target, self.optionstrings = [optionstring] if self.events and isinstance(self.events, basestring): self.events = [self.events] + if isinstance(report_optionstring, list): + self.report_optionstrings = report_optionstring + else: + self.report_optionstrings = [report_optionstring] if not self.labels: self.labels = ['perf_{}'.format(i) for i in range(len(self.optionstrings))] if len(self.labels) != len(self.optionstrings): raise ValueError('The number of labels must match the number of optstrings provided for perf.') + if len(self.optionstrings) != len(self.report_optionstrings): + raise ValueError('The number of report_optionstrings must match the number of optionstrings provided for perf.') + if perf_mode == 'stat' or perf_mode == 'record': + self.perf_mode = perf_mode + elif perf_mode != 'stat' or perf_mode != 'record': + raise ValueError('Invalid perf_mode setting, must be stat or record') self.binary = self.target.get_installed('perf') if self.force_install or not self.binary: @@ -98,6 +112,8 @@ def reset(self): for label in self.labels: filepath = self._get_target_outfile(label) self.target.remove(filepath) + filepath = self._get_target_reportfile(label) + self.target.remove(filepath) def start(self): for command in self.commands: @@ -108,7 +124,23 @@ def stop(self): # pylint: disable=arguments-differ def get_trace(self, outdir): - for label in self.labels: + for label, report_opt in zip(self.labels, self.report_optionstrings): + #in record mode generate report and copy + if self.perf_mode == 'record': + # .rpt + command = self._build_perf_report_command(report_opt, label) + self.target.execute(command, as_root=True) + target_file = self._get_target_reportfile(label) + host_relpath = os.path.basename(target_file) + host_file = _f(os.path.join(outdir, host_relpath)) + self.target.pull(target_file, host_file) + # .trace + target_file = self._get_target_tracefile(label) + host_relpath = os.path.basename(target_file) + host_file = _f(os.path.join(outdir, host_relpath)) + target_file = self._get_target_perfdatafile() + self.target.pull(target_file, host_file) + # .out target_file = self._get_target_outfile(label) host_relpath = os.path.basename(target_file) host_file = _f(os.path.join(outdir, host_relpath)) @@ -131,7 +163,25 @@ def _get_target_outfile(self, label): def _build_perf_command(self, options, events, label): event_string = ' '.join(['-e {}'.format(e) for e in events]) command = PERF_COMMAND_TEMPLATE.format(self.binary, + self.perf_mode, options or '', event_string, self._get_target_outfile(label)) return command + + def _get_target_perfdatafile(self): + return self.target.get_workpath('perf.data') + + def _get_target_reportfile(self, label): + return self.target.get_workpath('{}.rpt'.format(label)) + + def _get_target_tracefile(self, label): + return self.target.get_workpath('{}.trace'.format(label)) + + def _build_perf_report_command(self, reportoptions, label): + reportoptions_string = reportoptions + command = PERF_REPORT_COMMAND_TEMPLATE.format(self.binary, + reportoptions_string, + self._get_target_perfdatafile(), + self._get_target_reportfile(label)) + return command From 7a2d79f916f8597e056e3307b0ce09fd0b6aca34 Mon Sep 17 00:00:00 2001 From: markbellamy <49994920+markbellamy@users.noreply.github.com> Date: Mon, 29 Apr 2019 16:19:49 -0500 Subject: [PATCH 2/2] Updates based on feedback. --- devlib/trace/perf.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/devlib/trace/perf.py b/devlib/trace/perf.py index af70258a2..3f578af23 100644 --- a/devlib/trace/perf.py +++ b/devlib/trace/perf.py @@ -1,4 +1,3 @@ - # Copyright 2018 ARM Limited # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -71,7 +70,7 @@ def __init__(self, target, events=None, optionstring=None, labels=None, - perf_mode='stat', + mode='stat', report_optionstring=None, force_install=False): super(PerfCollector, self).__init__(target) @@ -96,10 +95,10 @@ def __init__(self, target, raise ValueError('The number of labels must match the number of optstrings provided for perf.') if len(self.optionstrings) != len(self.report_optionstrings): raise ValueError('The number of report_optionstrings must match the number of optionstrings provided for perf.') - if perf_mode == 'stat' or perf_mode == 'record': - self.perf_mode = perf_mode - elif perf_mode != 'stat' or perf_mode != 'record': - raise ValueError('Invalid perf_mode setting, must be stat or record') + if mode in ['stat', 'record']: + self.mode = mode + elif mode not in ['stat', 'record']: + raise ValueError('Invalid mode setting, must be stat or record') self.binary = self.target.get_installed('perf') if self.force_install or not self.binary: @@ -126,7 +125,7 @@ def stop(self): def get_trace(self, outdir): for label, report_opt in zip(self.labels, self.report_optionstrings): #in record mode generate report and copy - if self.perf_mode == 'record': + if self.mode == 'record': # .rpt command = self._build_perf_report_command(report_opt, label) self.target.execute(command, as_root=True) @@ -163,7 +162,7 @@ def _get_target_outfile(self, label): def _build_perf_command(self, options, events, label): event_string = ' '.join(['-e {}'.format(e) for e in events]) command = PERF_COMMAND_TEMPLATE.format(self.binary, - self.perf_mode, + self.mode, options or '', event_string, self._get_target_outfile(label))