# Copyright 2014 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import logging, threading, time from autotest_lib.client.bin import utils from autotest_lib.client.cros import service_stopper # List of thermal throttling services that should be disabled. # - temp_metrics for link. # - thermal for daisy, snow, pit etc. # TODO(ihf): cpu_quiet on nyan isn't a service. We still need to disable it # on nyan. See crbug.com/357457. _THERMAL_SERVICES = ['temp_metrics', 'thermal'] class PerfControl(object): """ Provides methods for setting the performance mode of a device. In particular it verifies the machine is idle and cold and tries to set it into a consistent, high performance state during initialization. Furthermore it monitors the state of the machine (in particular temperature) and verifies that nothing bad happened along the way. Example usage: with PerfControl() as pc: if not pc.verify_is_valid(): raise error.TestError(pc.get_error_reason()) # Do all performance testing. ... if not pc.verify_is_valid(): raise error.TestError(pc.get_error_reason()) """ def __init__(self): self._service_stopper = None # Keep a copy of the current state for cleanup. self._temperature_init = utils.get_current_temperature_max() self._temperature_critical = utils.get_temperature_critical() self._original_governors = utils.set_high_performance_mode() self._error_reason = None if not utils.wait_for_idle_cpu(60.0, 0.1): self._error_reason = 'Could not get idle CPU.' return if not utils.wait_for_cool_machine(): self._error_reason = 'Could not get cold machine.' return self._temperature_cold = utils.get_current_temperature_max() self._temperature_max = self._temperature_cold threading.Thread(target=self._monitor_performance_state).start() # Should be last just in case we had a runaway process. self._stop_thermal_throttling() def __enter__(self): return self def __exit__(self, _type, value, traceback): # First thing restart thermal management. self._restore_thermal_throttling() utils.restore_scaling_governor_states(self._original_governors) def get_error_reason(self): """ Returns an error reason string if we encountered problems to pass on to harness/wmatrix. """ return self._error_reason def verify_is_valid(self): """ For now we declare performance results as valid if - we did not have an error before. - the monitoring thread never saw temperatures too close to critical. TODO(ihf): Search log files for thermal throttling messages like in src/build/android/pylib/perf/thermal_throttle.py """ if self._error_reason: return False temperature_bad = self._temperature_critical - 1.0 logging.info("Max observed temperature = %.1f'C (bad limit = %.1f'C)", self._temperature_max, temperature_bad) if (self._temperature_max > temperature_bad): self._error_reason = 'Machine got hot during testing.' return False return True def _monitor_performance_state(self): """ Checks machine temperature once per second. TODO(ihf): make this more intelligent with regards to governor, CPU, GPU and maybe zram as needed. """ while True: time.sleep(1) current_temperature = utils.get_current_temperature_max() self._temperature_max = max(self._temperature_max, current_temperature) # TODO(ihf): Remove this spew once PerfControl is stable. logging.info('PerfControl CPU temperature = %.1f', current_temperature) def _stop_thermal_throttling(self): """ If exist on the platform/machine it stops the different thermal throttling scripts from running. Warning: this risks abnormal behavior if machine runs in high load. """ self._service_stopper = service_stopper.ServiceStopper( _THERMAL_SERVICES) def _restore_thermal_throttling(self): """ Restores the original thermal throttling state. """ if self._service_stopper: self._service_stopper.restore_services()