15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# found in the LICENSE file.
45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
56e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import contextlib
65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import csv
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import logging
85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import operator
95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import os
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import platform
115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import re
125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import shutil
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import tempfile
145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import zipfile
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry import decorators
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.core.platform import platform_backend
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.core.platform import power_monitor
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.util import cloud_storage
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.util import path
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.util import statistics
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)try:
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  import win32con  # pylint: disable=F0401
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  import win32event  # pylint: disable=F0401
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  import win32process  # pylint: disable=F0401
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)except ImportError:
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  win32con = None
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  win32event = None
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  win32process = None
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciSTART_EVENT = 'ippet_StartEvent'
341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciQUIT_EVENT = 'ippet_QuitEvent'
351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class IppetError(Exception):
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  pass
395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)@decorators.Cache
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def IppetPath():
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  # Look for pre-installed IPPET.
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ippet_path = path.FindInstalledWindowsApplication(os.path.join(
456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      'Intel', 'Intel(R) Platform Power Estimation Tool', 'ippet.exe'))
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if ippet_path:
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return ippet_path
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  # Look for IPPET installed previously by this script.
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ippet_path = os.path.join(
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      path.GetTelemetryDir(), 'bin', 'win', 'ippet', 'ippet.exe')
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if path.IsExecutable(ippet_path):
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return ippet_path
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  # Install IPPET.
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  zip_path = os.path.join(path.GetTelemetryDir(), 'bin', 'win', 'ippet.zip')
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  cloud_storage.GetIfChanged(zip_path, bucket=cloud_storage.PUBLIC_BUCKET)
585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  with zipfile.ZipFile(zip_path, 'r') as zip_file:
595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    zip_file.extractall(os.path.dirname(zip_path))
605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  os.remove(zip_path)
615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if path.IsExecutable(ippet_path):
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return ippet_path
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return None
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class IppetPowerMonitor(power_monitor.PowerMonitor):
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def __init__(self, backend):
705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    super(IppetPowerMonitor, self).__init__()
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._backend = backend
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._ippet_handle = None
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._output_dir = None
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def CanMonitorPower(self):
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not win32event:
775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return False
785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    # TODO(dtu): This should work on Windows 7, but it's flaky on the bots.
801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    # http://crbug.com/336558
811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    windows_8_or_later = (
825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._backend.GetOSName() == 'win' and
831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        self._backend.GetOSVersionName() >= platform_backend.WIN8)
841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if not windows_8_or_later:
855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return False
865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # This check works on Windows only.
885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    family, model = map(int, re.match('.+ Family ([0-9]+) Model ([0-9]+)',
895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                        platform.processor()).groups())
905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Model numbers from:
915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # https://software.intel.com/en-us/articles/intel-architecture-and- \
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # processor-identification-with-cpuid-model-and-family-numbers
935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # http://www.speedtraq.com
945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    sandy_bridge_or_later = ('Intel' in platform.processor() and family == 6 and
955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                             (model in (0x2A, 0x2D) or model >= 0x30))
965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not sandy_bridge_or_later:
975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return False
985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not IppetPath():
1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return False
1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return True
1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def CanMeasurePerApplicationPower(self):
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return self.CanMonitorPower()
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def StartMonitoringPower(self, browser):
1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    assert not self._ippet_handle, 'Called StartMonitoringPower() twice.'
1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._output_dir = tempfile.mkdtemp()
1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    parameters = ['-log_dir', self._output_dir, '-signals', 'START,QUIT',
1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                  '-battery', 'n', '-disk', 'n', '-gpu', 'n',
1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                  '-enable_web', 'n', '-zip', 'n', '-i', '0.1']
1131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    try:
1151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      with contextlib.closing(win32event.CreateEvent(
1161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          None, True, False, START_EVENT)) as start_event:
1171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        self._ippet_handle = self._backend.LaunchApplication(
1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            IppetPath(), parameters, elevate_privilege=True)
1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        wait_code = win32event.WaitForSingleObject(start_event, 5000)
1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if wait_code != win32event.WAIT_OBJECT_0:
1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        if wait_code == win32event.WAIT_TIMEOUT:
1221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          raise IppetError('Timed out waiting for IPPET to start.')
1231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        else:
1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          raise IppetError('Error code %d while waiting for IPPET to start.' %
1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                           wait_code)
1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    except:  # In case of emergency, don't leave IPPET processes hanging around.
1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if self._ippet_handle:
1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        try:
1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          exit_code = win32process.GetExitCodeProcess(self._ippet_handle)
1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          if exit_code == win32con.STILL_ACTIVE:
1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            win32process.TerminateProcess(self._ippet_handle, 0)
1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        finally:
1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          self._ippet_handle.Close()
1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          self._ippet_handle = None
1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      raise
1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def StopMonitoringPower(self):
1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    assert self._ippet_handle, (
1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'Called StopMonitoringPower() before StartMonitoringPower().')
1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    try:
1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      # Stop IPPET.
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      with contextlib.closing(win32event.OpenEvent(
1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          win32event.EVENT_MODIFY_STATE, False, QUIT_EVENT)) as quit_event:
1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        win32event.SetEvent(quit_event)
1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      # Wait for IPPET process to exit.
1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      wait_code = win32event.WaitForSingleObject(self._ippet_handle, 20000)
1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if wait_code != win32event.WAIT_OBJECT_0:
1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        if wait_code == win32event.WAIT_TIMEOUT:
1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          raise IppetError('Timed out waiting for IPPET to close.')
1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        else:
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          raise IppetError('Error code %d while waiting for IPPET to close.' %
1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                           wait_code)
1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    finally:  # If we need to, forcefully kill IPPET.
1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      try:
1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        exit_code = win32process.GetExitCodeProcess(self._ippet_handle)
1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        if exit_code == win32con.STILL_ACTIVE:
1601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          win32process.TerminateProcess(self._ippet_handle, 0)
1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          raise IppetError('IPPET is still running but should have stopped.')
1621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        elif exit_code != 0:
1631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          raise IppetError('IPPET closed with exit code %d.' % exit_code)
1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      finally:
1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._ippet_handle.Close()
1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._ippet_handle = None
1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Read IPPET's log file.
1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    log_file = os.path.join(self._output_dir, 'ippet_log_processes.xls')
1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    try:
1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      with open(log_file, 'r') as f:
1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        reader = csv.DictReader(f, dialect='excel-tab')
1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        data = list(reader)[1:]  # The first iteration only reports temperature.
1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    except IOError:
1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      logging.error('Output directory %s contains: %s',
1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                    self._output_dir, os.listdir(self._output_dir))
1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      raise
1785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    shutil.rmtree(self._output_dir)
1795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._output_dir = None
1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    def get(*args, **kwargs):
1825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      """Pull all iterations of a field from the IPPET data as a list.
1835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      Args:
1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        args: A list representing the field name.
1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        mult: A cosntant to multiply the field's value by, for unit conversions.
1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        default: The default value if the field is not found in the iteration.
1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      Returns:
1905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        A list containing the field's value across all iterations.
1915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      """
1925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      key = '\\\\.\\' + '\\'.join(args)
1935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      def value(line):
1945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if key in line:
1955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          return line[key]
1965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        elif 'default' in kwargs:
1975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          return kwargs['default']
1985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        else:
1995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          raise KeyError('Key "%s" not found in data and '
2005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                         'no default was given.' % key)
2015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return [float(value(line)) * kwargs.get('mult', 1) for line in data]
2025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    result = {
2045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'identifier': 'ippet',
2055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'power_samples_mw': get('Power(_Total)', 'Package W', mult=1000),
2065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'energy_consumption_mwh':
2075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            sum(map(operator.mul,
2085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                    get('Power(_Total)', 'Package W', mult=1000),
2095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                    get('sys', 'Interval(secs)', mult=1./3600.))),
2105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'component_utilization': {
2115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            'whole_package': {
2125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                'average_temperature_c':
2135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                    statistics.ArithmeticMean(get(
2145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                        'Temperature(Package)', 'Current C')),
2155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            },
2165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            'cpu': {
2175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                'power_samples_mw': get('Power(_Total)', 'CPU W', mult=1000),
2185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                'energy_consumption_mwh':
2195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                    sum(map(operator.mul,
2205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                            get('Power(_Total)', 'CPU W', mult=1000),
2215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                            get('sys', 'Interval(secs)', mult=1./3600.))),
2225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            },
2235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            'disk': {
2245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                'power_samples_mw': get('Power(_Total)', 'Disk W', mult=1000),
2255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                'energy_consumption_mwh':
2265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                    sum(map(operator.mul,
2275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                            get('Power(_Total)', 'Disk W', mult=1000),
2285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                            get('sys', 'Interval(secs)', mult=1./3600.))),
2295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            },
2305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            'gpu': {
2315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                'power_samples_mw': get('Power(_Total)', 'GPU W', mult=1000),
2325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                'energy_consumption_mwh':
2335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                    sum(map(operator.mul,
2345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                            get('Power(_Total)', 'GPU W', mult=1000),
2355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                            get('sys', 'Interval(secs)', mult=1./3600.))),
2365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            },
2375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        },
2385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
2395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Find Chrome processes in data. Note that this won't work if there are
2415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # extra Chrome processes lying around.
2425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    chrome_keys = set()
2435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for iteration in data:
2445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      for key in iteration.iterkeys():
2455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        parts = key.split('\\')
2465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if (len(parts) >= 4 and
2475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            re.match(r'Process\(Google Chrome [0-9]+\)', parts[3])):
2485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          chrome_keys.add(parts[3])
2495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Add Chrome process power usage to result.
2505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Note that this is only an estimate of Chrome's CPU power usage.
2515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if chrome_keys:
2525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      per_process_power_usage = [
2535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          get(key, 'CPU Power W', default=0, mult=1000) for key in chrome_keys]
2545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      result['application_energy_consumption_mwh'] = (
2555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          sum(map(operator.mul,
2565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                  map(sum, zip(*per_process_power_usage)),
2575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                  get('sys', 'Interval(secs)', mult=1./3600.))))
2585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return result
260