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