blob: 04c32bbc67c81739334dccecf4a8d721af7d64ce [file] [log] [blame]
#!/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))