diff options
5 files changed, 67 insertions, 59 deletions
diff --git a/build/android/pylib/gtest/test_runner.py b/build/android/pylib/gtest/test_runner.py index dd09488..cea7992 100644 --- a/build/android/pylib/gtest/test_runner.py +++ b/build/android/pylib/gtest/test_runner.py @@ -192,7 +192,7 @@ class TestRunner(base_test_runner.BaseTestRunner): def TearDown(self): """Cleans up the test enviroment for the test suite.""" if _TestSuiteRequiresHighPerfMode(self.test_package.suite_name): - self._perf_controller.RestoreOriginalPerfMode() + self._perf_controller.SetDefaultPerfMode() self.test_package.ClearApplicationState(self.device) self.tool.CleanUpEnvironment() super(TestRunner, self).TearDown() diff --git a/build/android/pylib/perf/perf_control.py b/build/android/pylib/perf/perf_control.py index 1c88945..e13c02a 100644 --- a/build/android/pylib/perf/perf_control.py +++ b/build/android/pylib/perf/perf_control.py @@ -13,6 +13,7 @@ class PerfControl(object): """Provides methods for setting the performance mode of a device.""" _SCALING_GOVERNOR_FMT = ( '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor') + _CPU_ONLINE_FMT = '/sys/devices/system/cpu/cpu%d/online' _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' def __init__(self, device): @@ -25,13 +26,22 @@ class PerfControl(object): assert kernel_max, 'Unable to find %s' % PerfControl._KERNEL_MAX self._kernel_max = int(kernel_max[0]) logging.info('Maximum CPU index: %d', self._kernel_max) - self._original_scaling_governor = \ - self._device.old_interface.GetFileContents( - PerfControl._SCALING_GOVERNOR_FMT % 0, - log_result=False)[0] + self._have_mpdecision = self._device.old_interface.FileExistsOnDevice( + '/system/bin/mpdecision') + + @property + def _NumCpuCores(self): + return self._kernel_max + 1 def SetHighPerfMode(self): + # TODO(epenner): Enable on all devices (http://crbug.com/383566) + if 'Nexus 4' == self._device.old_interface.GetProductModel(): + self._ForceAllCpusOnline(True) + self._SetScalingGovernorInternal('performance') + + def SetPerfProfilingMode(self): """Sets the highest possible performance mode for the device.""" + self._ForceAllCpusOnline(True) self._SetScalingGovernorInternal('performance') def SetDefaultPerfMode(self): @@ -45,13 +55,10 @@ class PerfControl(object): 'Nexus 10': 'interactive' }.get(product_model, 'ondemand') self._SetScalingGovernorInternal(governor_mode) - - def RestoreOriginalPerfMode(self): - """Resets the original performance mode of the device.""" - self._SetScalingGovernorInternal(self._original_scaling_governor) + self._ForceAllCpusOnline(False) def _SetScalingGovernorInternal(self, value): - for cpu in range(self._kernel_max + 1): + for cpu in range(self._NumCpuCores): scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu if self._device.old_interface.FileExistsOnDevice(scaling_governor_file): logging.info('Writing scaling governor mode \'%s\' -> %s', @@ -59,37 +66,43 @@ class PerfControl(object): self._device.old_interface.SetProtectedFileContents( scaling_governor_file, value) - def ForceAllCpusOnline(self, force_online): - """Force all CPUs on a device to be online. + def _AllCpusAreOnline(self): + for cpu in range(self._NumCpuCores): + online_path = PerfControl._CPU_ONLINE_FMT % cpu + if self._device.old_interface.GetFileContents(online_path)[0] == '0': + return False + return True + + def _ForceAllCpusOnline(self, force_online): + """Enable all CPUs on a device. - Force every CPU core on an Android device to remain online, or return the - cores under system power management control. This is needed to work around - a bug in perf which makes it unable to record samples from CPUs that become - online when recording is already underway. + Some vendors (or only Qualcomm?) hot-plug their CPUs, which can add noise + to measurements: + - In perf, samples are only taken for the CPUs that are online when the + measurement is started. + - The scaling governor can't be set for an offline CPU and frequency scaling + on newly enabled CPUs adds noise to both perf and tracing measurements. + + It appears Qualcomm is the only vendor that hot-plugs CPUs, and on Qualcomm + this is done by "mpdecision". - Args: - force_online: True to set all CPUs online, False to return them under - system power management control. """ - def ForceCpuOnline(online_path): - script = 'chmod 644 {0}; echo 1 > {0}; chmod 444 {0}'.format(online_path) + if self._have_mpdecision: + script = 'stop mpdecision' if force_online else 'start mpdecision' self._device.RunShellCommand(script, root=True) - return self._device.old_interface.GetFileContents(online_path)[0] == '1' - - def ResetCpu(online_path): - self._device.RunShellCommand('chmod 644 %s' % online_path, root=True) - - def WaitFor(condition): - for _ in range(100): - if condition(): - return - time.sleep(0.1) - raise RuntimeError('Timed out') - - cpu_online_files = self._device.RunShellCommand( - 'ls -d /sys/devices/system/cpu/cpu[0-9]*/online') - for online_path in cpu_online_files: - if force_online: - WaitFor(lambda: ForceCpuOnline(online_path)) - else: - ResetCpu(online_path) + + if not self._have_mpdecision and not self._AllCpusAreOnline(): + logging.warning('Unexpected cpu hot plugging detected.') + + if not force_online: + return + + for cpu in range(self._NumCpuCores): + online_path = PerfControl._CPU_ONLINE_FMT % cpu + self._device.old_interface.SetProtectedFileContents( + online_path, '1') + + # Double check all cores stayed online. + time.sleep(0.25) + if not self._AllCpusAreOnline(): + raise RuntimeError('Failed to force CPUs online') diff --git a/build/android/pylib/perf/perf_control_unittest.py b/build/android/pylib/perf/perf_control_unittest.py index a83b482..7bdc962 100644 --- a/build/android/pylib/perf/perf_control_unittest.py +++ b/build/android/pylib/perf/perf_control_unittest.py @@ -1,6 +1,7 @@ # Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# pylint: disable=W0212 import os import sys @@ -12,7 +13,6 @@ from pylib import android_commands from pylib.device import device_utils from pylib.perf import perf_control - class TestPerfControl(unittest.TestCase): def setUp(self): if not os.getenv('BUILDTYPE'): @@ -23,24 +23,19 @@ class TestPerfControl(unittest.TestCase): self._device = device_utils.DeviceUtils( android_commands.AndroidCommands(device=devices[0])) - def testForceAllCpusOnline(self): + def testHighPerfMode(self): perf = perf_control.PerfControl(self._device) - cpu_online_files = self._device.RunShellCommand( - 'ls -d /sys/devices/system/cpu/cpu[0-9]*/online') try: - perf.ForceAllCpusOnline(True) - for path in cpu_online_files: + perf.SetPerfProfilingMode() + for cpu in range(perf._NumCpuCores): + path = perf_control.PerfControl._CPU_ONLINE_FMT % cpu self.assertEquals('1', self._device.old_interface.GetFileContents(path)[0]) - mode = self._device.RunShellCommand('ls -l %s' % path)[0] - self.assertEquals('-r--r--r--', mode[:10]) + path = perf_control.PerfControl._SCALING_GOVERNOR_FMT % cpu + self.assertEquals('performance', + self._device.old_interface.GetFileContents(path)[0]) finally: - perf.ForceAllCpusOnline(False) - - for path in cpu_online_files: - mode = self._device.RunShellCommand('ls -l %s' % path)[0] - self.assertEquals('-rw-r--r--', mode[:10]) - + perf.SetDefaultPerfMode() if __name__ == '__main__': unittest.main() diff --git a/tools/android/adb_profile_chrome/perf_controller.py b/tools/android/adb_profile_chrome/perf_controller.py index 523fb85..aeb29fd 100644 --- a/tools/android/adb_profile_chrome/perf_controller.py +++ b/tools/android/adb_profile_chrome/perf_controller.py @@ -58,7 +58,7 @@ class _PerfProfiler(object): if categories: cmd += ['--event', ','.join(categories)] self._perf_control = perf_control.PerfControl(self._device) - self._perf_control.ForceAllCpusOnline(True) + self._perf_control.SetPerfProfilingMode() self._perf_process = subprocess.Popen(cmd, stdout=self._log_file, stderr=subprocess.STDOUT) @@ -67,7 +67,7 @@ class _PerfProfiler(object): perf_pids = self._device.old_interface.ExtractPid('perf') self._device.RunShellCommand('kill -SIGINT ' + ' '.join(perf_pids)) self._perf_process.wait() - self._perf_control.ForceAllCpusOnline(False) + self._perf_control.SetDefaultPerfMode() def _FailWithLog(self, msg): self._log_file.seek(0) diff --git a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py index 6b460d9..139cd37 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py @@ -172,7 +172,7 @@ class PerfProfiler(profiler.Profiler): device = browser_backend.adb.device() perf_binary = android_profiling_helper.PrepareDeviceForPerf(device) self._perf_control = perf_control.PerfControl(device) - self._perf_control.ForceAllCpusOnline(True) + self._perf_control.SetPerfProfilingMode() else: _PrepareHostForPerf() @@ -185,7 +185,7 @@ class PerfProfiler(profiler.Profiler): perf_binary, perfhost_binary)) except: if self._is_android: - self._perf_control.ForceAllCpusOnline(False) + self._perf_control.SetDefaultPerfMode() raise @classmethod @@ -209,7 +209,7 @@ class PerfProfiler(profiler.Profiler): def CollectProfile(self): if self._is_android: - self._perf_control.ForceAllCpusOnline(False) + self._perf_control.SetDefaultPerfMode() output_files = [] for single_process in self._process_profilers: output_files.append(single_process.CollectProfile()) |