sysfs_power_monitor.py revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
1f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org# Copyright 2014 The Chromium Authors. All rights reserved. 2f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org# Use of this source code is governed by a BSD-style license that can be 3f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org# found in the LICENSE file. 4f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 5f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgimport collections 6f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgimport os 7f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgimport re 8f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 9f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgfrom telemetry import decorators 10f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgfrom telemetry.core.platform import power_monitor 11f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 12f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgCPU_PATH = '/sys/devices/system/cpu/' 13f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 14f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.orgclass SysfsPowerMonitor(power_monitor.PowerMonitor): 15f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """PowerMonitor that relies on sysfs to monitor CPU statistics on several 16f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org different platforms. 17f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """ 18f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org def __init__(self, platform): 19f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """Constructor. 20f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 21f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Args: 22f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org platform: A SysfsPlatform object. 23f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 24f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Attributes: 25f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org _browser: The browser to monitor. 26f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org _cpus: A list of the CPUs on the target device. 27f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org _end_time: The time the test stopped monitoring power. 28f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org _final_cstate: The c-state residency times after the test. 29f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org _final_freq: The CPU frequency times after the test. 30f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org _initial_cstate: The c-state residency times before the test. 31f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org _initial_freq: The CPU frequency times before the test. 32f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org _platform: A SysfsPlatform object associated with the target platform. 33f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org _start_time: The time the test started monitoring power. 34f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """ 35f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org super(SysfsPowerMonitor, self).__init__() 36f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._browser = None 37f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._cpus = filter(lambda x: re.match(r'^cpu[0-9]+', x), 38f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org platform.RunShellCommand('ls %s' % CPU_PATH).split()) 39f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._final_cstate = None 40f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._final_freq = None 41f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._initial_cstate = None 42f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._initial_freq = None 43f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._platform = platform 44f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 45f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org @decorators.Cache 46f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org def CanMonitorPower(self): 47f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org return bool(self._platform.RunShellCommand( 48f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 'if [ -e %s ]; then echo true; fi' % CPU_PATH)) 49f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 50f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org def StartMonitoringPower(self, browser): 51f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org assert not self._browser, 'Must call StopMonitoringPower().' 52f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._browser = browser 53f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org if self.CanMonitorPower(): 54f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._initial_freq = self.GetCpuFreq() 55f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._initial_cstate = self.GetCpuState() 56f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 57f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org def StopMonitoringPower(self): 58f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org assert self._browser, 'StartMonitoringPower() not called.' 59f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org try: 60f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org out = {} 61f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org if self.CanMonitorPower(): 62f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._final_freq = self.GetCpuFreq() 63f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._final_cstate = self.GetCpuState() 64f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org frequencies = SysfsPowerMonitor.ComputeCpuStats( 65f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org SysfsPowerMonitor.ParseFreqSample(self._initial_freq), 66f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org SysfsPowerMonitor.ParseFreqSample(self._final_freq)) 67f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org cstates = SysfsPowerMonitor.ComputeCpuStats( 68f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._platform.ParseStateSample(self._initial_cstate), 69f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._platform.ParseStateSample(self._final_cstate)) 70f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org for cpu in frequencies: 71f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org out[cpu] = {'frequency_percent': frequencies[cpu]} 72f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org out[cpu]['cstate_residency_percent'] = cstates[cpu] 73f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org return out 74f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org finally: 75f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org self._browser = None 76f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 77f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org def GetCpuState(self): 78f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """Retrieve CPU c-state residency times from the device. 79f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 80f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Returns: 81f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Dictionary containing c-state residency times for each CPU. 82f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """ 83f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org stats = {} 84f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org for cpu in self._cpus: 85f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org cpu_state_path = os.path.join(CPU_PATH, cpu, 'cpuidle/state*') 86f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org stats[cpu] = self._platform.RunShellCommand( 87f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 'cat %s %s %s; date +%%s' % (os.path.join(cpu_state_path, 'name'), 88f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org os.path.join(cpu_state_path, 'time'), 89f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org os.path.join(cpu_state_path, 'latency'))) 90f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org return stats 91f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 92f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org def GetCpuFreq(self): 93f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """Retrieve CPU frequency times from the device. 94f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 95f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Returns: 96f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Dictionary containing frequency times for each CPU. 97f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """ 98f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org stats = {} 99f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org for cpu in self._cpus: 100f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org cpu_freq_path = os.path.join( 101f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org CPU_PATH, cpu, 'cpufreq/stats/time_in_state') 102f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org stats[cpu] = self._platform.RunShellCommand('cat %s' % cpu_freq_path) 103f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org return stats 104f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 105f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org @staticmethod 106f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org def ParseFreqSample(sample): 107f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """Parse a single frequency sample. 108f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 109f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Args: 110f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org sample: The single sample of frequency data to be parsed. 111f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 112f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Returns: 113f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org A dictionary associating a frequency with a time. 114f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """ 115f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org sample_stats = {} 116f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org for cpu in sample: 117f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org frequencies = {} 118f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org for line in sample[cpu].splitlines(): 119f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org pair = line.split() 120f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org freq = int(pair[0]) * 10 ** 3 121f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org timeunits = int(pair[1]) 122f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org if freq in frequencies: 123f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org frequencies[freq] += timeunits 124f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org else: 125f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org frequencies[freq] = timeunits 126f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org sample_stats[cpu] = frequencies 127f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org return sample_stats 128f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 129f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org @staticmethod 130f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org def ComputeCpuStats(initial, final): 131f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """Parse the CPU c-state and frequency values saved during monitoring. 132f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 133f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Args: 134f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org initial: The parsed dictionary of initial statistics to be converted 135f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org into percentages. 136f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org final: The parsed dictionary of final statistics to be converted 137f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org into percentages. 138f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org 139f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Returns: 140f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org Dictionary containing percentages for each CPU as well as an average 141f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org across all CPUs. 142f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org """ 143f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org cpu_stats = {} 144f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org # Each core might have different states or frequencies, so keep track of 145f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org # the total time in a state or frequency and how many cores report a time. 146f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org cumulative_times = collections.defaultdict(lambda: (0, 0)) 147f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org for cpu in initial: 148f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org current_cpu = {} 149f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org total = 0 150f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org for state in initial[cpu]: 151f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org current_cpu[state] = final[cpu][state] - initial[cpu][state] 152f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org total += current_cpu[state] 153f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org for state in current_cpu: 154f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org current_cpu[state] /= (float(total) / 100.0) 155f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org # Calculate the average c-state residency across all CPUs. 156f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org time, count = cumulative_times[state] 157f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org cumulative_times[state] = (time + current_cpu[state], count + 1) 158f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org cpu_stats[cpu] = current_cpu 159f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org average = {} 160f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org for state in cumulative_times: 161f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org time, count = cumulative_times[state] 162f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org average[state] = time / float(count) 163f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org cpu_stats['whole_package'] = average 164f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org return cpu_stats 165f2ba7591b1407a7ee9209f842c50696914dc2dedkbr@chromium.org