aboutsummaryrefslogtreecommitdiff
path: root/crosperf
diff options
context:
space:
mode:
authorJian Cai <jiancai@google.com>2021-07-26 17:17:28 -0700
committerJian Cai <jiancai@google.com>2021-07-27 17:35:16 +0000
commit05c8210cc7bbf8041e5f66cbfe134ca0667c3a76 (patch)
treea6c3ec66aef4f2149316d278fc709c2cf5abf609 /crosperf
parent8fe5e65c0f7b3711cbeb6326a79eaaf797a5ae9e (diff)
downloadtoolchain-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.py75
-rwxr-xr-xcrosperf/results_cache_unittest.py26
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):