• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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        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