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.cros import power_suspend, sys_power
     10 
     11 class 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