OLD | NEW |
---|---|
(Empty) | |
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 csv | |
6 import operator | |
7 import os | |
8 import platform | |
9 import re | |
10 import shutil | |
11 import tempfile | |
12 import urllib2 | |
13 import zipfile | |
14 | |
15 from telemetry import decorators | |
16 from telemetry.core import util | |
17 from telemetry.core.platform import platform_backend | |
18 from telemetry.core.platform import power_monitor | |
19 from telemetry.util import cloud_storage | |
20 from telemetry.util import path | |
21 from telemetry.util import statistics | |
22 | |
23 try: | |
24 import win32event # pylint: disable=F0401 | |
25 except ImportError: | |
26 win32event = None | |
27 | |
28 | |
29 @decorators.Cache | |
30 def IppetPath(): | |
31 # Look for pre-installed IPPET. | |
32 ippet_path = path.FindInstalledWindowsApplication(os.path.join( | |
33 'Intel', 'Intel(R) Platform Power Estimation Tool', 'ippet.exe')) | |
34 if ippet_path: | |
35 return ippet_path | |
36 | |
37 # Look for IPPET installed previously by this script. | |
38 ippet_path = os.path.join( | |
39 path.GetTelemetryDir(), 'bin', 'win', 'ippet', 'ippet.exe') | |
tonyg
2014/07/24 01:32:03
support_binaries.FindPath('ippet')?
| |
40 if path.IsExecutable(ippet_path): | |
41 return ippet_path | |
42 | |
43 # Install IPPET. | |
tonyg
2014/07/24 01:32:03
Rock on! You figured it out :)
Can we instead tea
| |
44 zip_path = os.path.join(path.GetTelemetryDir(), 'bin', 'win', 'ippet.zip') | |
45 cloud_storage.GetIfChanged(zip_path, bucket=cloud_storage.PUBLIC_BUCKET) | |
46 with zipfile.ZipFile(zip_path, 'r') as zip_file: | |
tonyg
2014/07/24 01:38:28
I wonder whether support binaries should be made z
| |
47 zip_file.extractall(os.path.dirname(zip_path)) | |
48 os.remove(zip_path) | |
49 | |
50 if path.IsExecutable(ippet_path): | |
51 return ippet_path | |
52 | |
53 return None | |
54 | |
55 | |
56 class IppetPowerMonitor(power_monitor.PowerMonitor): | |
57 def __init__(self, backend): | |
58 super(IppetPowerMonitor, self).__init__() | |
59 self._backend = backend | |
60 self._ippet_handle = None | |
61 self._ippet_port = None | |
62 self._output_dir = None | |
63 | |
64 def CanMonitorPower(self): | |
65 if not win32event: | |
66 return False | |
67 | |
68 windows_7_or_later = ( | |
69 self._backend.GetOSName() == 'win' and | |
70 self._backend.GetOSVersionName() >= platform_backend.WIN7) | |
71 if not windows_7_or_later: | |
72 return False | |
73 | |
74 # This check works on Windows only. | |
75 family, model = map(int, re.match('.+ Family ([0-9]+) Model ([0-9]+)', | |
76 platform.processor()).groups()) | |
77 # Model numbers from: | |
78 # https://software.intel.com/en-us/articles/intel-architecture-and- \ | |
79 # processor-identification-with-cpuid-model-and-family-numbers | |
80 # http://www.speedtraq.com | |
81 sandy_bridge_or_later = ('Intel' in platform.processor() and family == 6 and | |
82 (model in (0x2A, 0x2D) or model >= 0x30)) | |
83 if not sandy_bridge_or_later: | |
84 return False | |
85 | |
86 if not IppetPath(): | |
87 return False | |
88 | |
89 return True | |
90 | |
91 def CanMeasurePerApplicationPower(self): | |
92 return self.CanMonitorPower() | |
93 | |
94 def StartMonitoringPower(self, browser): | |
95 assert not self._ippet_handle, 'Called StartMonitoringPower() twice.' | |
96 self._output_dir = tempfile.mkdtemp() | |
97 self._ippet_port = util.GetUnreservedAvailableLocalPort() | |
98 parameters = ['-log_dir', self._output_dir, | |
99 '-web_port', str(self._ippet_port), | |
100 '-zip', 'n', '-all_processes', '-l', '0'] | |
101 self._ippet_handle = self._backend.LaunchApplication( | |
102 IppetPath(), parameters, elevate_privilege=True) | |
103 | |
104 def IppetServerIsUp(): | |
105 try: | |
106 urllib2.urlopen('http://127.0.0.1:%d/ippet' % self._ippet_port) | |
107 except urllib2.URLError: | |
108 return False | |
109 return True | |
110 util.WaitFor(IppetServerIsUp, timeout=5) | |
111 | |
112 def StopMonitoringPower(self): | |
113 assert self._ippet_handle, ( | |
114 'Called StopMonitoringPower() before StartMonitoringPower().') | |
115 # Stop IPPET. | |
116 ippet_quit_url = 'http://127.0.0.1:%d/ippet?cmd=quit' % self._ippet_port | |
117 urllib2.urlopen(ippet_quit_url).read() | |
118 win32event.WaitForSingleObject(self._ippet_handle, 5000) | |
119 self._ippet_handle = None | |
120 self._ippet_port = None | |
121 | |
122 # Read IPPET's log file. | |
123 log_file = os.path.join(self._output_dir, 'ippet_log_processes.xls') | |
124 with open(log_file, 'r') as f: | |
125 reader = csv.DictReader(f, dialect='excel-tab') | |
126 data = list(reader)[1:] # The initial iteration only reports temperature. | |
127 shutil.rmtree(self._output_dir) | |
128 self._output_dir = None | |
129 | |
130 def get(*args, **kwargs): | |
131 """Pull all iterations of a field from the IPPET data as a list. | |
132 | |
133 Args: | |
134 args: A list representing the field name. | |
135 mult: A cosntant to multiply the field's value by, for unit conversions. | |
136 default: The default value if the field is not found in the iteration. | |
137 | |
138 Returns: | |
139 A list containing the field's value across all iterations. | |
140 """ | |
141 key = '\\\\.\\' + '\\'.join(args) | |
142 def value(line): | |
143 if key in line: | |
144 return line[key] | |
145 elif 'default' in kwargs: | |
146 return kwargs['default'] | |
147 else: | |
148 raise KeyError('Key "%s" not found in data and ' | |
149 'no default was given.' % key) | |
150 return [float(value(line)) * kwargs.get('mult', 1) for line in data] | |
151 | |
152 result = { | |
153 'identifier': 'ippet', | |
154 'power_samples_mw': get('Power(_Total)', 'Package W', mult=1000), | |
155 'energy_consumption_mwh': | |
156 sum(map(operator.mul, | |
157 get('Power(_Total)', 'Package W', mult=1000), | |
158 get('sys', 'Interval(secs)', mult=1./3600.))), | |
159 'component_utilization': { | |
160 'whole_package': { | |
161 'average_temperature_c': | |
162 statistics.ArithmeticMean(get( | |
163 'Temperature(Package)', 'Current C')), | |
164 }, | |
165 'cpu': { | |
166 'power_samples_mw': get('Power(_Total)', 'CPU W', mult=1000), | |
167 'energy_consumption_mwh': | |
168 sum(map(operator.mul, | |
169 get('Power(_Total)', 'CPU W', mult=1000), | |
170 get('sys', 'Interval(secs)', mult=1./3600.))), | |
171 }, | |
172 'disk': { | |
173 'power_samples_mw': get('Power(_Total)', 'Disk W', mult=1000), | |
174 'energy_consumption_mwh': | |
175 sum(map(operator.mul, | |
176 get('Power(_Total)', 'Disk W', mult=1000), | |
177 get('sys', 'Interval(secs)', mult=1./3600.))), | |
178 }, | |
179 'gpu': { | |
180 'power_samples_mw': get('Power(_Total)', 'GPU W', mult=1000), | |
181 'energy_consumption_mwh': | |
182 sum(map(operator.mul, | |
183 get('Power(_Total)', 'GPU W', mult=1000), | |
184 get('sys', 'Interval(secs)', mult=1./3600.))), | |
185 }, | |
186 }, | |
187 } | |
188 | |
189 # Find Chrome processes in data. Note that this won't work if there are | |
190 # extra Chrome processes lying around. | |
191 chrome_keys = set() | |
192 for iteration in data: | |
193 for key in iteration.iterkeys(): | |
194 parts = key.split('\\') | |
195 if (len(parts) >= 4 and | |
196 re.match(r'Process\(Google Chrome [0-9]+\)', parts[3])): | |
tonyg
2014/07/24 01:38:28
I don't think we're supposed to do it this way. Th
dtu
2014/07/24 23:13:15
This class gets a PlatformBackend in its construct
| |
197 chrome_keys.add(parts[3]) | |
198 # Add Chrome process power usage to result. | |
199 # Note that this is only an estimate of Chrome's CPU power usage. | |
200 if chrome_keys: | |
201 per_process_power_usage = [ | |
202 get(key, 'CPU Power W', default=0, mult=1000) for key in chrome_keys] | |
203 result['application_energy_consumption_mwh'] = ( | |
204 sum(map(operator.mul, | |
205 map(sum, zip(*per_process_power_usage)), | |
206 get('sys', 'Interval(secs)', mult=1./3600.)))) | |
207 | |
208 return result | |
OLD | NEW |