Commit b51cfab2 authored by epenner@chromium.org's avatar epenner@chromium.org

Telemetry: Set scaling governor on all cpus (on all devices)

We try to set the scaling governor for all CPU cores, but
this doesn't work SOCs that dynamically disable cores
entirely (ie. Qualcomm). Since this only happens on Qcomm,
this patch stops the 'mpdecision' process on Qualcomm, which
makes it behave like other devices.

PERF-SHERIFFS: This may improve metrics for the better given
all cores are in 'performance' mode now. It should hopefully
reduce noise, since a different number of cores might have
had 'performance' set on each run, and any code that runs on
a non-performance core will suffer from noisy timings/rates.

Example: Tough-compositor-cases std-dev went from 0.25ms to
0.04ms with this change.

BUG=383566

Review URL: https://codereview.chromium.org/338233003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278837 0039d316-1c4b-4281-b951-d872f2087c98
parent d6a845e8
...@@ -192,7 +192,7 @@ class TestRunner(base_test_runner.BaseTestRunner): ...@@ -192,7 +192,7 @@ class TestRunner(base_test_runner.BaseTestRunner):
def TearDown(self): def TearDown(self):
"""Cleans up the test enviroment for the test suite.""" """Cleans up the test enviroment for the test suite."""
if _TestSuiteRequiresHighPerfMode(self.test_package.suite_name): if _TestSuiteRequiresHighPerfMode(self.test_package.suite_name):
self._perf_controller.RestoreOriginalPerfMode() self._perf_controller.SetDefaultPerfMode()
self.test_package.ClearApplicationState(self.device) self.test_package.ClearApplicationState(self.device)
self.tool.CleanUpEnvironment() self.tool.CleanUpEnvironment()
super(TestRunner, self).TearDown() super(TestRunner, self).TearDown()
...@@ -13,6 +13,7 @@ class PerfControl(object): ...@@ -13,6 +13,7 @@ class PerfControl(object):
"""Provides methods for setting the performance mode of a device.""" """Provides methods for setting the performance mode of a device."""
_SCALING_GOVERNOR_FMT = ( _SCALING_GOVERNOR_FMT = (
'/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor') '/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' _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
def __init__(self, device): def __init__(self, device):
...@@ -25,13 +26,22 @@ class PerfControl(object): ...@@ -25,13 +26,22 @@ class PerfControl(object):
assert kernel_max, 'Unable to find %s' % PerfControl._KERNEL_MAX assert kernel_max, 'Unable to find %s' % PerfControl._KERNEL_MAX
self._kernel_max = int(kernel_max[0]) self._kernel_max = int(kernel_max[0])
logging.info('Maximum CPU index: %d', self._kernel_max) logging.info('Maximum CPU index: %d', self._kernel_max)
self._original_scaling_governor = \ self._have_mpdecision = self._device.old_interface.FileExistsOnDevice(
self._device.old_interface.GetFileContents( '/system/bin/mpdecision')
PerfControl._SCALING_GOVERNOR_FMT % 0,
log_result=False)[0] @property
def _NumCpuCores(self):
return self._kernel_max + 1
def SetHighPerfMode(self): 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.""" """Sets the highest possible performance mode for the device."""
self._ForceAllCpusOnline(True)
self._SetScalingGovernorInternal('performance') self._SetScalingGovernorInternal('performance')
def SetDefaultPerfMode(self): def SetDefaultPerfMode(self):
...@@ -45,13 +55,10 @@ class PerfControl(object): ...@@ -45,13 +55,10 @@ class PerfControl(object):
'Nexus 10': 'interactive' 'Nexus 10': 'interactive'
}.get(product_model, 'ondemand') }.get(product_model, 'ondemand')
self._SetScalingGovernorInternal(governor_mode) self._SetScalingGovernorInternal(governor_mode)
self._ForceAllCpusOnline(False)
def RestoreOriginalPerfMode(self):
"""Resets the original performance mode of the device."""
self._SetScalingGovernorInternal(self._original_scaling_governor)
def _SetScalingGovernorInternal(self, value): 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 scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu
if self._device.old_interface.FileExistsOnDevice(scaling_governor_file): if self._device.old_interface.FileExistsOnDevice(scaling_governor_file):
logging.info('Writing scaling governor mode \'%s\' -> %s', logging.info('Writing scaling governor mode \'%s\' -> %s',
...@@ -59,37 +66,43 @@ class PerfControl(object): ...@@ -59,37 +66,43 @@ class PerfControl(object):
self._device.old_interface.SetProtectedFileContents( self._device.old_interface.SetProtectedFileContents(
scaling_governor_file, value) scaling_governor_file, value)
def ForceAllCpusOnline(self, force_online): def _AllCpusAreOnline(self):
"""Force all CPUs on a device to be online. 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 Some vendors (or only Qualcomm?) hot-plug their CPUs, which can add noise
cores under system power management control. This is needed to work around to measurements:
a bug in perf which makes it unable to record samples from CPUs that become - In perf, samples are only taken for the CPUs that are online when the
online when recording is already underway. 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): if self._have_mpdecision:
script = 'chmod 644 {0}; echo 1 > {0}; chmod 444 {0}'.format(online_path) script = 'stop mpdecision' if force_online else 'start mpdecision'
self._device.RunShellCommand(script, root=True) self._device.RunShellCommand(script, root=True)
return self._device.old_interface.GetFileContents(online_path)[0] == '1'
if not self._have_mpdecision and not self._AllCpusAreOnline():
def ResetCpu(online_path): logging.warning('Unexpected cpu hot plugging detected.')
self._device.RunShellCommand('chmod 644 %s' % online_path, root=True)
if not force_online:
def WaitFor(condition): return
for _ in range(100):
if condition(): for cpu in range(self._NumCpuCores):
return online_path = PerfControl._CPU_ONLINE_FMT % cpu
time.sleep(0.1) self._device.old_interface.SetProtectedFileContents(
raise RuntimeError('Timed out') online_path, '1')
cpu_online_files = self._device.RunShellCommand( # Double check all cores stayed online.
'ls -d /sys/devices/system/cpu/cpu[0-9]*/online') time.sleep(0.25)
for online_path in cpu_online_files: if not self._AllCpusAreOnline():
if force_online: raise RuntimeError('Failed to force CPUs online')
WaitFor(lambda: ForceCpuOnline(online_path))
else:
ResetCpu(online_path)
# Copyright 2014 The Chromium Authors. All rights reserved. # Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
# pylint: disable=W0212
import os import os
import sys import sys
...@@ -12,7 +13,6 @@ from pylib import android_commands ...@@ -12,7 +13,6 @@ from pylib import android_commands
from pylib.device import device_utils from pylib.device import device_utils
from pylib.perf import perf_control from pylib.perf import perf_control
class TestPerfControl(unittest.TestCase): class TestPerfControl(unittest.TestCase):
def setUp(self): def setUp(self):
if not os.getenv('BUILDTYPE'): if not os.getenv('BUILDTYPE'):
...@@ -23,24 +23,19 @@ class TestPerfControl(unittest.TestCase): ...@@ -23,24 +23,19 @@ class TestPerfControl(unittest.TestCase):
self._device = device_utils.DeviceUtils( self._device = device_utils.DeviceUtils(
android_commands.AndroidCommands(device=devices[0])) android_commands.AndroidCommands(device=devices[0]))
def testForceAllCpusOnline(self): def testHighPerfMode(self):
perf = perf_control.PerfControl(self._device) perf = perf_control.PerfControl(self._device)
cpu_online_files = self._device.RunShellCommand(
'ls -d /sys/devices/system/cpu/cpu[0-9]*/online')
try: try:
perf.ForceAllCpusOnline(True) perf.SetPerfProfilingMode()
for path in cpu_online_files: for cpu in range(perf._NumCpuCores):
path = perf_control.PerfControl._CPU_ONLINE_FMT % cpu
self.assertEquals('1', self.assertEquals('1',
self._device.old_interface.GetFileContents(path)[0]) self._device.old_interface.GetFileContents(path)[0])
mode = self._device.RunShellCommand('ls -l %s' % path)[0] path = perf_control.PerfControl._SCALING_GOVERNOR_FMT % cpu
self.assertEquals('-r--r--r--', mode[:10]) self.assertEquals('performance',
self._device.old_interface.GetFileContents(path)[0])
finally: finally:
perf.ForceAllCpusOnline(False) perf.SetDefaultPerfMode()
for path in cpu_online_files:
mode = self._device.RunShellCommand('ls -l %s' % path)[0]
self.assertEquals('-rw-r--r--', mode[:10])
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -58,7 +58,7 @@ class _PerfProfiler(object): ...@@ -58,7 +58,7 @@ class _PerfProfiler(object):
if categories: if categories:
cmd += ['--event', ','.join(categories)] cmd += ['--event', ','.join(categories)]
self._perf_control = perf_control.PerfControl(self._device) self._perf_control = perf_control.PerfControl(self._device)
self._perf_control.ForceAllCpusOnline(True) self._perf_control.SetPerfProfilingMode()
self._perf_process = subprocess.Popen(cmd, self._perf_process = subprocess.Popen(cmd,
stdout=self._log_file, stdout=self._log_file,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
...@@ -67,7 +67,7 @@ class _PerfProfiler(object): ...@@ -67,7 +67,7 @@ class _PerfProfiler(object):
perf_pids = self._device.old_interface.ExtractPid('perf') perf_pids = self._device.old_interface.ExtractPid('perf')
self._device.RunShellCommand('kill -SIGINT ' + ' '.join(perf_pids)) self._device.RunShellCommand('kill -SIGINT ' + ' '.join(perf_pids))
self._perf_process.wait() self._perf_process.wait()
self._perf_control.ForceAllCpusOnline(False) self._perf_control.SetDefaultPerfMode()
def _FailWithLog(self, msg): def _FailWithLog(self, msg):
self._log_file.seek(0) self._log_file.seek(0)
......
...@@ -172,7 +172,7 @@ class PerfProfiler(profiler.Profiler): ...@@ -172,7 +172,7 @@ class PerfProfiler(profiler.Profiler):
device = browser_backend.adb.device() device = browser_backend.adb.device()
perf_binary = android_profiling_helper.PrepareDeviceForPerf(device) perf_binary = android_profiling_helper.PrepareDeviceForPerf(device)
self._perf_control = perf_control.PerfControl(device) self._perf_control = perf_control.PerfControl(device)
self._perf_control.ForceAllCpusOnline(True) self._perf_control.SetPerfProfilingMode()
else: else:
_PrepareHostForPerf() _PrepareHostForPerf()
...@@ -185,7 +185,7 @@ class PerfProfiler(profiler.Profiler): ...@@ -185,7 +185,7 @@ class PerfProfiler(profiler.Profiler):
perf_binary, perfhost_binary)) perf_binary, perfhost_binary))
except: except:
if self._is_android: if self._is_android:
self._perf_control.ForceAllCpusOnline(False) self._perf_control.SetDefaultPerfMode()
raise raise
@classmethod @classmethod
...@@ -209,7 +209,7 @@ class PerfProfiler(profiler.Profiler): ...@@ -209,7 +209,7 @@ class PerfProfiler(profiler.Profiler):
def CollectProfile(self): def CollectProfile(self):
if self._is_android: if self._is_android:
self._perf_control.ForceAllCpusOnline(False) self._perf_control.SetDefaultPerfMode()
output_files = [] output_files = []
for single_process in self._process_profilers: for single_process in self._process_profilers:
output_files.append(single_process.CollectProfile()) output_files.append(single_process.CollectProfile())
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment