Chromium Code Reviews| 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 | |
| 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 | |
| OLD | NEW |