OLD | NEW |
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 | 5 |
6 import logging | 6 import logging |
7 from pylib import android_commands | 7 from pylib import android_commands |
8 from pylib.device import device_utils | 8 from pylib.device import device_utils |
| 9 import time |
9 | 10 |
10 | 11 |
11 class PerfControl(object): | 12 class PerfControl(object): |
12 """Provides methods for setting the performance mode of a device.""" | 13 """Provides methods for setting the performance mode of a device.""" |
13 _SCALING_GOVERNOR_FMT = ( | 14 |
14 '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor') | |
15 _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' | 15 _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' |
| 16 _CPU_DIR_FMT = '/sys/devices/system/cpu/cpu%d/' |
| 17 _SCALING_GOVERNOR_FMT = _CPU_DIR_FMT + 'cpufreq/scaling_governor' |
| 18 _CPU_ONLINE_FMT = _CPU_DIR_FMT + 'online' |
| 19 _SCALING_FREQUENCY_STAT_NAMES = [ |
| 20 'cpufreq/scaling_min_freq', |
| 21 'cpufreq/scaling_max_freq', |
| 22 'cpufreq/scaling_cur_freq', |
| 23 ] |
| 24 _MODELS_TO_FULLY_MANAGED_CPU_INDEXES = { |
| 25 'Nexus 4': [0, 1, 2, 3], |
| 26 } |
16 | 27 |
17 def __init__(self, device): | 28 def __init__(self, device): |
18 # TODO(jbudorick) Remove once telemetry gets switched over. | 29 # TODO(jbudorick) Remove once telemetry gets switched over. |
19 if isinstance(device, android_commands.AndroidCommands): | 30 if isinstance(device, android_commands.AndroidCommands): |
20 device = device_utils.DeviceUtils(device) | 31 device = device_utils.DeviceUtils(device) |
21 self._device = device | 32 self._device = device |
22 kernel_max = self._device.old_interface.GetFileContents( | 33 kernel_max = self._device.old_interface.GetFileContents( |
23 PerfControl._KERNEL_MAX, log_result=False) | 34 PerfControl._KERNEL_MAX, log_result=False) |
24 assert kernel_max, 'Unable to find %s' % PerfControl._KERNEL_MAX | 35 assert kernel_max, 'Unable to find %s' % PerfControl._KERNEL_MAX |
25 self._kernel_max = int(kernel_max[0]) | 36 self._kernel_max = int(kernel_max[0]) |
26 logging.info('Maximum CPU index: %d', self._kernel_max) | 37 logging.info('Maximum CPU index: %d', self._kernel_max) |
27 self._original_scaling_governor = \ | 38 self._original_scaling_governor = \ |
28 self._device.old_interface.GetFileContents( | 39 self._device.old_interface.GetFileContents( |
29 PerfControl._SCALING_GOVERNOR_FMT % 0, | 40 PerfControl._SCALING_GOVERNOR_FMT % 0, |
30 log_result=False)[0] | 41 log_result=False)[0] |
| 42 self._LogCurrentCpuFrequencyInfo() |
| 43 |
| 44 # Bring CPUs online/offline on some device types to reduce variance in |
| 45 # performance measurements. Other device types are either hard to fully |
| 46 # manage properly or we did not decide what settings look 'typical' for |
| 47 # them. 'Galaxy Nexus' and 'Nexus 10' seem to use both cores all time |
| 48 # without hotplugging them with PerfControl, but we still may need to manage |
| 49 # their frequency later. TODO(pasko): manage Nexus 7 CPUs with PerfControl, |
| 50 # v1 and v2 differ significantly. |
| 51 # |
| 52 # *Not fully managing* CPUs means that only a subset of CPU properties |
| 53 # are updated (scaling governor only now) and only for those CPUs |
| 54 # represented by sysfs at the moment (/sys/devices/system/cpu/cpuX/...) |
| 55 self._fully_managed_cpus = ( |
| 56 self._MODELS_TO_FULLY_MANAGED_CPU_INDEXES.get( |
| 57 self._device.old_interface.GetProductModel(), [])) |
| 58 if self._fully_managed_cpus: |
| 59 self._cpus_on_originally = [] |
| 60 for cpu in xrange(self._kernel_max + 1): |
| 61 if '1' in self._GetFileContents( |
| 62 PerfControl._CPU_ONLINE_FMT % cpu): |
| 63 self._cpus_on_originally.append(cpu) |
| 64 |
| 65 def _BringCpuOffline(self, cpu_index): |
| 66 logging.info('Bringing CPU #%d offline.', cpu_index) |
| 67 self._device.old_interface.SetFileContents( |
| 68 PerfControl._CPU_ONLINE_FMT % cpu_index, '0') |
| 69 |
| 70 def _BringCpuOnline(self, cpu_index): |
| 71 "Brings a CPU core online, waits for it to show up in sysfs." |
| 72 |
| 73 logging.info('Bringing CPU #%d online.' %cpu_index) |
| 74 times_to_wait_left = 5 |
| 75 cpu_online_file = PerfControl._CPU_ONLINE_FMT % cpu_index |
| 76 while (True): |
| 77 self._device.old_interface.SetFileContents(cpu_online_file, '1') |
| 78 if '1' in self._GetFileContents(cpu_online_file): |
| 79 break |
| 80 if times_to_wait_left == 0: |
| 81 logging.warning('Gave up bringing CPU %d online', cpu_index) |
| 82 break |
| 83 times_to_wait_left -= 1 |
| 84 logging.warning('Could not bring CPU %d online, retrying..', |
| 85 cpu_index) |
| 86 time.sleep(0.1) |
| 87 |
| 88 def _GetFileContents(self, file_name): |
| 89 return '\n'.join(self._device.old_interface.GetFileContents(file_name)) |
| 90 |
| 91 def _LogCurrentCpuFrequencyInfo(self): |
| 92 for cpu in xrange(self._kernel_max + 1): |
| 93 for stat_name in PerfControl._SCALING_FREQUENCY_STAT_NAMES: |
| 94 stat_file_name = (PerfControl._CPU_DIR_FMT + stat_name) % cpu |
| 95 if self._device.old_interface.FileExistsOnDevice(stat_file_name): |
| 96 info = self._GetFileContents(stat_file_name) |
| 97 logging.info('CPU #%d frequency info: %s: %s', cpu, stat_name, info) |
31 | 98 |
32 def SetHighPerfMode(self): | 99 def SetHighPerfMode(self): |
33 """Sets the highest possible performance mode for the device.""" | 100 """Sets the highest possible performance mode for the device.""" |
34 self._SetScalingGovernorInternal('performance') | 101 |
| 102 if not self._fully_managed_cpus: |
| 103 for cpu in xrange(self._kernel_max + 1): |
| 104 self._SetScalingGovernorInternal(cpu, 'performance') |
| 105 else: |
| 106 # Stop the 'mpdecision' hotplug manager to get a constant number of CPUs |
| 107 # online when running tests. TODO(pasko): stop 'thermald'. |
| 108 self._device.old_interface.RunShellCommand('stop mpdecision') |
| 109 for cpu in xrange(self._kernel_max + 1): |
| 110 if cpu in self._fully_managed_cpus: |
| 111 self._BringCpuOnline(cpu) |
| 112 self._SetScalingGovernorInternal(cpu, 'performance') |
| 113 else: |
| 114 self._BringCpuOffline(cpu) |
35 | 115 |
36 def SetDefaultPerfMode(self): | 116 def SetDefaultPerfMode(self): |
37 """Sets the performance mode for the device to its default mode.""" | 117 """Sets the performance mode for the device to its default mode.""" |
| 118 self._LogCurrentCpuFrequencyInfo() |
38 product_model = self._device.old_interface.GetProductModel() | 119 product_model = self._device.old_interface.GetProductModel() |
39 governor_mode = { | 120 governor_mode = { |
40 'GT-I9300': 'pegasusq', | 121 'GT-I9300': 'pegasusq', |
41 'Galaxy Nexus': 'interactive', | 122 'Galaxy Nexus': 'interactive', |
42 'Nexus 4': 'ondemand', | 123 'Nexus 4': 'ondemand', |
| 124 'Nexus 5': 'ondemand', |
43 'Nexus 7': 'interactive', | 125 'Nexus 7': 'interactive', |
44 'Nexus 10': 'interactive' | 126 'Nexus 10': 'interactive' |
45 }.get(product_model, 'ondemand') | 127 }.get(product_model, 'ondemand') |
46 self._SetScalingGovernorInternal(governor_mode) | 128 if not self._fully_managed_cpus: |
| 129 # Set the default perf mode only if no CPUs were forced online. |
| 130 for cpu in xrange(self._kernel_max + 1): |
| 131 self._SetScalingGovernorInternal(cpu, governor_mode) |
| 132 # If 'mpdecision' is present on the system, it will manage CPU |
| 133 # online/offline state, otherwise it is a no-op. |
| 134 self._device.old_interface.RunShellCommand('start mpdecision') |
47 | 135 |
48 def RestoreOriginalPerfMode(self): | 136 def RestoreOriginalPerfMode(self): |
49 """Resets the original performance mode of the device.""" | 137 """Resets the original performance mode of the device.""" |
50 self._SetScalingGovernorInternal(self._original_scaling_governor) | 138 self._LogCurrentCpuFrequencyInfo() |
| 139 if not self._fully_managed_cpus: |
| 140 for cpu in xrange(self._kernel_max + 1): |
| 141 self._SetScalingGovernorInternal(cpu, self._original_scaling_governor) |
| 142 else: |
| 143 for cpu in xrange(self._kernel_max + 1): |
| 144 if cpu in self._cpus_on_originally: |
| 145 self._BringCpuOnline(cpu) |
| 146 self._SetScalingGovernorInternal(cpu, self._original_scaling_governor) |
| 147 else: |
| 148 self._SetScalingGovernorInternal(cpu, self._original_scaling_governor) |
| 149 self._BringCpuOffline(cpu) |
| 150 self._device.old_interface.RunShellCommand('start mpdecision') |
51 | 151 |
52 def _SetScalingGovernorInternal(self, value): | 152 def _SetScalingGovernorInternal(self, cpu, value): |
53 for cpu in range(self._kernel_max + 1): | 153 scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu |
54 scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu | 154 if (self._fully_managed_cpus or |
55 if self._device.old_interface.FileExistsOnDevice(scaling_governor_file): | 155 self._device.old_interface.FileExistsOnDevice(scaling_governor_file)): |
56 logging.info('Writing scaling governor mode \'%s\' -> %s', | 156 logging.info('Writing scaling governor mode "%s" -> %s', |
57 value, scaling_governor_file) | 157 value, scaling_governor_file) |
58 self._device.old_interface.SetProtectedFileContents( | 158 self._device.old_interface.SetFileContents(scaling_governor_file, value) |
59 scaling_governor_file, value) | |
60 | |
OLD | NEW |