1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
9 from telemetry import decorators
10 from telemetry.core.platform import power_monitor
12 CPU_PATH = '/sys/devices/system/cpu/'
14 class SysfsPowerMonitor(power_monitor.PowerMonitor):
15 """PowerMonitor that relies on sysfs to monitor CPU statistics on several
18 def __init__(self, platform):
22 platform: A SysfsPlatform object.
25 _browser: The browser to monitor.
26 _cpus: A list of the CPUs on the target device.
27 _end_time: The time the test stopped monitoring power.
28 _final_cstate: The c-state residency times after the test.
29 _final_freq: The CPU frequency times after the test.
30 _initial_cstate: The c-state residency times before the test.
31 _initial_freq: The CPU frequency times before the test.
32 _platform: A SysfsPlatform object associated with the target platform.
33 _start_time: The time the test started monitoring power.
35 super(SysfsPowerMonitor, self).__init__()
37 self._cpus = filter(lambda x: re.match(r'^cpu[0-9]+', x),
38 platform.RunShellCommand('ls %s' % CPU_PATH).split())
39 self._final_cstate = None
40 self._final_freq = None
41 self._initial_cstate = None
42 self._initial_freq = None
43 self._platform = platform
46 def CanMonitorPower(self):
47 return bool(self._platform.RunShellCommand(
48 'if [ -e %s ]; then echo true; fi' % CPU_PATH))
50 def StartMonitoringPower(self, browser):
51 assert not self._browser, 'Must call StopMonitoringPower().'
52 self._browser = browser
53 if self.CanMonitorPower():
54 self._initial_freq = self.GetCpuFreq()
55 self._initial_cstate = self.GetCpuState()
57 def StopMonitoringPower(self):
58 assert self._browser, 'StartMonitoringPower() not called.'
61 if self.CanMonitorPower():
62 self._final_freq = self.GetCpuFreq()
63 self._final_cstate = self.GetCpuState()
64 frequencies = SysfsPowerMonitor.ComputeCpuStats(
65 SysfsPowerMonitor.ParseFreqSample(self._initial_freq),
66 SysfsPowerMonitor.ParseFreqSample(self._final_freq))
67 cstates = SysfsPowerMonitor.ComputeCpuStats(
68 self._platform.ParseStateSample(self._initial_cstate),
69 self._platform.ParseStateSample(self._final_cstate))
70 for cpu in frequencies:
71 out[cpu] = {'frequency_percent': frequencies[cpu]}
72 out[cpu]['cstate_residency_percent'] = cstates[cpu]
77 def GetCpuState(self):
78 """Retrieve CPU c-state residency times from the device.
81 Dictionary containing c-state residency times for each CPU.
84 for cpu in self._cpus:
85 cpu_state_path = os.path.join(CPU_PATH, cpu, 'cpuidle/state*')
86 stats[cpu] = self._platform.RunShellCommand(
87 'cat %s %s %s; date +%%s' % (os.path.join(cpu_state_path, 'name'),
88 os.path.join(cpu_state_path, 'time'),
89 os.path.join(cpu_state_path, 'latency')))
93 """Retrieve CPU frequency times from the device.
96 Dictionary containing frequency times for each CPU.
99 for cpu in self._cpus:
100 cpu_freq_path = os.path.join(
101 CPU_PATH, cpu, 'cpufreq/stats/time_in_state')
102 stats[cpu] = self._platform.RunShellCommand('cat %s' % cpu_freq_path)
106 def ParseFreqSample(sample):
107 """Parse a single frequency sample.
110 sample: The single sample of frequency data to be parsed.
113 A dictionary associating a frequency with a time.
118 for line in sample[cpu].splitlines():
120 freq = int(pair[0]) * 10 ** 3
121 timeunits = int(pair[1])
122 if freq in frequencies:
123 frequencies[freq] += timeunits
125 frequencies[freq] = timeunits
126 sample_stats[cpu] = frequencies
130 def ComputeCpuStats(initial, final):
131 """Parse the CPU c-state and frequency values saved during monitoring.
134 initial: The parsed dictionary of initial statistics to be converted
136 final: The parsed dictionary of final statistics to be converted
140 Dictionary containing percentages for each CPU as well as an average
144 # Each core might have different states or frequencies, so keep track of
145 # the total time in a state or frequency and how many cores report a time.
146 cumulative_times = collections.defaultdict(lambda: (0, 0))
150 for state in initial[cpu]:
151 current_cpu[state] = final[cpu][state] - initial[cpu][state]
152 total += current_cpu[state]
153 for state in current_cpu:
154 current_cpu[state] /= (float(total) / 100.0)
155 # Calculate the average c-state residency across all CPUs.
156 time, count = cumulative_times[state]
157 cumulative_times[state] = (time + current_cpu[state], count + 1)
158 cpu_stats[cpu] = current_cpu
160 for state in cumulative_times:
161 time, count = cumulative_times[state]
162 average[state] = time / float(count)
163 cpu_stats['whole_package'] = average