#!/usr/bin/python # # Copyright (c) 2013 The Chromium 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 errno, logging, os, select, signal, subprocess, time from autotest_lib.client.bin import utils, test from autotest_lib.client.common_lib import error class platform_CompressedSwap(test.test): """ Verify compressed swap is configured and basically works. """ version = 1 executable = 'hog' swap_enable_file = '/home/chronos/.swap_enabled' swap_disksize_file = '/sys/block/zram0/disksize' def setup(self): os.chdir(self.srcdir) utils.make(self.executable) def check_for_oom(self, hogs): for p in hogs: retcode = p.poll() # returns None if the thread is still running if retcode is not None: logging.info('hog %d of %d is gone, assume oom: retcode %s' % (hogs.index(p) + 1, len(hogs), retcode)) return True return False # Check for low memory notification by polling /dev/chromeos-low-mem. def getting_low_mem_notification(self): lowmem_fd = open('/dev/chromeos-low-mem', 'r') lowmem_poller = select.poll() lowmem_poller.register(lowmem_fd, select.POLLIN) events=lowmem_poller.poll(0) lowmem_fd.close() for fd, flag in events: if flag & select.POLLIN: return True return False def run_once(self, just_checking_lowmem=False, checking_for_oom=False): memtotal = utils.read_from_meminfo('MemTotal') swaptotal = utils.read_from_meminfo('SwapTotal') free_target = (memtotal + swaptotal) * 0.03 # Check for proper swap space configuration. # If the swap enable file says "0", swap.conf does not create swap. if not just_checking_lowmem and not checking_for_oom: if os.path.exists(self.swap_enable_file): enable_size = utils.read_one_line(self.swap_enable_file) else: enable_size = "nonexistent" # implies nonzero if enable_size == "0": if swaptotal != 0: raise error.TestFail('The swap enable file said 0, but' ' swap was still enabled for %d.' % swaptotal) logging.info('Swap enable (0), swap disabled.') else: # Rather than parsing swap.conf logic to calculate a size, # use the value it writes to /sys/block/zram0/disksize. if not os.path.exists(self.swap_disksize_file): raise error.TestFail('The %s swap enable file should have' ' caused zram to load, but %s was' ' not found.' % (enable_size, self.swap_disksize_file)) disksize = utils.read_one_line(self.swap_disksize_file) swaprequested = int(disksize) / 1000 if (swaptotal < swaprequested * 0.9 or swaptotal > swaprequested * 1.1): raise error.TestFail('Our swap of %d K is not within 10%' ' of the %d K we requested.' % (swaptotal, swaprequested)) logging.info('Swap enable (%s), requested %d, total %d' % (enable_size, swaprequested, swaptotal)) MB_PER_HOG = 50 MB_TO_KB = 1024 max_hog_count = (memtotal + swaptotal) / (MB_PER_HOG * MB_TO_KB) first_oom = 0 first_lowmem = 0 cleared_low_mem_notification = False # Loop over hog creation until MemFree+SwapFree approaches 0. # Confirm we do not see any OOMs (procs killed due to Out Of Memory). hogs = [] cmd = [ self.srcdir + '/' + self.executable, str(MB_PER_HOG)] logging.debug('Memory hog command line is %s' % cmd) while len(hogs) < max_hog_count: memfree = utils.read_from_meminfo('MemFree') swapfree = utils.read_from_meminfo('SwapFree') total_free = memfree + swapfree logging.debug('nhogs %d: memfree %d, swapfree %d' % (len(hogs), memfree, swapfree)) if not checking_for_oom and total_free < free_target: break; p = subprocess.Popen(cmd) utils.write_one_line('/proc/%d/oom_score_adj' % p.pid, '1000') hogs.append(p) time.sleep(2) if self.check_for_oom(hogs): first_oom = len(hogs) break # Check for low memory notification. if self.getting_low_mem_notification(): if first_lowmem == 0: first_lowmem = len(hogs) logging.info('Got low memory notification after hog %d' % len(hogs)) logging.info('Finished creating %d hogs, SwapFree %d, MemFree %d, ' 'low mem at %d, oom at %d' % (len(hogs), swapfree, memfree, first_lowmem, first_oom)) if not checking_for_oom and first_oom > 0: utils.system("killall -TERM hog") raise error.TestFail('Oom detected after %d hogs created' % len(hogs)) # Before cleaning up all the hogs, verify that killing hogs back to # our initial low memory notification causes notification to end. if first_lowmem > 0: hogs_killed = 0; for p in hogs: if not self.getting_low_mem_notification(): cleared_low_mem_notification = True logging.info('Cleared low memory notification after %d ' 'hogs were killed' % hogs_killed) break; try: p.kill() except OSError, e: if e.errno == errno.ESRCH: logging.info('Hog %d not found to kill, assume Oomed' % (hogs.index(p) + 1)); else: logging.warning('Hog %d kill failed: %s' % (hogs.index(p) + 1, os.strerror(e.errno))); else: hogs_killed += 1 time.sleep(2) # Clean up the rest of our hogs since they otherwise live forever. utils.system("killall -TERM hog") time.sleep(5) swapfree2 = utils.read_from_meminfo('SwapFree') logging.info('SwapFree was %d before cleanup, %d after.' % (swapfree, swapfree2)) # Raise exceptions due to low memory notification failures. if first_lowmem == 0: raise error.TestFail('We did not get low memory notification!') elif not cleared_low_mem_notification: raise error.TestFail('We did not clear low memory notification!') elif len(hogs) - hogs_killed < first_lowmem - 3: raise error.TestFail('We got low memory notification at hog %d, ' 'but we did not clear it until we dropped to ' 'hog %d' % (first_lowmem, len(hogs) - hogs_killed))