Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 77
Add support for 'perf record and 'perf report' to devlib#382
base:master
Are you sure you want to change the base?
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -23,7 +23,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 ' | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @setrofim & @marcbonnici : This might be a standard approach in If we don't want that, isn't Second question (tiny detail, out of curiosity), what is the final space in the string for? Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Basically, this is to save having to pull two files instead of one. Given that the output is generally parsed with a line parser looking for specific patterns, mixing in
The
Erm. Aesthetics? A subtle reference to the TBS animated show? </joke> Seriously, I have no idea. It might be a hold-over form an earlier WA2 implementation where it might have mattered. But I don't see it serving a purpose in the current code. Well spotted. | ||
| PERF_REPORT_COMMAND_TEMPLATE = '{} report {} -i {} > {} 2>&1 ' | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Couldn't we modify | ||
| PERF_COUNT_REGEX = re.compile(r'^(CPU\d+)?\s*(\d+)\s*(.*?)\s*(\[\s*\d+\.\d+%\s*\])?\s*$') | ||
| @@ -69,6 +70,8 @@ def __init__(self, target, | ||
| events=None, | ||
| optionstring=None, | ||
| labels=None, | ||
| mode='stat', | ||
| report_optionstring=None, | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @setrofim & @marcbonnici : could we consider extending Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you elaborate on what you mean (maybe raise a separate issue, if inappropriate for this PR)? | ||
| force_install=False): | ||
| super(PerfCollector, self).__init__(target) | ||
| self.events = events if events else DEFAULT_EVENTS | ||
| @@ -82,10 +85,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 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: | ||
| @@ -98,6 +111,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) | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We remove a report file event if we're not going to call | ||
| def start(self): | ||
| for command in self.commands: | ||
| @@ -108,7 +123,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): | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overall, as a reader that hasn't written the code, I find the fact that Going one step further, I notice that Another way to do this, would be to implement Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those are all excellent points. The "trace" interface hasn't been particularly well thought out and probably needs some serious though put into redesigning it. (And as has been pointed out elsewhere, "trace" is not a good name for what is being handled by this interface at the moment (see #378.) This a much bigger issue that this PR, and I don't think it should block it. There is already precedent for directories being passed to Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have raised an issue to track this: #383. If you have any suggestions for changes to the collector API, please feel free to add them to the issue. | ||
| #in record mode generate report and copy | ||
| if self.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)) | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you seem to be doing this process of:
Could we consider instead having a | ||
| self.target.pull(target_file, host_file) | ||
| # .trace | ||
| target_file = self._get_target_tracefile(label) | ||
| host_relpath = os.path.basename(target_file) | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like a cumbersome way of getting | ||
| host_file = _f(os.path.join(outdir, host_relpath)) | ||
| target_file = self._get_target_perfdatafile() | ||
| self.target.pull(target_file, host_file) | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The previous 5 lines seem to be pulling Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be the cause of the permission errors: Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW, what happens to the file pointed to by Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The file never exists on the device because a mixture of perf behavior and how it is executed by WA. WA executes perf to run sleep and then kills it after a certain time. This seems to break the perf -o option and perf just outputs to a default place/file name ({current directory}/perf.data). So the -o option isn't used to run perf record and we do a rename of perf.data. Seemed easiest just to do it as part of the copy rather than directly on the device. Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Interesting, I didn't know that. It's probably because | ||
| # .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 +162,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.mode, | ||
| options or '', | ||
| event_string, | ||
| self._get_target_outfile(label)) | ||
| return command | ||
| def _get_target_perfdatafile(self): | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these functions really necessary? | ||
| 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(), | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When testing this on my linux device the Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On Android it seems to include a I'd assume that it is the same on Linux but haven't confirmed. I can look at running on Linux target but will take me a little time. Do you happen to have the run.log file from your testing to confirm the command used on Linux? Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes I see the same behaviour on my android device however on linux it produces: For some reason the | ||
| self._get_target_reportfile(label)) | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And when testing this on my android device the fails with error code 244 caused by
| ||
| return command | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could take the opportunity here to introduce named replacement format fields:
allowing more readable and reliable calls to
PERF_COMMAND_TEMPLATE.format()as they don't use position-dependant inputs.