aboutsummaryrefslogtreecommitdiff
path: root/catapult/devil/devil/android/cpu_temperature.py
blob: 58ce87a05236faf77739fe38bb398d8cc6c60262 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# Copyright 2019 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.
"""Provides device interactions for CPU temperature monitoring."""
# pylint: disable=unused-argument

import logging

from devil.android import device_utils
from devil.android.perf import perf_control
from devil.utils import timeout_retry

logger = logging.getLogger(__name__)

# NB: when adding devices to this structure, be aware of the impact it may
# have on the chromium.perf waterfall, as it may increase testing time.
# Please contact a person responsible for the waterfall to see if the
# device you're adding is currently being tested.
_DEVICE_THERMAL_INFORMATION = {
    # Pixel 3
    'blueline': {
        'cpu_temps': {
            # See /sys/class/thermal/thermal_zone<number>/type for description
            # Types:
            # cpu0: cpu0-silver-step
            # cpu1: cpu1-silver-step
            # cpu2: cpu2-silver-step
            # cpu3: cpu3-silver-step
            # cpu4: cpu0-gold-step
            # cpu5: cpu1-gold-step
            # cpu6: cpu2-gold-step
            # cpu7: cpu3-gold-step
            'cpu0': '/sys/class/thermal/thermal_zone11/temp',
            'cpu1': '/sys/class/thermal/thermal_zone12/temp',
            'cpu2': '/sys/class/thermal/thermal_zone13/temp',
            'cpu3': '/sys/class/thermal/thermal_zone14/temp',
            'cpu4': '/sys/class/thermal/thermal_zone15/temp',
            'cpu5': '/sys/class/thermal/thermal_zone16/temp',
            'cpu6': '/sys/class/thermal/thermal_zone17/temp',
            'cpu7': '/sys/class/thermal/thermal_zone18/temp'
        },
        # Different device sensors use different multipliers
        # e.g. Pixel 3 35 degrees c is 35000
        'temp_multiplier': 1000
    },
    # Pixel
    'sailfish': {
        'cpu_temps': {
            # The following thermal zones tend to produce the most accurate
            # readings
            # Types:
            # cpu0: tsens_tz_sensor0
            # cpu1: tsens_tz_sensor1
            # cpu2: tsens_tz_sensor2
            # cpu3: tsens_tz_sensor3
            'cpu0': '/sys/class/thermal/thermal_zone1/temp',
            'cpu1': '/sys/class/thermal/thermal_zone2/temp',
            'cpu2': '/sys/class/thermal/thermal_zone3/temp',
            'cpu3': '/sys/class/thermal/thermal_zone4/temp'
        },
        'temp_multiplier': 10
    }
}


class CpuTemperature(object):

  def __init__(self, device):
    """CpuTemperature constructor.

      Args:
        device: A DeviceUtils instance.
      Raises:
        TypeError: If it is not passed a DeviceUtils instance.
    """
    if not isinstance(device, device_utils.DeviceUtils):
      raise TypeError('Must be initialized with DeviceUtils object.')
    self._device = device
    self._perf_control = perf_control.PerfControl(self._device)
    self._device_info = None

  def InitThermalDeviceInformation(self):
    """Init the current devices thermal information.
    """
    self._device_info = _DEVICE_THERMAL_INFORMATION.get(
                        self._device.build_product)

  def IsSupported(self):
    """Check if the current device is supported.

      Returns:
        True if the device is in _DEVICE_THERMAL_INFORMATION and the temp
        files exist. False otherwise.
    """
    # Init device info if it hasnt been manually initialised already
    if self._device_info is None:
      self.InitThermalDeviceInformation()

    if self._device_info is not None:
      return all(
          self._device.FileExists(f)
          for f in self._device_info['cpu_temps'].values())
    return False

  def LetCpuCoolToTemperature(self, target_temp, wait_period=30):
    """Lets device sit to give CPU time to cool down.

      Implements a similar mechanism to
      battery_utils.LetBatteryCoolToTemperature

      Args:
        temp: A float containing the maximum temperature to allow
          in degrees c.
        wait_period: An integer indicating time in seconds to wait
          between checking.
    """
    target_temp = int(target_temp * self._device_info['temp_multiplier'])

    def cool_cpu():
      # Get the temperatures
      cpu_temp_paths = self._device_info['cpu_temps']
      temps = []
      for temp_path in cpu_temp_paths.values():
        temp_return = self._device.ReadFile(temp_path)
        # Output is an array of strings, only need the first line.
        temps.append(int(temp_return))

      if not temps:
        logger.warning('Unable to read temperature files provided.')
        return True

      logger.info('Current CPU temperatures: %s', str(temps)[1:-1])

      return all(t <= target_temp for t in temps)

    logger.info('Waiting for the CPU to cool down to %s',
                target_temp / self._device_info['temp_multiplier'])

    # Set the governor to powersave to aid the cooling down of the CPU
    self._perf_control.SetScalingGovernor('powersave')

    # Retry 3 times, each time waiting 30 seconds.
    # This negates most (if not all) of the noise in recorded results without
    # taking too long
    timeout_retry.WaitFor(cool_cpu, wait_period=wait_period, max_tries=3)

    # Set the performance mode
    self._perf_control.SetHighPerfMode()

  def GetDeviceForTesting(self):
    return self._device

  def GetDeviceInfoForTesting(self):
    return self._device_info