-
Notifications
You must be signed in to change notification settings - Fork 78
Add support for 'perf record and 'perf report' to devlib #382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
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?
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. 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
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. 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
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. 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
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. 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
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. I have seen error code 244 on a few occasions but haven't tracked it down. It has been working reliable for a while for me on the Android device I am using. I was guessing it was related to the
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. I've tested on a few Android devices and see error code 244 or just failures on some but not others. It seems to be consistent behavior on the devices, so a device either works or doesn't. The error message seems to be related to some check of the perf.data file and likely finding an corruption issue with perf.data. So I think this is a result of the approach WA uses for running (aka killing) perf and some platforms behavior writing to the perf.data file to storage when killed. Apart from a re-write of perf support in WA I think the only thing that could be done here is to try identify the behavior that causes the file corruption and see if we can override it from the device command prompt (e.g. setprop ....)
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. That's interesting, I've tried running this again with the latest version and no longer see this problem on my 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. I hadn't notice @marcbonnici had replied to this: could we try rebasing on #384 to see if the error message still appears?
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. I've tried #384 and sleep(10) and still seeing exit code 244.
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. Hmm... I'm wondering why I received this initially and haven't been able to reproduce it since.
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. Just in case, could you give #386 a try? |
||
| 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.