1#!/usr/bin/python 2# 3# Copyright (c) 2013 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7import errno, logging, os, select, signal, subprocess, time 8 9from autotest_lib.client.bin import utils, test 10from autotest_lib.client.common_lib import error 11 12 13class platform_CompressedSwap(test.test): 14 """ 15 Verify compressed swap is configured and basically works. 16 """ 17 version = 1 18 executable = 'hog' 19 swap_enable_file = '/home/chronos/.swap_enabled' 20 swap_disksize_file = '/sys/block/zram0/disksize' 21 22 23 def setup(self): 24 os.chdir(self.srcdir) 25 utils.make(self.executable) 26 27 def check_for_oom(self, hogs): 28 for p in hogs: 29 retcode = p.poll() # returns None if the thread is still running 30 if retcode is not None: 31 logging.info('hog %d of %d is gone, assume oom: retcode %s' % 32 (hogs.index(p) + 1, len(hogs), retcode)) 33 return True 34 return False 35 36 # Check for low memory notification by polling /dev/chromeos-low-mem. 37 def getting_low_mem_notification(self): 38 lowmem_fd = open('/dev/chromeos-low-mem', 'r') 39 lowmem_poller = select.poll() 40 lowmem_poller.register(lowmem_fd, select.POLLIN) 41 events=lowmem_poller.poll(0) 42 lowmem_fd.close() 43 for fd, flag in events: 44 if flag & select.POLLIN: 45 return True 46 return False 47 48 def run_once(self, just_checking_lowmem=False, checking_for_oom=False): 49 50 memtotal = utils.read_from_meminfo('MemTotal') 51 swaptotal = utils.read_from_meminfo('SwapTotal') 52 free_target = (memtotal + swaptotal) * 0.03 53 54 # Check for proper swap space configuration. 55 # If the swap enable file says "0", swap.conf does not create swap. 56 if not just_checking_lowmem and not checking_for_oom: 57 if os.path.exists(self.swap_enable_file): 58 enable_size = utils.read_one_line(self.swap_enable_file) 59 else: 60 enable_size = "nonexistent" # implies nonzero 61 if enable_size == "0": 62 if swaptotal != 0: 63 raise error.TestFail('The swap enable file said 0, but' 64 ' swap was still enabled for %d.' % 65 swaptotal) 66 logging.info('Swap enable (0), swap disabled.') 67 else: 68 # Rather than parsing swap.conf logic to calculate a size, 69 # use the value it writes to /sys/block/zram0/disksize. 70 if not os.path.exists(self.swap_disksize_file): 71 raise error.TestFail('The %s swap enable file should have' 72 ' caused zram to load, but %s was' 73 ' not found.' % 74 (enable_size, self.swap_disksize_file)) 75 disksize = utils.read_one_line(self.swap_disksize_file) 76 swaprequested = int(disksize) / 1000 77 if (swaptotal < swaprequested * 0.9 or 78 swaptotal > swaprequested * 1.1): 79 raise error.TestFail('Our swap of %d K is not within 10%' 80 ' of the %d K we requested.' % 81 (swaptotal, swaprequested)) 82 logging.info('Swap enable (%s), requested %d, total %d' 83 % (enable_size, swaprequested, swaptotal)) 84 85 first_oom = 0 86 first_lowmem = 0 87 cleared_low_mem_notification = False 88 89 # Loop over hog creation until MemFree+SwapFree approaches 0. 90 # Confirm we do not see any OOMs (procs killed due to Out Of Memory). 91 hogs = [] 92 cmd = [ self.srcdir + '/' + self.executable, '50' ] 93 logging.debug('Memory hog command line is %s' % cmd) 94 while len(hogs) < 200: 95 memfree = utils.read_from_meminfo('MemFree') 96 swapfree = utils.read_from_meminfo('SwapFree') 97 total_free = memfree + swapfree 98 logging.debug('nhogs %d: memfree %d, swapfree %d' % 99 (len(hogs), memfree, swapfree)) 100 if not checking_for_oom and total_free < free_target: 101 break; 102 103 p = subprocess.Popen(cmd) 104 utils.write_one_line('/proc/%d/oom_score_adj' % p.pid, '1000') 105 hogs.append(p) 106 107 time.sleep(2) 108 109 if self.check_for_oom(hogs): 110 first_oom = len(hogs) 111 break 112 113 # Check for low memory notification. 114 if self.getting_low_mem_notification(): 115 if first_lowmem == 0: 116 first_lowmem = len(hogs) 117 logging.info('Got low memory notification after hog %d' % 118 len(hogs)) 119 120 logging.info('Finished creating %d hogs, SwapFree %d, MemFree %d, ' 121 'low mem at %d, oom at %d' % 122 (len(hogs), swapfree, memfree, first_lowmem, first_oom)) 123 124 if not checking_for_oom and first_oom > 0: 125 utils.system("killall -TERM hog") 126 raise error.TestFail('Oom detected after %d hogs created' % 127 len(hogs)) 128 129 # Before cleaning up all the hogs, verify that killing hogs back to 130 # our initial low memory notification causes notification to end. 131 if first_lowmem > 0: 132 hogs_killed = 0; 133 for p in hogs: 134 if not self.getting_low_mem_notification(): 135 cleared_low_mem_notification = True 136 logging.info('Cleared low memory notification after %d ' 137 'hogs were killed' % hogs_killed) 138 break; 139 try: 140 p.kill() 141 except OSError, e: 142 if e.errno == errno.ESRCH: 143 logging.info('Hog %d not found to kill, assume Oomed' % 144 (hogs.index(p) + 1)); 145 else: 146 logging.warning('Hog %d kill failed: %s' % 147 (hogs.index(p) + 1, 148 os.strerror(e.errno))); 149 else: 150 hogs_killed += 1 151 time.sleep(2) 152 153 # Clean up the rest of our hogs since they otherwise live forever. 154 utils.system("killall -TERM hog") 155 time.sleep(5) 156 swapfree2 = utils.read_from_meminfo('SwapFree') 157 logging.info('SwapFree was %d before cleanup, %d after.' % 158 (swapfree, swapfree2)) 159 160 # Raise exceptions due to low memory notification failures. 161 if first_lowmem == 0: 162 raise error.TestFail('We did not get low memory notification!') 163 elif not cleared_low_mem_notification: 164 raise error.TestFail('We did not clear low memory notification!') 165 elif len(hogs) - hogs_killed < first_lowmem - 3: 166 raise error.TestFail('We got low memory notification at hog %d, ' 167 'but we did not clear it until we dropped to ' 168 'hog %d' % 169 (first_lowmem, len(hogs) - hogs_killed)) 170