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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tools/telemetry/telemetry/core/platform/win_platform_backend.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor.py
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ffc3bcfbb867ef6686aff7dee47dc58e174c564
--- /dev/null
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor.py
@@ -0,0 +1,157 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import csv
+import operator
+import os
+import platform
+import re
+import shutil
+import tempfile
+import urllib2
+
+from telemetry.core.platform import platform_backend
+from telemetry.core.platform import power_monitor
+from telemetry.util import statistics
+
+try:
+ import win32event # pylint: disable=F0401
+except ImportError:
+ win32event = None
+
+
+IPPET_PATH = os.path.join(
+ '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.
+ 'Intel(R) Platform Power Estimation Tool', 'ippet.exe')
+
+
+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
+ def __init__(self, backend):
+ super(IppetPowerMonitor, self).__init__()
+ self._backend = backend
+ self._ippet_handle = None
+ self._output_dir = None
+
+ 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.
+ windows_7_or_later = (
+ self._backend.GetOSName() == 'win' and
+ self._backend.GetOSVersionName() >= platform_backend.WIN7)
+
+ # This check works on Windows only.
+ family, model = map(int, re.match('.+ Family ([0-9]+) Model ([0-9]+)',
+ platform.processor()).groups())
+ # Model numbers from:
+ # https://software.intel.com/en-us/articles/intel-architecture-and- \
+ # processor-identification-with-cpuid-model-and-family-numbers
+ # http://www.speedtraq.com
+ sandy_bridge_or_later = ('Intel' in platform.processor() and family == 6 and
+ (model in (0x2A, 0x2D) or model >= 0x30))
+
+ ippet_installed = (os.path.isfile(IPPET_PATH) and
+ os.access(IPPET_PATH, os.X_OK))
+
+ return (windows_7_or_later and sandy_bridge_or_later and
+ ippet_installed and win32event)
tonyg 2014/07/21 21:26:04 Maybe this method should shortcut?
dtu 2014/07/24 01:13:53 Done.
+
+ def StartMonitoringPower(self, browser):
+ assert not self._ippet_handle, 'Called StartMonitoringPower() twice.'
+ self._output_dir = tempfile.mkdtemp()
+ parameters = ['-log_dir', self._output_dir, '-zip', 'n',
+ '-all_processes', '-l', '0']
+ self._ippet_handle = self._backend.LaunchApplication(IPPET_PATH, parameters,
+ elevate_privilege=True)
+
+ def StopMonitoringPower(self):
+ assert self._ippet_handle, (
+ 'Called StopMonitoringPower() before StartMonitoringPower().')
+ # Stop IPPET.
+ 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.
+ win32event.WaitForSingleObject(self._ippet_handle, 5000)
+ self._ippet_handle = None
+
+ # Read IPPET's log file.
+ log_file = os.path.join(self._output_dir, 'ippet_log_processes.xls')
+ with open(log_file, 'r') as f:
+ reader = csv.DictReader(f, dialect='excel-tab')
+ 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
+ shutil.rmtree(self._output_dir)
+ self._output_dir = None
+
+ 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
+ """Pull all iterations of a field from the IPPET data as a list.
+
+ Args:
+ args: A list representing the field name.
+ mult: A cosntant to multiply the field's value by, for unit conversions.
+ default: The default value if the field is not found in the iteration.
+
+ Returns:
+ A list containing the field's value across all iterations.
+ """
+ key = '\\\\.\\' + '\\'.join(args)
+ def value(line):
+ if key in line:
+ return line[key]
+ elif 'default' in kwargs:
+ return kwargs['default']
+ else:
+ raise KeyError('Key "%s" not found in data and '
+ 'no default was given.' % key)
+ return [float(value(line)) * kwargs.get('mult', 1) for line in data]
+
+ result = {
+ 'identifier': 'ippet',
+ 'power_samples_mw': get('Power(_Total)', 'Package W', mult=1000),
+ 'energy_consumption_mwh':
+ sum(map(operator.mul,
+ get('Power(_Total)', 'Package W', mult=1000),
+ get('sys', 'Interval(secs)', mult=1./3600.))),
+ 'component_utilization': {
+ 'whole_package': {
+ 'average_temperature_c':
+ statistics.ArithmeticMean(get(
+ 'Temperature(Package)', 'Current C')),
+ },
+ 'cpu': {
+ 'power_samples_mw': get('Power(_Total)', 'CPU W', mult=1000),
+ 'energy_consumption_mwh':
+ sum(map(operator.mul,
+ get('Power(_Total)', 'CPU W', mult=1000),
+ get('sys', 'Interval(secs)', mult=1./3600.))),
+ },
+ '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.
+ 'power_samples_mw': get('Power(_Total)', 'Disk W', mult=1000),
+ 'energy_consumption_mwh':
+ sum(map(operator.mul,
+ get('Power(_Total)', 'Disk W', mult=1000),
+ get('sys', 'Interval(secs)', mult=1./3600.))),
+ },
+ 'gpu': {
+ 'power_samples_mw': get('Power(_Total)', 'GPU W', mult=1000),
+ 'energy_consumption_mwh':
+ sum(map(operator.mul,
+ get('Power(_Total)', 'GPU W', mult=1000),
+ get('sys', 'Interval(secs)', mult=1./3600.))),
+ },
+ },
+ }
+
+ # Find Chrome processes in data.
+ chrome_keys = set()
+ for iteration in data:
+ for key in iteration.iterkeys():
+ parts = key.split('\\')
+ if (len(parts) >= 4 and
+ re.match(r'Process\(Google Chrome [0-9]+\)', parts[3])):
+ chrome_keys.add(parts[3])
+ # Add Chrome process power usage to result.
+ if chrome_keys:
+ per_process_power_usage = [
+ 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
+ result['application_energy_consumption_mwh'] = (
+ sum(map(operator.mul,
+ map(sum, zip(*per_process_power_usage)),
+ get('sys', 'Interval(secs)', mult=1./3600.))))
+
+ return result
« 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