xref: /aosp_15_r20/external/autotest/client/cros/perf.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright 2014 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Liimport logging, threading, time
6*9c5db199SXin Li
7*9c5db199SXin Lifrom autotest_lib.client.bin import utils
8*9c5db199SXin Lifrom autotest_lib.client.cros import service_stopper
9*9c5db199SXin Li
10*9c5db199SXin Li
11*9c5db199SXin Liclass PerfControl(object):
12*9c5db199SXin Li    """
13*9c5db199SXin Li    Provides methods for setting the performance mode of a device.
14*9c5db199SXin Li
15*9c5db199SXin Li    In particular it verifies the machine is idle and cold and tries to set
16*9c5db199SXin Li    it into a consistent, high performance state during initialization.
17*9c5db199SXin Li
18*9c5db199SXin Li    Furthermore it monitors the state of the machine (in particular
19*9c5db199SXin Li    temperature) and verifies that nothing bad happened along the way.
20*9c5db199SXin Li
21*9c5db199SXin Li    Example usage:
22*9c5db199SXin Li
23*9c5db199SXin Li    with PerfControl() as pc:
24*9c5db199SXin Li        if not pc.verify_is_valid():
25*9c5db199SXin Li            raise error.TestError(pc.get_error_reason())
26*9c5db199SXin Li        # Do all performance testing.
27*9c5db199SXin Li        ...
28*9c5db199SXin Li        if not pc.verify_is_valid():
29*9c5db199SXin Li            raise error.TestError(pc.get_error_reason())
30*9c5db199SXin Li    """
31*9c5db199SXin Li    def __init__(self):
32*9c5db199SXin Li        self._service_stopper = None
33*9c5db199SXin Li        # Keep a copy of the current state for cleanup.
34*9c5db199SXin Li        self._temperature_init = utils.get_current_temperature_max()
35*9c5db199SXin Li        self._throttle_count = 0
36*9c5db199SXin Li        self._original_governors = utils.set_high_performance_mode()
37*9c5db199SXin Li        self._error_reason = None
38*9c5db199SXin Li        if not utils.wait_for_idle_cpu(60.0, 0.1):
39*9c5db199SXin Li            self._error_reason = 'Could not get idle CPU.'
40*9c5db199SXin Li            return
41*9c5db199SXin Li        if not utils.wait_for_cool_machine():
42*9c5db199SXin Li            self._error_reason = 'Could not get cold machine.'
43*9c5db199SXin Li            return
44*9c5db199SXin Li        self._temperature_cold = utils.get_current_temperature_max()
45*9c5db199SXin Li        self._temperature_max = self._temperature_cold
46*9c5db199SXin Li        threading.Thread(target=self._monitor_performance_state).start()
47*9c5db199SXin Li        # Should be last just in case we had a runaway process.
48*9c5db199SXin Li        self._stop_thermal_throttling()
49*9c5db199SXin Li
50*9c5db199SXin Li
51*9c5db199SXin Li    def __enter__(self):
52*9c5db199SXin Li        return self
53*9c5db199SXin Li
54*9c5db199SXin Li
55*9c5db199SXin Li    def __exit__(self, _type, value, traceback):
56*9c5db199SXin Li        # First thing restart thermal management.
57*9c5db199SXin Li        self._restore_thermal_throttling()
58*9c5db199SXin Li        utils.restore_scaling_governor_states(self._original_governors)
59*9c5db199SXin Li
60*9c5db199SXin Li
61*9c5db199SXin Li    def get_error_reason(self):
62*9c5db199SXin Li        """
63*9c5db199SXin Li        Returns an error reason string if we encountered problems to pass
64*9c5db199SXin Li        on to harness/wmatrix.
65*9c5db199SXin Li        """
66*9c5db199SXin Li        return self._error_reason
67*9c5db199SXin Li
68*9c5db199SXin Li
69*9c5db199SXin Li    def verify_is_valid(self):
70*9c5db199SXin Li        """
71*9c5db199SXin Li        For now we declare performance results as valid if
72*9c5db199SXin Li        - we did not have an error before.
73*9c5db199SXin Li        - the monitoring thread never saw temperatures a throttle
74*9c5db199SXin Li
75*9c5db199SXin Li        TODO(ihf): Search log files for thermal throttling messages like in
76*9c5db199SXin Li                   src/build/android/pylib/perf/thermal_throttle.py
77*9c5db199SXin Li        """
78*9c5db199SXin Li        if self._error_reason:
79*9c5db199SXin Li            return False
80*9c5db199SXin Li        logging.info("Max observed temperature = %.1f'C (throttle_count=%d)",
81*9c5db199SXin Li                     self._temperature_max, self._throttle_count)
82*9c5db199SXin Li        if (self._throttle_count):
83*9c5db199SXin Li            self._error_reason = 'Machine got hot during testing.'
84*9c5db199SXin Li            return False
85*9c5db199SXin Li        return True
86*9c5db199SXin Li
87*9c5db199SXin Li
88*9c5db199SXin Li    def _monitor_performance_state(self):
89*9c5db199SXin Li        """
90*9c5db199SXin Li        Checks machine temperature once per second.
91*9c5db199SXin Li        TODO(ihf): make this more intelligent with regards to governor,
92*9c5db199SXin Li                   CPU, GPU and maybe zram as needed.
93*9c5db199SXin Li        """
94*9c5db199SXin Li        while True:
95*9c5db199SXin Li            time.sleep(1)
96*9c5db199SXin Li            current_temperature = utils.get_current_temperature_max()
97*9c5db199SXin Li            self._temperature_max = max(self._temperature_max,
98*9c5db199SXin Li                                        current_temperature)
99*9c5db199SXin Li            is_throttled = utils.is_system_thermally_throttled()
100*9c5db199SXin Li            if is_throttled:
101*9c5db199SXin Li                self._throttle_count += 1
102*9c5db199SXin Li
103*9c5db199SXin Li            # TODO(ihf): Remove this spew once PerfControl is stable.
104*9c5db199SXin Li            logging.info('PerfControl system temperature = %.1f, throttled=%s',
105*9c5db199SXin Li                          current_temperature, is_throttled)
106*9c5db199SXin Li
107*9c5db199SXin Li
108*9c5db199SXin Li    def _stop_thermal_throttling(self):
109*9c5db199SXin Li        """
110*9c5db199SXin Li        If exist on the platform/machine it stops the different thermal
111*9c5db199SXin Li        throttling scripts from running.
112*9c5db199SXin Li        Warning: this risks abnormal behavior if machine runs in high load.
113*9c5db199SXin Li        """
114*9c5db199SXin Li        self._service_stopper = service_stopper.get_thermal_service_stopper()
115*9c5db199SXin Li        self._service_stopper.stop_services()
116*9c5db199SXin Li
117*9c5db199SXin Li
118*9c5db199SXin Li    def _restore_thermal_throttling(self):
119*9c5db199SXin Li        """
120*9c5db199SXin Li        Restores the original thermal throttling state.
121*9c5db199SXin Li        """
122*9c5db199SXin Li        if self._service_stopper:
123*9c5db199SXin Li            self._service_stopper.restore_services()
124