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

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: Increase timeout again to 20s. 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
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..eaf031aa138b654fa3824492ef5a870376272f7f
--- /dev/null
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor.py
@@ -0,0 +1,236 @@
+# 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 logging
+import operator
+import os
+import platform
+import re
+import shutil
+import tempfile
+import urllib2
+import zipfile
+
+from telemetry import decorators
+from telemetry.core import util
+from telemetry.core.platform import platform_backend
+from telemetry.core.platform import power_monitor
+from telemetry.util import cloud_storage
+from telemetry.util import path
+from telemetry.util import statistics
+
+try:
+ import win32con # pylint: disable=F0401
+ import win32event # pylint: disable=F0401
+ import win32process # pylint: disable=F0401
+except ImportError:
+ win32con = None
+ win32event = None
+ win32process = None
+
+
+class IppetError(Exception):
+ pass
+
+
+@decorators.Cache
+def IppetPath():
+ # Look for pre-installed IPPET.
+ ippet_path = path.FindInstalledWindowsApplication(os.path.join(
+ 'Intel', 'Intel(R) Platform Power Estimation Tool', 'ippet.exe'))
+ if ippet_path:
+ return ippet_path
+
+ # Look for IPPET installed previously by this script.
+ ippet_path = os.path.join(
+ path.GetTelemetryDir(), 'bin', 'win', 'ippet', 'ippet.exe')
+ if path.IsExecutable(ippet_path):
+ return ippet_path
+
+ # Install IPPET.
+ zip_path = os.path.join(path.GetTelemetryDir(), 'bin', 'win', 'ippet.zip')
+ cloud_storage.GetIfChanged(zip_path, bucket=cloud_storage.PUBLIC_BUCKET)
+ with zipfile.ZipFile(zip_path, 'r') as zip_file:
+ zip_file.extractall(os.path.dirname(zip_path))
+ os.remove(zip_path)
+
+ if path.IsExecutable(ippet_path):
+ return ippet_path
+
+ return None
+
+
+class IppetPowerMonitor(power_monitor.PowerMonitor):
+ def __init__(self, backend):
+ super(IppetPowerMonitor, self).__init__()
+ self._backend = backend
+ self._ippet_handle = None
+ self._ippet_port = None
+ self._output_dir = None
+
+ def CanMonitorPower(self):
+ if not win32event:
+ return False
+
+ windows_7_or_later = (
+ self._backend.GetOSName() == 'win' and
+ self._backend.GetOSVersionName() >= platform_backend.WIN7)
+ if not windows_7_or_later:
+ return False
+
+ # 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))
+ if not sandy_bridge_or_later:
+ return False
+
+ if not IppetPath():
+ return False
+
+ return True
+
+ def CanMeasurePerApplicationPower(self):
+ return self.CanMonitorPower()
+
+ def StartMonitoringPower(self, browser):
+ assert not self._ippet_handle, 'Called StartMonitoringPower() twice.'
+ self._output_dir = tempfile.mkdtemp()
+ self._ippet_port = util.GetUnreservedAvailableLocalPort()
+ parameters = ['-log_dir', self._output_dir,
+ '-web_port', str(self._ippet_port),
+ '-zip', 'n', '-all_processes', '-l', '0']
+ self._ippet_handle = self._backend.LaunchApplication(
+ IppetPath(), parameters, elevate_privilege=True)
+
+ def IppetServerIsUp():
+ try:
+ urllib2.urlopen('http://127.0.0.1:%d/ippet' % self._ippet_port)
+ except urllib2.URLError:
+ return False
+ return True
+ util.WaitFor(IppetServerIsUp, timeout=5)
+
+ def StopMonitoringPower(self):
+ assert self._ippet_handle, (
+ 'Called StopMonitoringPower() before StartMonitoringPower().')
+ # Stop IPPET.
+ ippet_quit_url = 'http://127.0.0.1:%d/ippet?cmd=quit' % self._ippet_port
+ quit_output = urllib2.urlopen(ippet_quit_url).read()
+ if quit_output != 'quiting\r\n':
+ raise IppetError('Failed to quit IPPET: %s' % quit_output.strip())
+ wait_return_code = win32event.WaitForSingleObject(self._ippet_handle, 20000)
+ if wait_return_code != win32event.WAIT_OBJECT_0:
+ if wait_return_code == win32event.WAIT_TIMEOUT:
+ raise IppetError('Timed out waiting for IPPET to close.')
+ else:
+ raise IppetError('Error code %d while waiting for IPPET to close.' %
+ wait_return_code)
+ ippet_exit_code = win32process.GetExitCodeProcess(self._ippet_handle)
+ if ippet_exit_code == win32con.STILL_ACTIVE:
+ raise IppetError('IPPET is still running but should have stopped.')
+ elif ippet_exit_code != 0:
+ raise IppetError('IPPET closed with exit code %d.' % ippet_exit_code)
+ self._ippet_handle.Close()
+ self._ippet_handle = None
+ self._ippet_port = None
+
+ # Read IPPET's log file.
+ log_file = os.path.join(self._output_dir, 'ippet_log_processes.xls')
+ try:
+ with open(log_file, 'r') as f:
+ reader = csv.DictReader(f, dialect='excel-tab')
+ data = list(reader)[1:] # The first iteration only reports temperature.
+ except IOError:
+ logging.error('Output directory %s contains: %s',
+ self._output_dir, os.listdir(self._output_dir))
+ raise
+ shutil.rmtree(self._output_dir)
+ self._output_dir = None
+
+ def get(*args, **kwargs):
+ """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': {
+ '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. Note that this won't work if there are
+ # extra Chrome processes lying around.
+ 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.
+ # Note that this is only an estimate of Chrome's CPU power usage.
+ if chrome_keys:
+ per_process_power_usage = [
+ get(key, 'CPU Power W', default=0, mult=1000) for key in chrome_keys]
+ 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

Powered by Google App Engine
This is Rietveld 408576698