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