| Index: tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor.py
|
| diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a2cdb479f33076027523d6a28cb38a5410bb9ac6
|
| --- /dev/null
|
| +++ b/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor.py
|
| @@ -0,0 +1,175 @@
|
| +# 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 atexit
|
| +import ctypes
|
| +import os
|
| +import platform
|
| +import re
|
| +import shutil
|
| +import sys
|
| +import zipfile
|
| +
|
| +from telemetry import decorators
|
| +from telemetry.core.platform import power_monitor
|
| +from telemetry.util import cloud_storage
|
| +from telemetry.util import path
|
| +
|
| +
|
| +MSR_RAPL_POWER_UNIT = 0x606
|
| +MSR_PKG_ENERGY_STATUS = 0x611 # Whole package
|
| +MSR_PP0_ENERGY_STATUS = 0x639 # Core
|
| +MSR_PP1_ENERGY_STATUS = 0x641 # Uncore
|
| +MSR_DRAM_ENERGY_STATUS = 0x619
|
| +IA32_PACKAGE_THERM_STATUS = 0x1b1
|
| +IA32_TEMPERATURE_TARGET = 0x1a2
|
| +
|
| +
|
| +WINRING0_STATUS_MESSAGES = (
|
| + 'No error',
|
| + 'Unsupported platform',
|
| + 'Driver not loaded. You may need to run as Administrator',
|
| + 'Driver not found',
|
| + 'Driver unloaded by other process',
|
| + 'Driver not loaded because of executing on Network Drive',
|
| + 'Unkown error',
|
| +)
|
| +
|
| +
|
| +# The DLL initialization is global, so put it in a global variable.
|
| +_winring0 = None
|
| +
|
| +
|
| +class WinRing0Error(OSError):
|
| + pass
|
| +
|
| +
|
| +@decorators.Cache
|
| +def WinRing0Path():
|
| + file_name = 'WinRing0x64' if sys.maxsize > 2 ** 32 else 'WinRing0'
|
| + winring0_path = os.path.join(path.GetTelemetryDir(), 'bin', 'win', 'winring0')
|
| + dll_path = os.path.join(winring0_path, file_name + '.dll')
|
| + driver_path = os.path.join(winring0_path, file_name + '.sys')
|
| +
|
| + # Check for WinRing0 and download if needed.
|
| + if not (os.path.exists(dll_path) and os.path.exists(driver_path)):
|
| + zip_path = os.path.join(path.GetTelemetryDir(),
|
| + 'bin', 'win', 'winring0.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)
|
| +
|
| + # Copy kernel driver to the Python executable's path.
|
| + executable_dir = os.path.dirname(sys.executable)
|
| + if not os.path.exists(os.path.join(executable_dir, file_name + '.sys')):
|
| + shutil.copy(driver_path, executable_dir)
|
| +
|
| + return dll_path
|
| +
|
| +
|
| +def _Initialize():
|
| + global _winring0
|
| + if not _winring0:
|
| + winring0 = ctypes.CDLL(WinRing0Path())
|
| + if not winring0.InitializeOls():
|
| + winring0_status = winring0.GetDllStatus()
|
| + raise WinRing0Error(winring0_status,
|
| + 'Unable to initialize WinRing0: %s' %
|
| + WINRING0_STATUS_MESSAGES[winring0_status])
|
| + _winring0 = winring0
|
| + atexit.register(_Deinitialize)
|
| +
|
| +
|
| +def _Deinitialize():
|
| + global _winring0
|
| + if _winring0:
|
| + _winring0.DeinitializeOls()
|
| + _winring0 = None
|
| +
|
| +
|
| +def _ReadMsr(msr_number):
|
| + low = ctypes.c_uint()
|
| + high = ctypes.c_uint()
|
| + _winring0.Rdmsr(ctypes.c_uint(msr_number),
|
| + ctypes.byref(low), ctypes.byref(high))
|
| + return high.value << 32 | low.value
|
| +
|
| +
|
| +@decorators.Cache
|
| +def _EnergyMultiplier():
|
| + return 0.5 ** ((_ReadMsr(MSR_RAPL_POWER_UNIT) >> 8) & 0x1f)
|
| +
|
| +
|
| +def _PackageEnergyJoules():
|
| + return _ReadMsr(MSR_PKG_ENERGY_STATUS) * _EnergyMultiplier()
|
| +
|
| +
|
| +def _TemperatureCelsius():
|
| + tcc_activation_temp = _ReadMsr(IA32_TEMPERATURE_TARGET) >> 16 & 0x7f
|
| + if tcc_activation_temp <= 0:
|
| + tcc_activation_temp = 105
|
| + package_temp_headroom = _ReadMsr(IA32_PACKAGE_THERM_STATUS) >> 16 & 0x7f
|
| + return tcc_activation_temp - package_temp_headroom
|
| +
|
| +
|
| +def _JoulesToMilliwattHours(value_joules):
|
| + return value_joules * 1000 / 3600.
|
| +
|
| +
|
| +class MsrPowerMonitor(power_monitor.PowerMonitor):
|
| + def __init__(self, backend):
|
| + super(MsrPowerMonitor, self).__init__()
|
| + self._backend = backend
|
| + self._start_energy_j = None
|
| + self._start_temp_c = None
|
| +
|
| + def CanMonitorPower(self):
|
| + if self._backend.GetOSName() != 'win':
|
| + 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
|
| +
|
| + try:
|
| + _Initialize()
|
| + except OSError:
|
| + return False
|
| +
|
| + return True
|
| +
|
| + def StartMonitoringPower(self, browser):
|
| + assert not (self._start_energy_j or self._start_temp_c), (
|
| + 'Called StartMonitoringPower() twice.')
|
| + _Initialize()
|
| + self._start_energy_j = _PackageEnergyJoules()
|
| + self._start_temp_c = _TemperatureCelsius()
|
| +
|
| + def StopMonitoringPower(self):
|
| + assert self._start_energy_j and self._start_temp_c, (
|
| + 'Called StopMonitoringPower() before StartMonitoringPower().')
|
| + energy_consumption_j = _PackageEnergyJoules() - self._start_energy_j
|
| + average_temp_c = (_TemperatureCelsius() + self._start_temp_c) / 2.
|
| +
|
| + self._start_energy_j = None
|
| + self._start_temp_c = None
|
| +
|
| + return {
|
| + 'identifier': 'msr',
|
| + 'energy_consumption_mwh': _JoulesToMilliwattHours(energy_consumption_j),
|
| + 'component_utilization': {
|
| + 'whole_package': {
|
| + 'average_temperature_c': average_temp_c,
|
| + },
|
| + },
|
| + }
|
|
|