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         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