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

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: 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
« no previous file with comments | « no previous file | tools/telemetry/telemetry/core/platform/win_platform_backend.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
14 from telemetry.core.platform import platform_backend
15 from telemetry.core.platform import power_monitor
16 from telemetry.util import statistics
17
18 try:
19 import win32event # pylint: disable=F0401
20 except ImportError:
21 win32event = None
22
23
24 IPPET_PATH = os.path.join(
25 'C:\\', 'Program Files (x86)', 'Intel',
tonyg 2014/07/21 21:26:03 I think we want to reuse the os.getenv() code in d
dtu 2014/07/24 01:13:53 Done.
26 'Intel(R) Platform Power Estimation Tool', 'ippet.exe')
27
28
29 class IppetPowerMonitor(power_monitor.PowerMonitor):
tonyg 2014/07/21 21:26:04 Doesn't this need to be registered somewhere or do
dtu 2014/07/24 01:13:53 Done. It's really ad-hoc right now, could use a re
30 def __init__(self, backend):
31 super(IppetPowerMonitor, self).__init__()
32 self._backend = backend
33 self._ippet_handle = None
34 self._output_dir = None
35
36 def CanMonitorPower(self):
tonyg 2014/07/21 21:26:04 Is this method worth caching?
dtu 2014/07/24 01:13:53 Eh, none of these checks are expensive.
37 windows_7_or_later = (
38 self._backend.GetOSName() == 'win' and
39 self._backend.GetOSVersionName() >= platform_backend.WIN7)
40
41 # This check works on Windows only.
42 family, model = map(int, re.match('.+ Family ([0-9]+) Model ([0-9]+)',
43 platform.processor()).groups())
44 # Model numbers from:
45 # https://software.intel.com/en-us/articles/intel-architecture-and- \
46 # processor-identification-with-cpuid-model-and-family-numbers
47 # http://www.speedtraq.com
48 sandy_bridge_or_later = ('Intel' in platform.processor() and family == 6 and
49 (model in (0x2A, 0x2D) or model >= 0x30))
50
51 ippet_installed = (os.path.isfile(IPPET_PATH) and
52 os.access(IPPET_PATH, os.X_OK))
53
54 return (windows_7_or_later and sandy_bridge_or_later and
55 ippet_installed and win32event)
tonyg 2014/07/21 21:26:04 Maybe this method should shortcut?
dtu 2014/07/24 01:13:53 Done.
56
57 def StartMonitoringPower(self, browser):
58 assert not self._ippet_handle, 'Called StartMonitoringPower() twice.'
59 self._output_dir = tempfile.mkdtemp()
60 parameters = ['-log_dir', self._output_dir, '-zip', 'n',
61 '-all_processes', '-l', '0']
62 self._ippet_handle = self._backend.LaunchApplication(IPPET_PATH, parameters,
63 elevate_privilege=True)
64
65 def StopMonitoringPower(self):
66 assert self._ippet_handle, (
67 'Called StopMonitoringPower() before StartMonitoringPower().')
68 # Stop IPPET.
69 urllib2.urlopen('http://127.0.0.1:8080/ippet?cmd=quit').read()
tonyg 2014/07/21 21:26:03 Can the command line take an explicit port? This'd
dtu 2014/07/24 01:13:53 Done.
70 win32event.WaitForSingleObject(self._ippet_handle, 5000)
71 self._ippet_handle = None
72
73 # Read IPPET's log file.
74 log_file = os.path.join(self._output_dir, 'ippet_log_processes.xls')
75 with open(log_file, 'r') as f:
76 reader = csv.DictReader(f, dialect='excel-tab')
77 data = list(reader)[1:] # There's not much data in the initial iteration.
tonyg 2014/07/21 21:26:04 Is this always true? Maybe the amount of data is t
dtu 2014/07/24 01:13:53 Yes it's always true. The "zeroth" iteration repor
78 shutil.rmtree(self._output_dir)
79 self._output_dir = None
80
81 def get(*args, **kwargs):
tonyg 2014/07/21 21:26:04 Worth unittesting?
dtu 2014/07/24 01:13:53 This internal function, no, because it's not a pub
82 """Pull all iterations of a field from the IPPET data as a list.
83
84 Args:
85 args: A list representing the field name.
86 mult: A cosntant to multiply the field's value by, for unit conversions.
87 default: The default value if the field is not found in the iteration.
88
89 Returns:
90 A list containing the field's value across all iterations.
91 """
92 key = '\\\\.\\' + '\\'.join(args)
93 def value(line):
94 if key in line:
95 return line[key]
96 elif 'default' in kwargs:
97 return kwargs['default']
98 else:
99 raise KeyError('Key "%s" not found in data and '
100 'no default was given.' % key)
101 return [float(value(line)) * kwargs.get('mult', 1) for line in data]
102
103 result = {
104 'identifier': 'ippet',
105 'power_samples_mw': get('Power(_Total)', 'Package W', mult=1000),
106 'energy_consumption_mwh':
107 sum(map(operator.mul,
108 get('Power(_Total)', 'Package W', mult=1000),
109 get('sys', 'Interval(secs)', mult=1./3600.))),
110 'component_utilization': {
111 'whole_package': {
112 'average_temperature_c':
113 statistics.ArithmeticMean(get(
114 'Temperature(Package)', 'Current C')),
115 },
116 'cpu': {
117 'power_samples_mw': get('Power(_Total)', 'CPU W', mult=1000),
118 'energy_consumption_mwh':
119 sum(map(operator.mul,
120 get('Power(_Total)', 'CPU W', mult=1000),
121 get('sys', 'Interval(secs)', mult=1./3600.))),
122 },
123 'disk': {
tonyg 2014/07/21 21:26:03 Oh cool, I didn't know it was capable of disk.
dtu 2014/07/24 01:13:53 Acknowledged.
124 'power_samples_mw': get('Power(_Total)', 'Disk W', mult=1000),
125 'energy_consumption_mwh':
126 sum(map(operator.mul,
127 get('Power(_Total)', 'Disk W', mult=1000),
128 get('sys', 'Interval(secs)', mult=1./3600.))),
129 },
130 'gpu': {
131 'power_samples_mw': get('Power(_Total)', 'GPU W', mult=1000),
132 'energy_consumption_mwh':
133 sum(map(operator.mul,
134 get('Power(_Total)', 'GPU W', mult=1000),
135 get('sys', 'Interval(secs)', mult=1./3600.))),
136 },
137 },
138 }
139
140 # Find Chrome processes in data.
141 chrome_keys = set()
142 for iteration in data:
143 for key in iteration.iterkeys():
144 parts = key.split('\\')
145 if (len(parts) >= 4 and
146 re.match(r'Process\(Google Chrome [0-9]+\)', parts[3])):
147 chrome_keys.add(parts[3])
148 # Add Chrome process power usage to result.
149 if chrome_keys:
150 per_process_power_usage = [
151 get(key, 'CPU Power W', default=0, mult=1000) for key in chrome_keys]
dtu 2014/07/24 01:13:53 I'd also note that "application_energy_consumption
152 result['application_energy_consumption_mwh'] = (
153 sum(map(operator.mul,
154 map(sum, zip(*per_process_power_usage)),
155 get('sys', 'Interval(secs)', mult=1./3600.))))
156
157 return result
OLDNEW
« no previous file with comments | « no previous file | tools/telemetry/telemetry/core/platform/win_platform_backend.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698