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 5import logging 6import platform 7import re 8 9from telemetry import decorators 10from telemetry.internal.platform import power_monitor 11 12 13MSR_RAPL_POWER_UNIT = 0x606 14MSR_PKG_ENERGY_STATUS = 0x611 # Whole package 15MSR_PP0_ENERGY_STATUS = 0x639 # Core 16MSR_PP1_ENERGY_STATUS = 0x641 # Uncore 17MSR_DRAM_ENERGY_STATUS = 0x619 18IA32_PACKAGE_THERM_STATUS = 0x1b1 19IA32_TEMPERATURE_TARGET = 0x1a2 20 21 22def _JoulesToMilliwattHours(value_joules): 23 return value_joules * 1000 / 3600. 24 25 26def _IsSandyBridgeOrLater(vendor, family, model): 27 # Model numbers from: 28 # https://software.intel.com/en-us/articles/intel-architecture-and-processor-identification-with-cpuid-model-and-family-numbers 29 # http://www.speedtraq.com 30 return ('Intel' in vendor and family == 6 and 31 (model in (0x2A, 0x2D) or model >= 0x30)) 32 33 34class MsrPowerMonitor(power_monitor.PowerMonitor): 35 def __init__(self, backend): 36 super(MsrPowerMonitor, self).__init__() 37 self._backend = backend 38 self._start_energy_j = None 39 self._start_temp_c = None 40 41 def CanMonitorPower(self): 42 raise NotImplementedError() 43 44 def StartMonitoringPower(self, browser): 45 self._CheckStart() 46 self._start_energy_j = self._PackageEnergyJoules() 47 self._start_temp_c = self._TemperatureCelsius() 48 49 def StopMonitoringPower(self): 50 self._CheckStop() 51 energy_consumption_j = self._PackageEnergyJoules() - self._start_energy_j 52 average_temp_c = (self._TemperatureCelsius() + self._start_temp_c) / 2. 53 if energy_consumption_j < 0: # Correct overflow. 54 # The energy portion of the MSR is 4 bytes. 55 energy_consumption_j += 2 ** 32 * self._EnergyMultiplier() 56 57 self._start_energy_j = None 58 self._start_temp_c = None 59 60 return { 61 'identifier': 'msr', 62 'energy_consumption_mwh': _JoulesToMilliwattHours(energy_consumption_j), 63 'platform_info': { 64 'average_temperature_c': average_temp_c, 65 }, 66 } 67 68 @decorators.Cache 69 def _EnergyMultiplier(self): 70 return 0.5 ** self._backend.ReadMsr(MSR_RAPL_POWER_UNIT, 8, 5) 71 72 def _PackageEnergyJoules(self): 73 return (self._backend.ReadMsr(MSR_PKG_ENERGY_STATUS, 0, 32) * 74 self._EnergyMultiplier()) 75 76 def _TemperatureCelsius(self): 77 tcc_activation_temp = self._backend.ReadMsr(IA32_TEMPERATURE_TARGET, 16, 7) 78 if tcc_activation_temp <= 0: 79 tcc_activation_temp = 105 80 package_temp_headroom = self._backend.ReadMsr( 81 IA32_PACKAGE_THERM_STATUS, 16, 7) 82 return tcc_activation_temp - package_temp_headroom 83 84 def _CheckMSRs(self): 85 try: 86 if self._PackageEnergyJoules() <= 0: 87 logging.info('Cannot monitor power: no energy readings.') 88 return False 89 90 if self._TemperatureCelsius() <= 0: 91 logging.info('Cannot monitor power: no temperature readings.') 92 return False 93 except OSError as e: 94 logging.info('Cannot monitor power: %s' % e) 95 return False 96 return True 97 98 99class MsrPowerMonitorLinux(MsrPowerMonitor): 100 def CanMonitorPower(self): 101 vendor = None 102 family = None 103 model = None 104 cpuinfo = open('/proc/cpuinfo').read().splitlines() 105 for line in cpuinfo: 106 if vendor and family and model: 107 break 108 if line.startswith('vendor_id'): 109 vendor = line.split('\t')[1] 110 elif line.startswith('cpu family'): 111 family = int(line.split(' ')[2]) 112 elif line.startswith('model\t\t'): 113 model = int(line.split(' ')[1]) 114 if not _IsSandyBridgeOrLater(vendor, family, model): 115 logging.info('Cannot monitor power: pre-Sandy Bridge CPU.') 116 return False 117 118 if not self._CheckMSRs(): 119 logging.info('Try running tools/telemetry/build/linux_setup_msr.py.') 120 return False 121 122 return True 123 124 125class MsrPowerMonitorWin(MsrPowerMonitor): 126 def CanMonitorPower(self): 127 family, model = map(int, re.match('.+ Family ([0-9]+) Model ([0-9]+)', 128 platform.processor()).groups()) 129 if not _IsSandyBridgeOrLater(platform.processor(), family, model): 130 logging.info('Cannot monitor power: pre-Sandy Bridge CPU.') 131 return False 132 133 try: 134 return self._CheckMSRs() 135 finally: 136 # Since _CheckMSRs() starts the MSR server on win platform, we must close 137 # it after checking to avoid leaking msr server process. 138 self._backend.CloseMsrServer() 139 140 def StopMonitoringPower(self): 141 power_statistics = super(MsrPowerMonitorWin, self).StopMonitoringPower() 142 self._backend.CloseMsrServer() 143 return power_statistics 144