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