Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / platform / power_monitor / sysfs_power_monitor.py
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.
4
5 import collections
6 import os
7 import re
8
9 from telemetry import decorators
10 from telemetry.core.platform import power_monitor
11
12 CPU_PATH = '/sys/devices/system/cpu/'
13
14 class SysfsPowerMonitor(power_monitor.PowerMonitor):
15   """PowerMonitor that relies on sysfs to monitor CPU statistics on several
16   different platforms.
17   """
18   def __init__(self, platform):
19     """Constructor.
20
21     Args:
22         platform: A SysfsPlatform object.
23
24     Attributes:
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.
34     """
35     super(SysfsPowerMonitor, self).__init__()
36     self._browser = None
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
44
45   @decorators.Cache
46   def CanMonitorPower(self):
47     return bool(self._platform.RunShellCommand(
48         'if [ -e %s ]; then echo true; fi' % CPU_PATH))
49
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()
56
57   def StopMonitoringPower(self):
58     assert self._browser, 'StartMonitoringPower() not called.'
59     try:
60       out = {}
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]
73       return out
74     finally:
75       self._browser = None
76
77   def GetCpuState(self):
78     """Retrieve CPU c-state residency times from the device.
79
80     Returns:
81         Dictionary containing c-state residency times for each CPU.
82     """
83     stats = {}
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')))
90     return stats
91
92   def GetCpuFreq(self):
93     """Retrieve CPU frequency times from the device.
94
95     Returns:
96         Dictionary containing frequency times for each CPU.
97     """
98     stats = {}
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)
103     return stats
104
105   @staticmethod
106   def ParseFreqSample(sample):
107     """Parse a single frequency sample.
108
109     Args:
110         sample: The single sample of frequency data to be parsed.
111
112     Returns:
113         A dictionary associating a frequency with a time.
114     """
115     sample_stats = {}
116     for cpu in sample:
117       frequencies = {}
118       for line in sample[cpu].splitlines():
119         pair = line.split()
120         freq = int(pair[0]) * 10 ** 3
121         timeunits = int(pair[1])
122         if freq in frequencies:
123           frequencies[freq] += timeunits
124         else:
125           frequencies[freq] = timeunits
126       sample_stats[cpu] = frequencies
127     return sample_stats
128
129   @staticmethod
130   def ComputeCpuStats(initial, final):
131     """Parse the CPU c-state and frequency values saved during monitoring.
132
133     Args:
134         initial: The parsed dictionary of initial statistics to be converted
135         into percentages.
136         final: The parsed dictionary of final statistics to be converted
137         into percentages.
138
139     Returns:
140         Dictionary containing percentages for each CPU as well as an average
141         across all CPUs.
142     """
143     cpu_stats = {}
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))
147     for cpu in initial:
148       current_cpu = {}
149       total = 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
159     average = {}
160     for state in cumulative_times:
161       time, count = cumulative_times[state]
162       average[state] = time / float(count)
163     cpu_stats['whole_package'] = average
164     return cpu_stats