1# Lint as: python2, python3 2# Copyright 2018 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import logging 7import time 8 9from autotest_lib.client.bin import test 10from autotest_lib.client.bin import utils 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.common_lib.cros import chrome 13from autotest_lib.client.common_lib.cros import power_load_util 14from autotest_lib.client.cros.power import sys_power 15 16 17# Stop adding tab when swap_free / swap_total is less than this value. 18_LOW_SWAP_THRESHOLD = 0.5 19# Terminate the test if active_tabs / created_tabs is less than this value. 20_TOO_FEW_ACTIVE_TABS_THRESHOLD = 0.33 21 22 23class power_LowMemorySuspend(test.test): 24 """Low memory suspending stress test.""" 25 version = 1 26 27 def low_swap_free(self): 28 """Returns true if free swap is low.""" 29 meminfo = utils.get_meminfo() 30 if meminfo.SwapFree < meminfo.SwapTotal * _LOW_SWAP_THRESHOLD: 31 logging.info("Swap is low, swap free: %d, swap total: %d", 32 meminfo.SwapFree, meminfo.SwapTotal) 33 return True 34 return False 35 36 def create_tabs(self, cr): 37 """Creates tabs until swap free is low. 38 39 @return: list of created tabs 40 """ 41 # Any non-trivial web page is suitable to consume memory. 42 URL = 'https://inbox.google.com/' 43 tabs = [] 44 45 # There is some race condition to navigate the first tab, navigating 46 # the first tab may fail unless sleep 2 seconds before navigation. 47 # Skip the first tab. 48 49 while not self.low_swap_free(): 50 logging.info('creating tab %d', len(tabs)) 51 tab = cr.browser.tabs.New() 52 tabs.append(tab) 53 tab.Navigate(URL); 54 try: 55 tab.WaitForDocumentReadyStateToBeComplete(timeout=20) 56 except Exception as e: 57 logging.warning('Exception when waiting page ready: %s', e) 58 59 return tabs 60 61 def check_tab_discard(self, cr, tabs): 62 """Raises error if too many tabs are discarded.""" 63 try: 64 active_tabs = len(cr.browser.tabs) 65 except Exception as e: 66 logging.info('error getting active tab count: %s', e) 67 return 68 created_tabs = len(tabs) 69 if (active_tabs < created_tabs * _TOO_FEW_ACTIVE_TABS_THRESHOLD): 70 msg = ('Too many discards, active tabs: %d, created tabs: %d' % 71 (active_tabs, created_tabs)) 72 raise error.TestFail(msg) 73 74 def cycling_suspend(self, cr, tabs, switches_per_suspend, 75 total_suspend_duration, suspend_seconds, 76 additional_sleep): 77 """Page cycling and suspending. 78 79 @return: total suspending count. 80 """ 81 start_time = time.time() 82 suspend_count = 0 83 tab_index = 0 84 spurious_wakeup_count = 0 85 MAX_SPURIOUS_WAKEUP = 5 86 87 while time.time() - start_time < total_suspend_duration: 88 # Page cycling 89 for _ in range(switches_per_suspend): 90 try: 91 tabs[tab_index].Activate() 92 tabs[tab_index].WaitForFrameToBeDisplayed() 93 except Exception as e: 94 logging.info('cannot activate tab: %s', e) 95 tab_index = (tab_index + 1) % len(tabs) 96 97 self.check_tab_discard(cr, tabs) 98 99 # Suspending for the specified seconds. 100 try: 101 sys_power.do_suspend(suspend_seconds) 102 except sys_power.SpuriousWakeupError: 103 spurious_wakeup_count += 1 104 if spurious_wakeup_count > MAX_SPURIOUS_WAKEUP: 105 raise error.TestFail('Too many SpuriousWakeupError.') 106 suspend_count += 1 107 108 # Waiting for system stable, otherwise the subsequent tab 109 # operations may fail. 110 time.sleep(additional_sleep) 111 112 self.check_tab_discard(cr, tabs) 113 114 return suspend_count 115 116 def run_once(self, switches_per_suspend=15, total_suspend_duration=2400, 117 suspend_seconds=10, additional_sleep=10): 118 """Runs the test once.""" 119 username = power_load_util.get_username() 120 password = power_load_util.get_password() 121 with chrome.Chrome(gaia_login=True, username=username, 122 password=password) as cr: 123 tabs = self.create_tabs(cr) 124 suspend_count = self.cycling_suspend( 125 cr, tabs, switches_per_suspend, total_suspend_duration, 126 suspend_seconds, additional_sleep) 127 128 tabs_after_suspending = len(cr.browser.tabs) 129 meminfo = utils.get_meminfo() 130 ending_swap_free = meminfo.SwapFree 131 swap_total = meminfo.SwapTotal 132 133 perf_results = {} 134 perf_results['number_of_tabs'] = len(tabs) 135 perf_results['number_of_suspending'] = suspend_count 136 perf_results['tabs_after_suspending'] = tabs_after_suspending 137 perf_results['ending_swap_free'] = ending_swap_free 138 perf_results['swap_total'] = swap_total 139 self.write_perf_keyval(perf_results) 140 141 self.output_perf_value(description='number_of_tabs', 142 value=len(tabs)) 143 self.output_perf_value(description='number_of_suspending', 144 value=suspend_count) 145 self.output_perf_value(description='tabs_after_suspending', 146 value=tabs_after_suspending) 147 self.output_perf_value(description='ending_swap_free', 148 value=ending_swap_free, units='KB') 149 self.output_perf_value(description='swap_total', 150 value=swap_total, units='KB') 151 152