# 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()