Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(303)

Side by Side Diff: tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor.py

Issue 394923003: [telemetry] Add IPPET power monitor. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Increase timeout again to 20s. Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 logging
7 import operator
8 import os
9 import platform
10 import re
11 import shutil
12 import tempfile
13 import urllib2
14 import zipfile
15
16 from telemetry import decorators
17 from telemetry.core import util
18 from telemetry.core.platform import platform_backend
19 from telemetry.core.platform import power_monitor
20 from telemetry.util import cloud_storage
21 from telemetry.util import path
22 from telemetry.util import statistics
23
24 try:
25 import win32con # pylint: disable=F0401
26 import win32event # pylint: disable=F0401
27 import win32process # pylint: disable=F0401
28 except ImportError:
29 win32con = None
30 win32event = None
31 win32process = None
32
33
34 class IppetError(Exception):
35 pass
36
37
38 @decorators.Cache
39 def IppetPath():
40 # Look for pre-installed IPPET.
41 ippet_path = path.FindInstalledWindowsApplication(os.path.join(
42 'Intel', 'Intel(R) Platform Power Estimation Tool', 'ippet.exe'))
43 if ippet_path:
44 return ippet_path
45
46 # Look for IPPET installed previously by this script.
47 ippet_path = os.path.join(
48 path.GetTelemetryDir(), 'bin', 'win', 'ippet', 'ippet.exe')
49 if path.IsExecutable(ippet_path):
50 return ippet_path
51
52 # Install IPPET.
53 zip_path = os.path.join(path.GetTelemetryDir(), 'bin', 'win', 'ippet.zip')
54 cloud_storage.GetIfChanged(zip_path, bucket=cloud_storage.PUBLIC_BUCKET)
55 with zipfile.ZipFile(zip_path, 'r') as zip_file:
56 zip_file.extractall(os.path.dirname(zip_path))
57 os.remove(zip_path)
58
59 if path.IsExecutable(ippet_path):
60 return ippet_path
61
62 return None
63
64
65 class IppetPowerMonitor(power_monitor.PowerMonitor):
66 def __init__(self, backend):
67 super(IppetPowerMonitor, self).__init__()
68 self._backend = backend
69 self._ippet_handle = None
70 self._ippet_port = None
71 self._output_dir = None
72
73 def CanMonitorPower(self):
74 if not win32event:
75 return False
76
77 windows_7_or_later = (
78 self._backend.GetOSName() == 'win' and
79 self._backend.GetOSVersionName() >= platform_backend.WIN7)
80 if not windows_7_or_later:
81 return False
82
83 # This check works on Windows only.
84 family, model = map(int, re.match('.+ Family ([0-9]+) Model ([0-9]+)',
85 platform.processor()).groups())
86 # Model numbers from:
87 # https://software.intel.com/en-us/articles/intel-architecture-and- \
88 # processor-identification-with-cpuid-model-and-family-numbers
89 # http://www.speedtraq.com
90 sandy_bridge_or_later = ('Intel' in platform.processor() and family == 6 and
91 (model in (0x2A, 0x2D) or model >= 0x30))
92 if not sandy_bridge_or_later:
93 return False
94
95 if not IppetPath():
96 return False
97
98 return True
99
100 def CanMeasurePerApplicationPower(self):
101 return self.CanMonitorPower()
102
103 def StartMonitoringPower(self, browser):
104 assert not self._ippet_handle, 'Called StartMonitoringPower() twice.'
105 self._output_dir = tempfile.mkdtemp()
106 self._ippet_port = util.GetUnreservedAvailableLocalPort()
107 parameters = ['-log_dir', self._output_dir,
108 '-web_port', str(self._ippet_port),
109 '-zip', 'n', '-all_processes', '-l', '0']
110 self._ippet_handle = self._backend.LaunchApplication(
111 IppetPath(), parameters, elevate_privilege=True)
112
113 def IppetServerIsUp():
114 try:
115 urllib2.urlopen('http://127.0.0.1:%d/ippet' % self._ippet_port)
116 except urllib2.URLError:
117 return False
118 return True
119 util.WaitFor(IppetServerIsUp, timeout=5)
120
121 def StopMonitoringPower(self):
122 assert self._ippet_handle, (
123 'Called StopMonitoringPower() before StartMonitoringPower().')
124 # Stop IPPET.
125 ippet_quit_url = 'http://127.0.0.1:%d/ippet?cmd=quit' % self._ippet_port
126 quit_output = urllib2.urlopen(ippet_quit_url).read()
127 if quit_output != 'quiting\r\n':
128 raise IppetError('Failed to quit IPPET: %s' % quit_output.strip())
129 wait_return_code = win32event.WaitForSingleObject(self._ippet_handle, 20000)
130 if wait_return_code != win32event.WAIT_OBJECT_0:
131 if wait_return_code == win32event.WAIT_TIMEOUT:
132 raise IppetError('Timed out waiting for IPPET to close.')
133 else:
134 raise IppetError('Error code %d while waiting for IPPET to close.' %
135 wait_return_code)
136 ippet_exit_code = win32process.GetExitCodeProcess(self._ippet_handle)
137 if ippet_exit_code == win32con.STILL_ACTIVE:
138 raise IppetError('IPPET is still running but should have stopped.')
139 elif ippet_exit_code != 0:
140 raise IppetError('IPPET closed with exit code %d.' % ippet_exit_code)
141 self._ippet_handle.Close()
142 self._ippet_handle = None
143 self._ippet_port = None
144
145 # Read IPPET's log file.
146 log_file = os.path.join(self._output_dir, 'ippet_log_processes.xls')
147 try:
148 with open(log_file, 'r') as f:
149 reader = csv.DictReader(f, dialect='excel-tab')
150 data = list(reader)[1:] # The first iteration only reports temperature.
151 except IOError:
152 logging.error('Output directory %s contains: %s',
153 self._output_dir, os.listdir(self._output_dir))
154 raise
155 shutil.rmtree(self._output_dir)
156 self._output_dir = None
157
158 def get(*args, **kwargs):
159 """Pull all iterations of a field from the IPPET data as a list.
160
161 Args:
162 args: A list representing the field name.
163 mult: A cosntant to multiply the field's value by, for unit conversions.
164 default: The default value if the field is not found in the iteration.
165
166 Returns:
167 A list containing the field's value across all iterations.
168 """
169 key = '\\\\.\\' + '\\'.join(args)
170 def value(line):
171 if key in line:
172 return line[key]
173 elif 'default' in kwargs:
174 return kwargs['default']
175 else:
176 raise KeyError('Key "%s" not found in data and '
177 'no default was given.' % key)
178 return [float(value(line)) * kwargs.get('mult', 1) for line in data]
179
180 result = {
181 'identifier': 'ippet',
182 'power_samples_mw': get('Power(_Total)', 'Package W', mult=1000),
183 'energy_consumption_mwh':
184 sum(map(operator.mul,
185 get('Power(_Total)', 'Package W', mult=1000),
186 get('sys', 'Interval(secs)', mult=1./3600.))),
187 'component_utilization': {
188 'whole_package': {
189 'average_temperature_c':
190 statistics.ArithmeticMean(get(
191 'Temperature(Package)', 'Current C')),
192 },
193 'cpu': {
194 'power_samples_mw': get('Power(_Total)', 'CPU W', mult=1000),
195 'energy_consumption_mwh':
196 sum(map(operator.mul,
197 get('Power(_Total)', 'CPU W', mult=1000),
198 get('sys', 'Interval(secs)', mult=1./3600.))),
199 },
200 'disk': {
201 'power_samples_mw': get('Power(_Total)', 'Disk W', mult=1000),
202 'energy_consumption_mwh':
203 sum(map(operator.mul,
204 get('Power(_Total)', 'Disk W', mult=1000),
205 get('sys', 'Interval(secs)', mult=1./3600.))),
206 },
207 'gpu': {
208 'power_samples_mw': get('Power(_Total)', 'GPU W', mult=1000),
209 'energy_consumption_mwh':
210 sum(map(operator.mul,
211 get('Power(_Total)', 'GPU W', mult=1000),
212 get('sys', 'Interval(secs)', mult=1./3600.))),
213 },
214 },
215 }
216
217 # Find Chrome processes in data. Note that this won't work if there are
218 # extra Chrome processes lying around.
219 chrome_keys = set()
220 for iteration in data:
221 for key in iteration.iterkeys():
222 parts = key.split('\\')
223 if (len(parts) >= 4 and
224 re.match(r'Process\(Google Chrome [0-9]+\)', parts[3])):
225 chrome_keys.add(parts[3])
226 # Add Chrome process power usage to result.
227 # Note that this is only an estimate of Chrome's CPU power usage.
228 if chrome_keys:
229 per_process_power_usage = [
230 get(key, 'CPU Power W', default=0, mult=1000) for key in chrome_keys]
231 result['application_energy_consumption_mwh'] = (
232 sum(map(operator.mul,
233 map(sum, zip(*per_process_power_usage)),
234 get('sys', 'Interval(secs)', mult=1./3600.))))
235
236 return result
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698