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

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: Add unit tests. Created 6 years, 5 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 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698