• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 The Chromium OS 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.
4import logging
5import time
6
7from autotest_lib.client.bin import test
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.cros import service_stopper
10from autotest_lib.client.cros.power import power_dashboard
11from autotest_lib.client.cros.power import power_rapl
12from autotest_lib.client.cros.power import power_status
13from autotest_lib.client.cros.power import power_telemetry_utils
14from autotest_lib.client.cros.power import power_utils
15
16
17class power_Test(test.test):
18    """Optional base class power related tests."""
19    version = 1
20
21    def initialize(self, seconds_period=20., pdash_note='',
22                   force_discharge=False):
23        """Perform necessary initialization prior to power test run.
24
25        @param seconds_period: float of probing interval in seconds.
26        @param pdash_note: note of the current run to send to power dashboard.
27        @param force_discharge: force battery to discharge during the test.
28
29        @var backlight: power_utils.Backlight object.
30        @var keyvals: dictionary of result keyvals.
31        @var status: power_status.SysStat object.
32
33        @var _checkpoint_logger: power_status.CheckpointLogger to track
34                                 checkpoint data.
35        @var _plog: power_status.PowerLogger object to monitor power.
36        @var _psr: power_utils.DisplayPanelSelfRefresh object to monitor PSR.
37        @var _services: service_stopper.ServiceStopper object.
38        @var _start_time: float of time in seconds since Epoch test started.
39        @var _stats: power_status.StatoMatic object.
40        @var _tlog: power_status.TempLogger object to monitor temperatures.
41        @var _clog: power_status.CPUStatsLogger object to monitor CPU(s)
42                    frequencies and c-states.
43        @var _meas_logs: list of power_status.MeasurementLoggers
44        """
45        super(power_Test, self).initialize()
46        self.backlight = power_utils.Backlight()
47        self.backlight.set_default()
48        self.keyvals = dict()
49        self.status = power_status.get_status()
50
51        self._checkpoint_logger = power_status.CheckpointLogger()
52        self._seconds_period = seconds_period
53
54        measurements = []
55
56        self._force_discharge = force_discharge
57        if force_discharge:
58            if not self.status.battery:
59                raise error.TestNAError('DUT does not have battery. '
60                                        'Could not force discharge.')
61            if not power_utils.charge_control_by_ectool(False):
62                raise error.TestError('Could not run battery force discharge.')
63
64        if force_discharge or not self.status.on_ac():
65            measurements.append(
66                power_status.SystemPower(self.status.battery_path))
67        if power_utils.has_powercap_support():
68            measurements += power_rapl.create_powercap()
69        elif power_utils.has_rapl_support():
70            measurements += power_rapl.create_rapl()
71        self._plog = power_status.PowerLogger(measurements,
72                seconds_period=seconds_period,
73                checkpoint_logger=self._checkpoint_logger)
74        self._psr = power_utils.DisplayPanelSelfRefresh()
75        self._services = service_stopper.ServiceStopper(
76                service_stopper.ServiceStopper.POWER_DRAW_SERVICES)
77        self._services.stop_services()
78        self._stats = power_status.StatoMatic()
79
80        self._tlog = power_status.TempLogger([],
81                seconds_period=seconds_period,
82                checkpoint_logger=self._checkpoint_logger)
83        self._clog = power_status.CPUStatsLogger(seconds_period=seconds_period,
84                checkpoint_logger=self._checkpoint_logger)
85
86        self._meas_logs = [self._plog, self._tlog, self._clog]
87
88        self._pdash_note = pdash_note
89
90    def warmup(self, warmup_time=30):
91        """Warm up.
92
93        Wait between initialization and run_once for new settings to stabilize.
94
95        @param warmup_time: integer of seconds to warmup.
96        """
97        time.sleep(warmup_time)
98
99    def start_measurements(self):
100        """Start measurements."""
101        for log in self._meas_logs:
102            log.start()
103        self._start_time = time.time()
104        if self.status.battery:
105            self._start_energy = self.status.battery.energy
106        power_telemetry_utils.start_measurement()
107
108    def loop_sleep(self, loop, sleep_secs):
109        """Jitter free sleep.
110
111        @param loop: integer of loop (1st is zero).
112        @param sleep_secs: integer of desired sleep seconds.
113        """
114        next_time = self._start_time + (loop + 1) * sleep_secs
115        time.sleep(next_time - time.time())
116
117    def checkpoint_measurements(self, name, start_time=None):
118        """Checkpoint measurements.
119
120        @param name: string name of measurement being checkpointed.
121        @param start_time: float of time in seconds since Epoch that
122                measurements being checkpointed began.
123        """
124        if not start_time:
125            start_time = self._start_time
126        self.status.refresh()
127        self._checkpoint_logger.checkpoint(name, start_time)
128        self._psr.refresh()
129
130    def publish_keyvals(self):
131        """Publish power result keyvals."""
132        keyvals = self._stats.publish()
133        keyvals['level_backlight_max'] = self.backlight.get_max_level()
134        keyvals['level_backlight_current'] = self.backlight.get_level()
135
136        # record battery stats if not on AC
137        if not self._force_discharge and self.status.on_ac():
138            keyvals['b_on_ac'] = 1
139        else:
140            keyvals['b_on_ac'] = 0
141
142        if self.status.battery:
143            keyvals['ah_charge_full'] = self.status.battery.charge_full
144            keyvals['ah_charge_full_design'] = \
145                                self.status.battery.charge_full_design
146            keyvals['ah_charge_now'] = self.status.battery.charge_now
147            keyvals['a_current_now'] = self.status.battery.current_now
148            keyvals['wh_energy'] = self.status.battery.energy
149            energy_used = self._start_energy - self.status.battery.energy
150            runtime_minutes = (time.time() - self._start_time) / 60.
151            keyvals['wh_energy_used'] = energy_used
152            keyvals['minutes_tested'] = runtime_minutes
153            if energy_used > 0 and runtime_minutes > 1:
154                keyvals['w_energy_rate'] = energy_used * 60 / runtime_minutes
155            keyvals['v_voltage_min_design'] = \
156                                self.status.battery.voltage_min_design
157            keyvals['v_voltage_now'] = self.status.battery.voltage_now
158
159        for log in self._meas_logs:
160            keyvals.update(log.calc())
161        keyvals.update(self._psr.get_keyvals())
162
163        self.keyvals.update(keyvals)
164
165        core_keyvals = power_utils.get_core_keyvals(self.keyvals)
166        self.write_perf_keyval(core_keyvals)
167
168    def publish_dashboard(self):
169        """Report results to chromeperf & power dashboard."""
170
171        self.publish_keyvals()
172
173        # publish power values
174        for key, values in self.keyvals.iteritems():
175            if key.endswith('pwr_avg'):
176                self.output_perf_value(description=key, value=values, units='W',
177                                   higher_is_better=False, graph='power')
178
179        # publish temperature values
180        for key, values in self.keyvals.iteritems():
181            if key.endswith('temp_avg'):
182                self.output_perf_value(description=key, value=values, units='C',
183                                   higher_is_better=False, graph='temperature')
184
185        # publish to power dashboard
186        pdash = power_dashboard.PowerLoggerDashboard(
187            self._plog, self.tagged_testname, self.resultsdir,
188            note=self._pdash_note)
189        pdash.upload()
190        cdash = power_dashboard.CPUStatsLoggerDashboard(
191            self._clog, self.tagged_testname, self.resultsdir,
192            note=self._pdash_note)
193        cdash.upload()
194        tdash = power_dashboard.TempLoggerDashboard(
195            self._tlog, self.tagged_testname, self.resultsdir,
196            note=self._pdash_note)
197        tdash.upload()
198
199    def _save_results(self):
200        """Save results of each logger in resultsdir."""
201        for log in self._meas_logs:
202            log.save_results(self.resultsdir)
203        self._checkpoint_logger.save_checkpoint_data(self.resultsdir)
204
205    def postprocess_iteration(self):
206        """Write keyval and send data to dashboard."""
207        power_telemetry_utils.end_measurement()
208        self.status.refresh()
209        for log in self._meas_logs:
210            log.done = True
211        super(power_Test, self).postprocess_iteration()
212        self.publish_dashboard()
213        self._save_results()
214
215    def cleanup(self):
216        """Reverse setting change in initialization."""
217        if self._force_discharge:
218            if not power_utils.charge_control_by_ectool(True):
219                logging.warn('Can not restore from force discharge.')
220        if self.backlight:
221            self.backlight.restore()
222        self._services.restore_services()
223        super(power_Test, self).cleanup()
224