15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved. 25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# found in the LICENSE file. 45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import collections 61320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport logging 75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import os 85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import re 95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry import decorators 115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.core.platform import power_monitor 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)CPU_PATH = '/sys/devices/system/cpu/' 155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class SysfsPowerMonitor(power_monitor.PowerMonitor): 185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """PowerMonitor that relies on sysfs to monitor CPU statistics on several 195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) different platforms. 205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def __init__(self, linux_based_platform_backend): 225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Constructor. 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Args: 251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci linux_based_platform_backend: A LinuxBasedPlatformBackend object. 265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Attributes: 285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) _browser: The browser to monitor. 295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) _cpus: A list of the CPUs on the target device. 305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) _end_time: The time the test stopped monitoring power. 315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) _final_cstate: The c-state residency times after the test. 325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) _final_freq: The CPU frequency times after the test. 335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) _initial_cstate: The c-state residency times before the test. 345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) _initial_freq: The CPU frequency times before the test. 351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci _platform: A LinuxBasedPlatformBackend object associated with the 361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci target platform. 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) _start_time: The time the test started monitoring power. 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) super(SysfsPowerMonitor, self).__init__() 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._browser = None 4103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) self._cpus = None 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._final_cstate = None 435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._final_freq = None 445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._initial_cstate = None 455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._initial_freq = None 461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._platform = linux_based_platform_backend 475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) @decorators.Cache 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def CanMonitorPower(self): 501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return bool(self._platform.RunCommand( 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'if [ -e %s ]; then echo true; fi' % CPU_PATH)) 525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def StartMonitoringPower(self, browser): 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) assert not self._browser, 'Must call StopMonitoringPower().' 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._browser = browser 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if self.CanMonitorPower(): 5703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) self._cpus = filter( 5803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) lambda x: re.match(r'^cpu[0-9]+', x), 591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._platform.RunCommand('ls %s' % CPU_PATH).split()) 605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._initial_freq = self.GetCpuFreq() 615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._initial_cstate = self.GetCpuState() 625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def StopMonitoringPower(self): 645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) assert self._browser, 'StartMonitoringPower() not called.' 655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) try: 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) out = {} 6703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if SysfsPowerMonitor.CanMonitorPower(self): 685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._final_freq = self.GetCpuFreq() 695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._final_cstate = self.GetCpuState() 705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) frequencies = SysfsPowerMonitor.ComputeCpuStats( 715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) SysfsPowerMonitor.ParseFreqSample(self._initial_freq), 725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) SysfsPowerMonitor.ParseFreqSample(self._final_freq)) 736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) cstates = SysfsPowerMonitor.ComputeCpuStats( 741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._platform.ParseCStateSample(self._initial_cstate), 751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._platform.ParseCStateSample(self._final_cstate)) 765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for cpu in frequencies: 775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) out[cpu] = {'frequency_percent': frequencies[cpu]} 785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) out[cpu]['cstate_residency_percent'] = cstates[cpu] 795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return out 805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) finally: 815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._browser = None 825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def GetCpuState(self): 845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Retrieve CPU c-state residency times from the device. 855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Returns: 875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Dictionary containing c-state residency times for each CPU. 885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) stats = {} 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for cpu in self._cpus: 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) cpu_state_path = os.path.join(CPU_PATH, cpu, 'cpuidle/state*') 921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci stats[cpu] = self._platform.RunCommand( 936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 'cat %s %s %s; date +%%s' % (os.path.join(cpu_state_path, 'name'), 945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) os.path.join(cpu_state_path, 'time'), 955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) os.path.join(cpu_state_path, 'latency'))) 965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return stats 975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def GetCpuFreq(self): 995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Retrieve CPU frequency times from the device. 1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Returns: 1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Dictionary containing frequency times for each CPU. 1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) stats = {} 1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for cpu in self._cpus: 1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) cpu_freq_path = os.path.join( 1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) CPU_PATH, cpu, 'cpufreq/stats/time_in_state') 1081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci try: 1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci stats[cpu] = self._platform.GetFileContents(cpu_freq_path) 1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci except Exception as e: 1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci logging.warning( 1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 'Cannot read cpu frequency times in %s due to error: %s' % 1131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci (cpu_freq_path, e.message)) 1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci stats[cpu] = None 1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return stats 1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) @staticmethod 1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def ParseFreqSample(sample): 1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Parse a single frequency sample. 1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Args: 1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) sample: The single sample of frequency data to be parsed. 1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Returns: 1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) A dictionary associating a frequency with a time. 1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) sample_stats = {} 1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for cpu in sample: 1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) frequencies = {} 1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if sample[cpu] is None: 1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci sample_stats[cpu] = None 1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci continue 1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for line in sample[cpu].splitlines(): 1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pair = line.split() 1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) freq = int(pair[0]) * 10 ** 3 1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) timeunits = int(pair[1]) 1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if freq in frequencies: 1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) frequencies[freq] += timeunits 1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) frequencies[freq] = timeunits 1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) sample_stats[cpu] = frequencies 1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return sample_stats 1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) @staticmethod 1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def ComputeCpuStats(initial, final): 1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Parse the CPU c-state and frequency values saved during monitoring. 1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Args: 1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) initial: The parsed dictionary of initial statistics to be converted 1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) into percentages. 1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) final: The parsed dictionary of final statistics to be converted 1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) into percentages. 1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Returns: 1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Dictionary containing percentages for each CPU as well as an average 1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) across all CPUs. 1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) cpu_stats = {} 1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Each core might have different states or frequencies, so keep track of 1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # the total time in a state or frequency and how many cores report a time. 1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) cumulative_times = collections.defaultdict(lambda: (0, 0)) 1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for cpu in initial: 1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) current_cpu = {} 1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) total = 0 1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if not initial[cpu] or not final[cpu]: 1661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci cpu_stats[cpu] = collections.defaultdict(int) 1671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci continue 1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for state in initial[cpu]: 1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) current_cpu[state] = final[cpu][state] - initial[cpu][state] 1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) total += current_cpu[state] 1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for state in current_cpu: 1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) current_cpu[state] /= (float(total) / 100.0) 1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Calculate the average c-state residency across all CPUs. 1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) time, count = cumulative_times[state] 1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) cumulative_times[state] = (time + current_cpu[state], count + 1) 1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) cpu_stats[cpu] = current_cpu 1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) average = {} 1785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for state in cumulative_times: 1795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) time, count = cumulative_times[state] 1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) average[state] = time / float(count) 1815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) cpu_stats['whole_package'] = average 1825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return cpu_stats 18303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 18403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) @staticmethod 18503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) def CombineResults(cpu_stats, power_stats): 18603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) """Add frequency and c-state residency data to the power data. 18703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 18803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) Args: 18903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) cpu_stats: Dictionary containing CPU statistics. 19003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) power_stats: Dictionary containing power statistics. 19103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 19203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) Returns: 19303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) Dictionary in the format returned by StopMonitoringPower. 19403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) """ 19503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if not cpu_stats: 19603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return power_stats 19703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if 'component_utilization' not in power_stats: 19803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) power_stats['component_utilization'] = {} 19903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) for cpu in cpu_stats: 20003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) power_stats['component_utilization'][cpu] = cpu_stats[cpu] 20103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return power_stats 202