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