• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2013 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.
4
5import logging, numpy, random, time
6
7from autotest_lib.client.bin import test, utils
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.common_lib.cros.network import interface
10from autotest_lib.client.cros.power import power_suspend, sys_power
11
12class power_SuspendStress(test.test):
13    """Class for test."""
14    version = 1
15
16    def initialize(self, duration, idle=False, init_delay=0, min_suspend=0,
17                   min_resume=5, max_resume_window=3, check_connection=True,
18                   iterations=None, suspend_state=''):
19        """
20        Entry point.
21
22        @param duration: total run time of the test
23        @param idle: use sys_power.idle_suspend method.
24                (use with dummy_IdleSuspend)
25        @param init_delay: wait this many seconds before starting the test to
26                give parallel tests time to get started
27        @param min_suspend: suspend durations will be chosen randomly out of
28                the interval between min_suspend and min_suspend + 3 seconds.
29        @param min_resume: minimal time in seconds between suspends.
30        @param max_resume_window: maximum range to use between suspends. i.e.,
31                we will stay awake between min_resume and min_resume +
32                max_resume_window seconds.
33        @param check_connection: If true, we check that the network interface
34                used for testing is up after resume. Otherwsie we reboot.
35        @param iterations: number of times to attempt suspend.  If !=None has
36                precedence over duration.
37        @param suspend_state: Force to suspend to a specific
38                state ("mem" or "freeze"). If the string is empty, suspend
39                state is left to the default pref on the system.
40        """
41        self._endtime = time.time()
42        if duration:
43            self._endtime += duration
44        self._init_delay = init_delay
45        self._min_suspend = min_suspend
46        self._min_resume = min_resume
47        self._max_resume_window = max_resume_window
48        self._check_connection = check_connection
49        self._iterations = iterations
50        self._suspend_state = suspend_state
51        self._method = sys_power.idle_suspend if idle else sys_power.do_suspend
52
53    def _done(self):
54        if self._iterations != None:
55            self._iterations -= 1
56            return self._iterations < 0
57        return time.time() >= self._endtime
58
59    def _get_default_network_interface(self):
60        interface_choices={}
61        with open('/proc/net/route') as fh:
62            for line in fh:
63                fields = line.strip().split()
64                if fields[1] != '00000000' or not int(fields[3], 16) & 2:
65                    continue
66                interface_choices[fields[0]] = int(fields[6])
67        if not interface_choices:
68            return None
69
70        return interface.Interface(min(interface_choices,
71            key=interface_choices.get))
72
73    def run_once(self):
74        time.sleep(self._init_delay)
75        self._suspender = power_suspend.Suspender(
76                self.resultsdir, method=self._method,
77                suspend_state=self._suspend_state)
78        # Find the interface which is used for most communication.
79        # We assume the interface connects to the gateway and has the lowest
80        # metric.
81        if self._check_connection:
82            iface = utils.poll_for_condition(
83                        self._get_default_network_interface,
84                        desc='Find default network interface')
85            logging.info('Found default network interface: %s', iface.name)
86
87        while not self._done():
88            time.sleep(self._min_resume +
89                       random.randint(0, self._max_resume_window))
90            # Check the network interface to the caller is still available
91            if self._check_connection:
92                # Give a 10 second window for the network to come back.
93                try:
94                    utils.poll_for_condition(iface.is_link_operational,
95                                             desc='Link is operational')
96                except utils.TimeoutError:
97                    logging.error('Link to the server gone, reboot')
98                    utils.system('reboot')
99                    # Reboot may return; raise a TestFail() to abort too, even
100                    # though the server likely won't see this.
101                    raise error.TestFail('Link is gone; rebooting')
102
103            self._suspender.suspend(random.randint(0, 3) + self._min_suspend)
104
105
106    def postprocess_iteration(self):
107        if self._suspender.successes:
108            keyvals = {'suspend_iterations': len(self._suspender.successes)}
109            for key in self._suspender.successes[0]:
110                values = [result[key] for result in self._suspender.successes]
111                keyvals[key + '_mean'] = numpy.mean(values)
112                keyvals[key + '_stddev'] = numpy.std(values)
113                keyvals[key + '_min'] = numpy.amin(values)
114                keyvals[key + '_max'] = numpy.amax(values)
115            self.write_perf_keyval(keyvals)
116        if self._suspender.failures:
117            total = len(self._suspender.failures)
118            iterations = len(self._suspender.successes) + total
119            timeout = kernel = firmware = spurious = 0
120            for failure in self._suspender.failures:
121                if type(failure) is sys_power.SuspendTimeout: timeout += 1
122                if type(failure) is sys_power.KernelError: kernel += 1
123                if type(failure) is sys_power.FirmwareError: firmware += 1
124                if type(failure) is sys_power.SpuriousWakeupError: spurious += 1
125            if total == kernel + timeout:
126                raise error.TestWarn('%d non-fatal suspend failures in %d '
127                        'iterations (%d timeouts, %d kernel warnings)' %
128                        (total, iterations, timeout, kernel))
129            if total == 1:
130                # just throw it as is, makes aggregation on dashboards easier
131                raise self._suspender.failures[0]
132            raise error.TestFail('%d suspend failures in %d iterations (%d '
133                    'timeouts, %d kernel warnings, %d firmware errors, %d '
134                    'spurious wakeups)' %
135                    (total, iterations, timeout, kernel, firmware, spurious))
136
137
138    def cleanup(self):
139        """
140        Clean this up before we wait ages for all the log copying to finish...
141        """
142        self._suspender.finalize()
143