155 lines
5.3 KiB
Python
155 lines
5.3 KiB
Python
# 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
|