1# Copyright 2013 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
6from pylib import android_commands
7from pylib.device import device_utils
8
9
10class OmapThrottlingDetector(object):
11  """Class to detect and track thermal throttling on an OMAP 4."""
12  OMAP_TEMP_FILE = ('/sys/devices/platform/omap/omap_temp_sensor.0/'
13                    'temperature')
14
15  @staticmethod
16  def IsSupported(device):
17    return device.FileExists(OmapThrottlingDetector.OMAP_TEMP_FILE)
18
19  def __init__(self, device):
20    self._device = device
21
22  @staticmethod
23  def BecameThrottled(log_line):
24    return 'omap_thermal_throttle' in log_line
25
26  @staticmethod
27  def BecameUnthrottled(log_line):
28    return 'omap_thermal_unthrottle' in log_line
29
30  @staticmethod
31  def GetThrottlingTemperature(log_line):
32    if 'throttle_delayed_work_fn' in log_line:
33      return float([s for s in log_line.split() if s.isdigit()][0]) / 1000.0
34
35  def GetCurrentTemperature(self):
36    tempdata = self._device.ReadFile(OmapThrottlingDetector.OMAP_TEMP_FILE)
37    return float(tempdata[0]) / 1000.0
38
39
40class ExynosThrottlingDetector(object):
41  """Class to detect and track thermal throttling on an Exynos 5."""
42  @staticmethod
43  def IsSupported(device):
44    return device.FileExists('/sys/bus/exynos5-core')
45
46  def __init__(self, device):
47    pass
48
49  @staticmethod
50  def BecameThrottled(log_line):
51    return 'exynos_tmu: Throttling interrupt' in log_line
52
53  @staticmethod
54  def BecameUnthrottled(log_line):
55    return 'exynos_thermal_unthrottle: not throttling' in log_line
56
57  @staticmethod
58  def GetThrottlingTemperature(_log_line):
59    return None
60
61  @staticmethod
62  def GetCurrentTemperature():
63    return None
64
65
66class ThermalThrottle(object):
67  """Class to detect and track thermal throttling.
68
69  Usage:
70    Wait for IsThrottled() to be False before running test
71    After running test call HasBeenThrottled() to find out if the
72    test run was affected by thermal throttling.
73  """
74
75  def __init__(self, device):
76    # TODO(jbudorick) Remove once telemetry gets switched over.
77    if isinstance(device, android_commands.AndroidCommands):
78      device = device_utils.DeviceUtils(device)
79    self._device = device
80    self._throttled = False
81    self._detector = None
82    if OmapThrottlingDetector.IsSupported(device):
83      self._detector = OmapThrottlingDetector(device)
84    elif ExynosThrottlingDetector.IsSupported(device):
85      self._detector = ExynosThrottlingDetector(device)
86
87  def HasBeenThrottled(self):
88    """True if there has been any throttling since the last call to
89       HasBeenThrottled or IsThrottled.
90    """
91    return self._ReadLog()
92
93  def IsThrottled(self):
94    """True if currently throttled."""
95    self._ReadLog()
96    return self._throttled
97
98  def _ReadLog(self):
99    if not self._detector:
100      return False
101    has_been_throttled = False
102    serial_number = str(self._device)
103    log = self._device.RunShellCommand('dmesg -c')
104    degree_symbol = unichr(0x00B0)
105    for line in log:
106      if self._detector.BecameThrottled(line):
107        if not self._throttled:
108          logging.warning('>>> Device %s thermally throttled', serial_number)
109        self._throttled = True
110        has_been_throttled = True
111      elif self._detector.BecameUnthrottled(line):
112        if self._throttled:
113          logging.warning('>>> Device %s thermally unthrottled', serial_number)
114        self._throttled = False
115        has_been_throttled = True
116      temperature = self._detector.GetThrottlingTemperature(line)
117      if temperature is not None:
118        logging.info(u'Device %s thermally throttled at %3.1f%sC',
119                     serial_number, temperature, degree_symbol)
120
121    if logging.getLogger().isEnabledFor(logging.DEBUG):
122      # Print current temperature of CPU SoC.
123      temperature = self._detector.GetCurrentTemperature()
124      if temperature is not None:
125        logging.debug(u'Current SoC temperature of %s = %3.1f%sC',
126                      serial_number, temperature, degree_symbol)
127
128      # Print temperature of battery, to give a system temperature
129      dumpsys_log = self._device.RunShellCommand('dumpsys battery')
130      for line in dumpsys_log:
131        if 'temperature' in line:
132          btemp = float([s for s in line.split() if s.isdigit()][0]) / 10.0
133          logging.debug(u'Current battery temperature of %s = %3.1f%sC',
134                        serial_number, btemp, degree_symbol)
135
136    return has_been_throttled
137
138