1#!/usr/bin/python2 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 MB_PER_HOG = 50 86 MB_TO_KB = 1024 87 max_hog_count = (memtotal + swaptotal) / (MB_PER_HOG * MB_TO_KB) 88 first_oom = 0 89 first_lowmem = 0 90 cleared_low_mem_notification = False 91 92 # Loop over hog creation until MemFree+SwapFree approaches 0. 93 # Confirm we do not see any OOMs (procs killed due to Out Of Memory). 94 hogs = [] 95 cmd = [ self.srcdir + '/' + self.executable, str(MB_PER_HOG)] 96 logging.debug('Memory hog command line is %s' % cmd) 97 while len(hogs) < max_hog_count: 98 memfree = utils.read_from_meminfo('MemFree') 99 swapfree = utils.read_from_meminfo('SwapFree') 100 total_free = memfree + swapfree 101 logging.debug('nhogs %d: memfree %d, swapfree %d' % 102 (len(hogs), memfree, swapfree)) 103 if not checking_for_oom and total_free < free_target: 104 break; 105 106 p = subprocess.Popen(cmd) 107 utils.write_one_line('/proc/%d/oom_score_adj' % p.pid, '1000') 108 hogs.append(p) 109 110 time.sleep(2) 111 112 if self.check_for_oom(hogs): 113 first_oom = len(hogs) 114 break 115 116 # Check for low memory notification. 117 if self.getting_low_mem_notification(): 118 if first_lowmem == 0: 119 first_lowmem = len(hogs) 120 logging.info('Got low memory notification after hog %d' % 121 len(hogs)) 122 123 logging.info('Finished creating %d hogs, SwapFree %d, MemFree %d, ' 124 'low mem at %d, oom at %d' % 125 (len(hogs), swapfree, memfree, first_lowmem, first_oom)) 126 127 if not checking_for_oom and first_oom > 0: 128 utils.system("killall -TERM hog") 129 raise error.TestFail('Oom detected after %d hogs created' % 130 len(hogs)) 131 132 # Before cleaning up all the hogs, verify that killing hogs back to 133 # our initial low memory notification causes notification to end. 134 if first_lowmem > 0: 135 hogs_killed = 0; 136 for p in hogs: 137 if not self.getting_low_mem_notification(): 138 cleared_low_mem_notification = True 139 logging.info('Cleared low memory notification after %d ' 140 'hogs were killed' % hogs_killed) 141 break; 142 try: 143 p.kill() 144 except OSError, e: 145 if e.errno == errno.ESRCH: 146 logging.info('Hog %d not found to kill, assume Oomed' % 147 (hogs.index(p) + 1)); 148 else: 149 logging.warning('Hog %d kill failed: %s' % 150 (hogs.index(p) + 1, 151 os.strerror(e.errno))); 152 else: 153 hogs_killed += 1 154 time.sleep(2) 155 156 # Clean up the rest of our hogs since they otherwise live forever. 157 utils.system("killall -TERM hog") 158 time.sleep(5) 159 swapfree2 = utils.read_from_meminfo('SwapFree') 160 logging.info('SwapFree was %d before cleanup, %d after.' % 161 (swapfree, swapfree2)) 162 163 # Raise exceptions due to low memory notification failures. 164 if first_lowmem == 0: 165 raise error.TestFail('We did not get low memory notification!') 166 elif not cleared_low_mem_notification: 167 raise error.TestFail('We did not clear low memory notification!') 168 elif len(hogs) - hogs_killed < first_lowmem - 3: 169 raise error.TestFail('We got low memory notification at hog %d, ' 170 'but we did not clear it until we dropped to ' 171 'hog %d' % 172 (first_lowmem, len(hogs) - hogs_killed)) 173