Home | History | Annotate | Download | only in platform_CompressedSwap
      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 
      7 import errno, logging, os, select, signal, subprocess, time
      8 
      9 from autotest_lib.client.bin import utils, test
     10 from autotest_lib.client.common_lib import error
     11 
     12 
     13 class 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