Home | History | Annotate | Download | only in power_SuspendStress
      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 
      5 import logging, numpy, random, time
      6 
      7 from autotest_lib.client.bin import test, utils
      8 from autotest_lib.client.common_lib import error
      9 from autotest_lib.client.common_lib.cros.network import interface
     10 from autotest_lib.client.cros.power import power_suspend, sys_power
     11 
     12 class 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 run_once(self):
     60         time.sleep(self._init_delay)
     61         self._suspender = power_suspend.Suspender(
     62                 self.resultsdir, method=self._method,
     63                 suspend_state=self._suspend_state)
     64         # Find the interface which is used for most communication.
     65         # We assume the interface connects to the gateway and has the lowest
     66         # metric.
     67         if self._check_connection:
     68             interface_choices={}
     69             with open('/proc/net/route') as fh:
     70                 for line in fh:
     71                     fields = line.strip().split()
     72                     if fields[1] != '00000000' or not int(fields[3], 16) & 2:
     73                         continue
     74                     interface_choices[fields[0]] = fields[6]
     75             iface = interface.Interface(min(interface_choices))
     76 
     77         while not self._done():
     78             time.sleep(self._min_resume +
     79                        random.randint(0, self._max_resume_window))
     80             # Check the network interface to the caller is still available
     81             if self._check_connection:
     82                 # Give a 10 second window for the network to come back.
     83                 try:
     84                     utils.poll_for_condition(iface.is_link_operational,
     85                                              desc='Link is operational')
     86                 except utils.TimeoutError:
     87                     logging.error('Link to the server gone, reboot')
     88                     utils.system('reboot')
     89 
     90             self._suspender.suspend(random.randint(0, 3) + self._min_suspend)
     91 
     92 
     93     def postprocess_iteration(self):
     94         if self._suspender.successes:
     95             keyvals = {'suspend_iterations': len(self._suspender.successes)}
     96             for key in self._suspender.successes[0]:
     97                 values = [result[key] for result in self._suspender.successes]
     98                 keyvals[key + '_mean'] = numpy.mean(values)
     99                 keyvals[key + '_stddev'] = numpy.std(values)
    100                 keyvals[key + '_min'] = numpy.amin(values)
    101                 keyvals[key + '_max'] = numpy.amax(values)
    102             self.write_perf_keyval(keyvals)
    103         if self._suspender.failures:
    104             total = len(self._suspender.failures)
    105             iterations = len(self._suspender.successes) + total
    106             timeout = kernel = firmware = spurious = 0
    107             for failure in self._suspender.failures:
    108                 if type(failure) is sys_power.SuspendTimeout: timeout += 1
    109                 if type(failure) is sys_power.KernelError: kernel += 1
    110                 if type(failure) is sys_power.FirmwareError: firmware += 1
    111                 if type(failure) is sys_power.SpuriousWakeupError: spurious += 1
    112             if total == kernel + timeout:
    113                 raise error.TestWarn('%d non-fatal suspend failures in %d '
    114                         'iterations (%d timeouts, %d kernel warnings)' %
    115                         (total, iterations, timeout, kernel))
    116             if total == 1:
    117                 # just throw it as is, makes aggregation on dashboards easier
    118                 raise self._suspender.failures[0]
    119             raise error.TestFail('%d suspend failures in %d iterations (%d '
    120                     'timeouts, %d kernel warnings, %d firmware errors, %d '
    121                     'spurious wakeups)' %
    122                     (total, iterations, timeout, kernel, firmware, spurious))
    123 
    124 
    125     def cleanup(self):
    126         """
    127         Clean this up before we wait ages for all the log copying to finish...
    128         """
    129         self._suspender.finalize()
    130