| Index: build/android/pylib/perf/perf_control.py
|
| diff --git a/build/android/pylib/perf/perf_control.py b/build/android/pylib/perf/perf_control.py
|
| index 92aaa2433012499b4b78ab5682b557fc69eae604..7952656f9e334df00dfccbf2ecb976d1e8bd6d53 100644
|
| --- a/build/android/pylib/perf/perf_control.py
|
| +++ b/build/android/pylib/perf/perf_control.py
|
| @@ -6,13 +6,24 @@
|
| import logging
|
| from pylib import android_commands
|
| from pylib.device import device_utils
|
| +import time
|
|
|
|
|
| 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')
|
| +
|
| _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
|
| + _CPU_DIR_FMT = '/sys/devices/system/cpu/cpu%d/'
|
| + _SCALING_GOVERNOR_FMT = _CPU_DIR_FMT + 'cpufreq/scaling_governor'
|
| + _CPU_ONLINE_FMT = _CPU_DIR_FMT + 'online'
|
| + _SCALING_FREQUENCY_STAT_NAMES = [
|
| + 'cpufreq/scaling_min_freq',
|
| + 'cpufreq/scaling_max_freq',
|
| + 'cpufreq/scaling_cur_freq',
|
| + ]
|
| + _MODELS_TO_FULLY_MANAGED_CPU_INDEXES = {
|
| + 'Nexus 4': [0, 1, 2, 3],
|
| + }
|
|
|
| def __init__(self, device):
|
| # TODO(jbudorick) Remove once telemetry gets switched over.
|
| @@ -28,33 +39,120 @@ class PerfControl(object):
|
| self._device.old_interface.GetFileContents(
|
| PerfControl._SCALING_GOVERNOR_FMT % 0,
|
| log_result=False)[0]
|
| + self._LogCurrentCpuFrequencyInfo()
|
| +
|
| + # Bring CPUs online/offline on some device types to reduce variance in
|
| + # performance measurements. Other device types are either hard to fully
|
| + # manage properly or we did not decide what settings look 'typical' for
|
| + # them. 'Galaxy Nexus' and 'Nexus 10' seem to use both cores all time
|
| + # without hotplugging them with PerfControl, but we still may need to manage
|
| + # their frequency later. TODO(pasko): manage Nexus 7 CPUs with PerfControl,
|
| + # v1 and v2 differ significantly.
|
| + #
|
| + # *Not fully managing* CPUs means that only a subset of CPU properties
|
| + # are updated (scaling governor only now) and only for those CPUs
|
| + # represented by sysfs at the moment (/sys/devices/system/cpu/cpuX/...)
|
| + self._fully_managed_cpus = (
|
| + self._MODELS_TO_FULLY_MANAGED_CPU_INDEXES.get(
|
| + self._device.old_interface.GetProductModel(), []))
|
| + if self._fully_managed_cpus:
|
| + self._cpus_on_originally = []
|
| + for cpu in xrange(self._kernel_max + 1):
|
| + if '1' in self._GetFileContents(
|
| + PerfControl._CPU_ONLINE_FMT % cpu):
|
| + self._cpus_on_originally.append(cpu)
|
| +
|
| + def _BringCpuOffline(self, cpu_index):
|
| + logging.info('Bringing CPU #%d offline.', cpu_index)
|
| + self._device.old_interface.SetFileContents(
|
| + PerfControl._CPU_ONLINE_FMT % cpu_index, '0')
|
| +
|
| + def _BringCpuOnline(self, cpu_index):
|
| + "Brings a CPU core online, waits for it to show up in sysfs."
|
| +
|
| + logging.info('Bringing CPU #%d online.' %cpu_index)
|
| + times_to_wait_left = 5
|
| + cpu_online_file = PerfControl._CPU_ONLINE_FMT % cpu_index
|
| + while (True):
|
| + self._device.old_interface.SetFileContents(cpu_online_file, '1')
|
| + if '1' in self._GetFileContents(cpu_online_file):
|
| + break
|
| + if times_to_wait_left == 0:
|
| + logging.warning('Gave up bringing CPU %d online', cpu_index)
|
| + break
|
| + times_to_wait_left -= 1
|
| + logging.warning('Could not bring CPU %d online, retrying..',
|
| + cpu_index)
|
| + time.sleep(0.1)
|
| +
|
| + def _GetFileContents(self, file_name):
|
| + return '\n'.join(self._device.old_interface.GetFileContents(file_name))
|
| +
|
| + def _LogCurrentCpuFrequencyInfo(self):
|
| + for cpu in xrange(self._kernel_max + 1):
|
| + for stat_name in PerfControl._SCALING_FREQUENCY_STAT_NAMES:
|
| + stat_file_name = (PerfControl._CPU_DIR_FMT + stat_name) % cpu
|
| + if self._device.old_interface.FileExistsOnDevice(stat_file_name):
|
| + info = self._GetFileContents(stat_file_name)
|
| + logging.info('CPU #%d frequency info: %s: %s', cpu, stat_name, info)
|
|
|
| def SetHighPerfMode(self):
|
| """Sets the highest possible performance mode for the device."""
|
| - self._SetScalingGovernorInternal('performance')
|
| +
|
| + if not self._fully_managed_cpus:
|
| + for cpu in xrange(self._kernel_max + 1):
|
| + self._SetScalingGovernorInternal(cpu, 'performance')
|
| + else:
|
| + # Stop the 'mpdecision' hotplug manager to get a constant number of CPUs
|
| + # online when running tests. TODO(pasko): stop 'thermald'.
|
| + self._device.old_interface.RunShellCommand('stop mpdecision')
|
| + for cpu in xrange(self._kernel_max + 1):
|
| + if cpu in self._fully_managed_cpus:
|
| + self._BringCpuOnline(cpu)
|
| + self._SetScalingGovernorInternal(cpu, 'performance')
|
| + else:
|
| + self._BringCpuOffline(cpu)
|
|
|
| def SetDefaultPerfMode(self):
|
| """Sets the performance mode for the device to its default mode."""
|
| + self._LogCurrentCpuFrequencyInfo()
|
| product_model = self._device.old_interface.GetProductModel()
|
| governor_mode = {
|
| 'GT-I9300': 'pegasusq',
|
| 'Galaxy Nexus': 'interactive',
|
| 'Nexus 4': 'ondemand',
|
| + 'Nexus 5': 'ondemand',
|
| 'Nexus 7': 'interactive',
|
| 'Nexus 10': 'interactive'
|
| }.get(product_model, 'ondemand')
|
| - self._SetScalingGovernorInternal(governor_mode)
|
| + if not self._fully_managed_cpus:
|
| + # Set the default perf mode only if no CPUs were forced online.
|
| + for cpu in xrange(self._kernel_max + 1):
|
| + self._SetScalingGovernorInternal(cpu, governor_mode)
|
| + # If 'mpdecision' is present on the system, it will manage CPU
|
| + # online/offline state, otherwise it is a no-op.
|
| + self._device.old_interface.RunShellCommand('start mpdecision')
|
|
|
| def RestoreOriginalPerfMode(self):
|
| """Resets the original performance mode of the device."""
|
| - self._SetScalingGovernorInternal(self._original_scaling_governor)
|
| -
|
| - def _SetScalingGovernorInternal(self, value):
|
| - for cpu in range(self._kernel_max + 1):
|
| - 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',
|
| - value, scaling_governor_file)
|
| - self._device.old_interface.SetProtectedFileContents(
|
| - scaling_governor_file, value)
|
| + self._LogCurrentCpuFrequencyInfo()
|
| + if not self._fully_managed_cpus:
|
| + for cpu in xrange(self._kernel_max + 1):
|
| + self._SetScalingGovernorInternal(cpu, self._original_scaling_governor)
|
| + else:
|
| + for cpu in xrange(self._kernel_max + 1):
|
| + if cpu in self._cpus_on_originally:
|
| + self._BringCpuOnline(cpu)
|
| + self._SetScalingGovernorInternal(cpu, self._original_scaling_governor)
|
| + else:
|
| + self._SetScalingGovernorInternal(cpu, self._original_scaling_governor)
|
| + self._BringCpuOffline(cpu)
|
| + self._device.old_interface.RunShellCommand('start mpdecision')
|
|
|
| + def _SetScalingGovernorInternal(self, cpu, value):
|
| + scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu
|
| + if (self._fully_managed_cpus or
|
| + self._device.old_interface.FileExistsOnDevice(scaling_governor_file)):
|
| + logging.info('Writing scaling governor mode "%s" -> %s',
|
| + value, scaling_governor_file)
|
| + self._device.old_interface.SetFileContents(scaling_governor_file, value)
|
|
|