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