diff options
author | Jian Cai <jiancai@google.com> | 2021-07-26 17:17:28 -0700 |
---|---|---|
committer | Jian Cai <jiancai@google.com> | 2021-07-27 17:35:16 +0000 |
commit | 05c8210cc7bbf8041e5f66cbfe134ca0667c3a76 (patch) | |
tree | a6c3ec66aef4f2149316d278fc709c2cf5abf609 /crosperf | |
parent | 8fe5e65c0f7b3711cbeb6326a79eaaf797a5ae9e (diff) | |
download | toolchain-utils-05c8210cc7bbf8041e5f66cbfe134ca0667c3a76.tar.gz |
crosperf: remove idle samples from crosperf report
Right now Crosperf reports include samples from idle functions.
Filtering these samples from the reports allow users to have more
accurate evaluation of the performance impact of code changes as they
focuson the meaningful samples.
BUG=b:194737325
TEST=unit tests passed.
Change-Id: I22bc3c78999eec9a141440e92ce52457a4bfa0a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/3054437
Reviewed-by: Manoj Gupta <manojgupta@chromium.org>
Tested-by: Jian Cai <jiancai@google.com>
Diffstat (limited to 'crosperf')
-rw-r--r-- | crosperf/results_cache.py | 75 | ||||
-rwxr-xr-x | crosperf/results_cache_unittest.py | 26 |
2 files changed, 89 insertions, 12 deletions
diff --git a/crosperf/results_cache.py b/crosperf/results_cache.py index c1cc8720..26da344a 100644 --- a/crosperf/results_cache.py +++ b/crosperf/results_cache.py @@ -273,10 +273,16 @@ class Result(object): return keyvals_dict def GetSamples(self): - samples = 0 + actual_samples = 0 for perf_data_file in self.perf_data_files: chroot_perf_data_file = misc.GetInsideChrootPath(self.chromeos_root, perf_data_file) + perf_report_file = '%s.report' % perf_data_file + if os.path.exists(perf_report_file): + raise RuntimeError('Perf report file already exists: %s' % + perf_report_file) + chroot_perf_report_file = misc.GetInsideChrootPath( + self.chromeos_root, perf_report_file) perf_path = os.path.join(self.chromeos_root, 'chroot', 'usr/bin/perf') perf_file = '/usr/sbin/perf' if os.path.exists(perf_path): @@ -304,17 +310,76 @@ class Result(object): # Each line looks like this: # 45.42% 237210 chrome # And we want the second number which is the sample count. - sample = 0 + samples = 0 try: for line in result.split('\n'): attr = line.split() if len(attr) == 3 and '%' in attr[0]: - sample += int(attr[1]) + samples += int(attr[1]) except: raise RuntimeError('Cannot parse perf dso result') - samples += sample - return [samples, u'samples'] + actual_samples += samples + + # Remove idle cycles from the accumulated sample count. + debug_path = self.label.debug_path + + if debug_path: + symfs = '--symfs ' + debug_path + vmlinux = '--vmlinux ' + os.path.join(debug_path, 'usr', 'lib', 'debug', + 'boot', 'vmlinux') + kallsyms = '' + print('** WARNING **: --kallsyms option not applied, no System.map-* ' + 'for downloaded image.') + else: + if self.label.image_type != 'local': + print('** WARNING **: Using local debug info in /build, this may ' + 'not match the downloaded image.') + build_path = os.path.join('/build', self.board) + symfs = self._CheckDebugPath('symfs', build_path) + vmlinux_path = os.path.join(build_path, 'usr/lib/debug/boot/vmlinux') + vmlinux = self._CheckDebugPath('vmlinux', vmlinux_path) + kallsyms_path = os.path.join(build_path, 'boot') + kallsyms = self._CheckDebugPath('kallsyms', kallsyms_path) + + command = ('%s report -n %s %s %s -i %s --stdio --quiet > %s' % + (perf_file, symfs, vmlinux, kallsyms, chroot_perf_data_file, + chroot_perf_report_file)) + if self.log_level != 'verbose': + self._logger.LogOutput('Generating perf report...\nCMD: %s' % command) + exit_code = self.ce.ChrootRunCommand(self.chromeos_root, command) + if exit_code == 0: + if self.log_level != 'verbose': + self._logger.LogOutput('Perf report generated successfully.') + else: + raise RuntimeError('Perf report not generated correctly. CMD: %s' % + command) + + idle_functions = { + '[kernel.kallsyms]': + ('intel_idle', 'arch_cpu_idle', 'intel_idle', 'cpu_startup_entry', + 'default_idle', 'cpu_idle_loop', 'do_idle'), + } + idle_samples = 0 + + with open(chroot_perf_report_file) as f: + try: + for line in f: + # Each line has the following fields, + # pylint: disable=line-too-long + # Overhead Samples Command Shared Object Symbol + # pylint: disable=line-too-long + # 1.48% 60 swapper [kernel.kallsyms] [k] intel_idle + (_, samples, _, dso, _, function) = line.strip().split() + if dso in idle_functions and function in idle_functions[dso]: + if self.log_level != 'verbose': + self._logger.LogOutput('Removing %s samples from %s in %s' % + (samples, function, dso)) + idle_samples += int(samples) + except: + raise RuntimeError('Cannot parse perf report') + actual_samples -= idle_samples + return [actual_samples, u'samples'] def GetResultsDir(self): if self.suite == 'tast': diff --git a/crosperf/results_cache_unittest.py b/crosperf/results_cache_unittest.py index 2eeea71c..9d95c5d9 100755 --- a/crosperf/results_cache_unittest.py +++ b/crosperf/results_cache_unittest.py @@ -9,6 +9,7 @@ from __future__ import print_function +import io import os import shutil import tempfile @@ -768,15 +769,26 @@ class ResultTest(unittest.TestCase): @mock.patch.object(misc, 'GetInsideChrootPath') @mock.patch.object(command_executer.CommandExecuter, 'ChrootRunCommandWOutput') - def test_get_samples(self, mock_chrootruncmd, mock_getpath): - fake_file = '/usr/chromeos/chroot/tmp/results/fake_file' + @mock.patch.object(command_executer.CommandExecuter, 'ChrootRunCommand') + def test_get_samples(self, mock_get_idle_samples, mock_get_total_samples, + mock_getpath): self.result.perf_data_files = ['/tmp/results/perf.data'] self.result.board = 'samus' - mock_getpath.return_value = fake_file - self.result.ce.ChrootRunCommandWOutput = mock_chrootruncmd - mock_chrootruncmd.return_value = ['', '45.42% 237210 chrome ', ''] - samples = self.result.GetSamples() - self.assertEqual(samples, [237210, u'samples']) + mock_getpath.return_value = '/usr/chromeos/chroot/tmp/results/perf.data' + mock_get_total_samples.return_value = [ + '', '45.42% 237210 chrome ', '' + ] + mock_get_idle_samples.return_value = 0 + + # mock_open does not seem to support iteration. + # pylint: disable=line-too-long + content = """1.63% 66 dav1d-tile chrome [.] decode_coefs + 1.48% 60 swapper [kernel.kallsyms] [k] intel_idle + 1.16% 47 dav1d-tile chrome [.] decode_sb""" + + with mock.patch('builtins.open', return_value=io.StringIO(content)): + samples = self.result.GetSamples() + self.assertEqual(samples, [237210 - 60, u'samples']) def test_get_results_dir(self): |