# Copyright 2018 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import logging import time from autotest_lib.client.bin import test from autotest_lib.client.bin import utils from autotest_lib.client.common_lib import error from autotest_lib.client.common_lib.cros import chrome from autotest_lib.client.common_lib.cros import arc_util from autotest_lib.client.cros.power import sys_power # Stop adding tab when swap_free / swap_total is less than this value. _LOW_SWAP_THRESHOLD = 0.5 # Terminate the test if active_tabs / created_tabs is less than this value. _TOO_FEW_ACTIVE_TABS_THRESHOLD = 0.33 class power_LowMemorySuspend(test.test): """Low memory suspending stress test.""" version = 1 def low_swap_free(self): """Returns true if free swap is low.""" meminfo = utils.get_meminfo() if meminfo.SwapFree < meminfo.SwapTotal * _LOW_SWAP_THRESHOLD: logging.info("Swap is low, swap free: %d, swap total: %d", meminfo.SwapFree, meminfo.SwapTotal) return True return False def create_tabs(self, cr): """Creates tabs until swap free is low. @return: list of created tabs """ # Any non-trivial web page is suitable to consume memory. URL = 'https://inbox.google.com/' tabs = [] # There is some race condition to navigate the first tab, navigating # the first tab may fail unless sleep 2 seconds before navigation. # Skip the first tab. while not self.low_swap_free(): logging.info('creating tab %d', len(tabs)) tab = cr.browser.tabs.New() tabs.append(tab) tab.Navigate(URL); try: tab.WaitForDocumentReadyStateToBeComplete(timeout=20) except Exception as e: logging.warning('Exception when waiting page ready: %s', e) return tabs def check_tab_discard(self, cr, tabs): """Raises error if too many tabs are discarded.""" try: active_tabs = len(cr.browser.tabs) except Exception as e: logging.info('error getting active tab count: %s', e) return created_tabs = len(tabs) if (active_tabs < created_tabs * _TOO_FEW_ACTIVE_TABS_THRESHOLD): msg = ('Too many discards, active tabs: %d, created tabs: %d' % (active_tabs, created_tabs)) raise error.TestFail(msg) def cycling_suspend(self, cr, tabs, switches_per_suspend, total_suspend_duration, suspend_seconds, additional_sleep): """Page cycling and suspending. @return: total suspending count. """ start_time = time.time() suspend_count = 0 tab_index = 0 spurious_wakeup_count = 0 MAX_SPURIOUS_WAKEUP = 5 while time.time() - start_time < total_suspend_duration: # Page cycling for _ in range(switches_per_suspend): try: tabs[tab_index].Activate() tabs[tab_index].WaitForFrameToBeDisplayed() except Exception as e: logging.info('cannot activate tab: %s', e) tab_index = (tab_index + 1) % len(tabs) self.check_tab_discard(cr, tabs) # Suspending for the specified seconds. try: sys_power.do_suspend(suspend_seconds) except sys_power.SpuriousWakeupError: spurious_wakeup_count += 1 if spurious_wakeup_count > MAX_SPURIOUS_WAKEUP: raise error.TestFail('Too many SpuriousWakeupError.') suspend_count += 1 # Waiting for system stable, otherwise the subsequent tab # operations may fail. time.sleep(additional_sleep) self.check_tab_discard(cr, tabs) return suspend_count def run_once(self, switches_per_suspend=15, total_suspend_duration=2400, suspend_seconds=10, additional_sleep=10): """Runs the test once.""" username, password = arc_util.get_test_account_info() with chrome.Chrome(gaia_login=True, username=username, password=password) as cr: tabs = self.create_tabs(cr) suspend_count = self.cycling_suspend( cr, tabs, switches_per_suspend, total_suspend_duration, suspend_seconds, additional_sleep) tabs_after_suspending = len(cr.browser.tabs) meminfo = utils.get_meminfo() ending_swap_free = meminfo.SwapFree swap_total = meminfo.SwapTotal perf_results = {} perf_results['number_of_tabs'] = len(tabs) perf_results['number_of_suspending'] = suspend_count perf_results['tabs_after_suspending'] = tabs_after_suspending perf_results['ending_swap_free'] = ending_swap_free perf_results['swap_total'] = swap_total self.write_perf_keyval(perf_results) self.output_perf_value(description='number_of_tabs', value=len(tabs)) self.output_perf_value(description='number_of_suspending', value=suspend_count) self.output_perf_value(description='tabs_after_suspending', value=tabs_after_suspending) self.output_perf_value(description='ending_swap_free', value=ending_swap_free, units='KB') self.output_perf_value(description='swap_total', value=swap_total, units='KB')