xref: /aosp_15_r20/external/chromium-trace/catapult/devil/devil/android/cpu_temperature.py (revision 1fa4b3da657c0e9ad43c0220bacf9731820715a5)
1# Copyright 2019 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"""Provides device interactions for CPU temperature monitoring."""
5# pylint: disable=unused-argument
6
7import logging
8
9from devil.android import device_utils
10from devil.android.perf import perf_control
11from devil.utils import timeout_retry
12
13logger = logging.getLogger(__name__)
14
15# NB: when adding devices to this structure, be aware of the impact it may
16# have on the chromium.perf waterfall, as it may increase testing time.
17# Please contact a person responsible for the waterfall to see if the
18# device you're adding is currently being tested.
19_DEVICE_THERMAL_INFORMATION = {
20    # Pixel 3
21    'blueline': {
22        'cpu_temps': {
23            # See /sys/class/thermal/thermal_zone<number>/type for description
24            # Types:
25            # cpu0: cpu0-silver-step
26            # cpu1: cpu1-silver-step
27            # cpu2: cpu2-silver-step
28            # cpu3: cpu3-silver-step
29            # cpu4: cpu0-gold-step
30            # cpu5: cpu1-gold-step
31            # cpu6: cpu2-gold-step
32            # cpu7: cpu3-gold-step
33            'cpu0': '/sys/class/thermal/thermal_zone11/temp',
34            'cpu1': '/sys/class/thermal/thermal_zone12/temp',
35            'cpu2': '/sys/class/thermal/thermal_zone13/temp',
36            'cpu3': '/sys/class/thermal/thermal_zone14/temp',
37            'cpu4': '/sys/class/thermal/thermal_zone15/temp',
38            'cpu5': '/sys/class/thermal/thermal_zone16/temp',
39            'cpu6': '/sys/class/thermal/thermal_zone17/temp',
40            'cpu7': '/sys/class/thermal/thermal_zone18/temp'
41        },
42        # Different device sensors use different multipliers
43        # e.g. Pixel 3 35 degrees c is 35000
44        'temp_multiplier': 1000
45    },
46    # Pixel
47    'sailfish': {
48        'cpu_temps': {
49            # The following thermal zones tend to produce the most accurate
50            # readings
51            # Types:
52            # cpu0: tsens_tz_sensor0
53            # cpu1: tsens_tz_sensor1
54            # cpu2: tsens_tz_sensor2
55            # cpu3: tsens_tz_sensor3
56            'cpu0': '/sys/class/thermal/thermal_zone1/temp',
57            'cpu1': '/sys/class/thermal/thermal_zone2/temp',
58            'cpu2': '/sys/class/thermal/thermal_zone3/temp',
59            'cpu3': '/sys/class/thermal/thermal_zone4/temp'
60        },
61        'temp_multiplier': 10
62    }
63}
64
65
66class CpuTemperature(object):
67  def __init__(self, device):
68    """CpuTemperature constructor.
69
70      Args:
71        device: A DeviceUtils instance.
72      Raises:
73        TypeError: If it is not passed a DeviceUtils instance.
74    """
75    if not isinstance(device, device_utils.DeviceUtils):
76      raise TypeError('Must be initialized with DeviceUtils object.')
77    self._device = device
78    self._perf_control = perf_control.PerfControl(self._device)
79    self._device_info = None
80
81  def InitThermalDeviceInformation(self):
82    """Init the current devices thermal information.
83    """
84    self._device_info = _DEVICE_THERMAL_INFORMATION.get(
85        self._device.build_product)
86
87  def IsSupported(self):
88    """Check if the current device is supported.
89
90      Returns:
91        True if the device is in _DEVICE_THERMAL_INFORMATION and the temp
92        files exist. False otherwise.
93    """
94    # Init device info if it hasnt been manually initialised already
95    if self._device_info is None:
96      self.InitThermalDeviceInformation()
97
98    if self._device_info is not None:
99      return all(
100          self._device.FileExists(f)
101          for f in self._device_info['cpu_temps'].values())
102    return False
103
104  def LetCpuCoolToTemperature(self, target_temp, wait_period=30):
105    """Lets device sit to give CPU time to cool down.
106
107      Implements a similar mechanism to
108      battery_utils.LetBatteryCoolToTemperature
109
110      Args:
111        temp: A float containing the maximum temperature to allow
112          in degrees c.
113        wait_period: An integer indicating time in seconds to wait
114          between checking.
115    """
116    target_temp = int(target_temp * self._device_info['temp_multiplier'])
117
118    def cool_cpu():
119      # Get the temperatures
120      cpu_temp_paths = self._device_info['cpu_temps']
121      temps = []
122      for temp_path in cpu_temp_paths.values():
123        temp_return = self._device.ReadFile(temp_path)
124        # Output is an array of strings, only need the first line.
125        temps.append(int(temp_return))
126
127      if not temps:
128        logger.warning('Unable to read temperature files provided.')
129        return True
130
131      logger.info('Current CPU temperatures: %s', str(temps)[1:-1])
132
133      return all(t <= target_temp for t in temps)
134
135    logger.info('Waiting for the CPU to cool down to %s',
136                target_temp / self._device_info['temp_multiplier'])
137
138    # Set the governor to powersave to aid the cooling down of the CPU
139    self._perf_control.SetScalingGovernor('powersave')
140
141    # Retry 3 times, each time waiting 30 seconds.
142    # This negates most (if not all) of the noise in recorded results without
143    # taking too long
144    timeout_retry.WaitFor(cool_cpu, wait_period=wait_period, max_tries=3)
145
146    # Set the performance mode
147    self._perf_control.SetHighPerfMode()
148
149  def GetDeviceForTesting(self):
150    return self._device
151
152  def GetDeviceInfoForTesting(self):
153    return self._device_info
154