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