1#!/usr/bin/python
2#
3# Copyright (c) 2010 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__author__ = 'kdlucas@chromium.org (Kelly Lucas)'
8
9import logging, re
10
11from autotest_lib.client.bin import utils, test
12from autotest_lib.client.common_lib import error
13
14
15class platform_MemCheck(test.test):
16    """
17    Verify memory usage looks correct.
18    """
19    version = 1
20    swap_disksize_file = '/sys/block/zram0/disksize'
21
22    def run_once(self):
23        errors = 0
24        keyval = dict()
25        # The total memory will shrink if the system bios grabs more of the
26        # reserved memory. We derived the value below by giving a small
27        # cushion to allow for more system BIOS usage of ram. The memref value
28        # is driven by the supported netbook model with the least amount of
29        # total memory.  ARM and x86 values differ considerably.
30        cpuType = utils.get_cpu_arch()
31        memref = 986392
32        vmemref = 102400
33        if cpuType == "arm":
34            memref = 700000
35            vmemref = 210000
36
37        speedref = 1333
38        os_reserve = 600000
39
40        # size reported in /sys/block/zram0/disksize is in byte
41        swapref = int(utils.read_one_line(self.swap_disksize_file)) / 1024
42
43        less_refs = ['MemTotal', 'MemFree', 'VmallocTotal']
44        approx_refs = ['SwapTotal']
45
46        # read physical HW size from mosys and adjust memref if need
47        cmd = 'mosys memory spd print geometry -s size_mb'
48        phy_size_run = utils.run(cmd)
49        phy_size = 0
50        for line in phy_size_run.stdout.split():
51            phy_size += int(line)
52        # memref is in KB but phy_size is in MB
53        phy_size *= 1024
54        keyval['PhysicalSize'] = phy_size
55        memref = max(memref, phy_size - os_reserve)
56        freeref = memref / 2
57
58        # Special rule for free memory size for parrot and butterfly
59        board = utils.get_board()
60        if board.startswith('parrot'):
61            freeref = 100 * 1024
62        elif board.startswith('butterfly'):
63            freeref = freeref - 400 * 1024
64        elif board.startswith('rambi') or board.startswith('expresso'):
65            logging.info('Skipping test on rambi and expresso, '
66                         'see crbug.com/411401')
67            return
68
69        ref = {'MemTotal': memref,
70               'MemFree': freeref,
71               'SwapTotal': swapref,
72               'VmallocTotal': vmemref,
73              }
74
75        logging.info('board: %s, phy_size: %d memref: %d freeref: %d',
76                      board, phy_size, memref, freeref)
77
78        error_list = []
79
80        for k in ref:
81            value = utils.read_from_meminfo(k)
82            keyval[k] = value
83            if k in less_refs:
84                if value < ref[k]:
85                    logging.warning('%s is %d', k, value)
86                    logging.warning('%s should be at least %d', k, ref[k])
87                    errors += 1
88                    error_list += [k]
89            elif k in approx_refs:
90                if value < ref[k] * 0.9 or ref[k] * 1.1 < value:
91                    logging.warning('%s is %d', k, value)
92                    logging.warning('%s should be within 10%% of %d', k, ref[k])
93                    errors += 1
94                    error_list += [k]
95
96        # read spd timings
97        cmd = 'mosys memory spd print timings -s speeds'
98        # result example
99        # DDR3-800, DDR3-1066, DDR3-1333, DDR3-1600
100        pattern = '[A-Z]*DDR([3-9]|[1-9]\d+)[A-Z]*-(?P<speed>\d+)'
101        timing_run = utils.run(cmd)
102
103        keyval['speedref'] = speedref
104        for dimm, line in enumerate(timing_run.stdout.split('\n')):
105            if not line:
106                continue
107            max_timing = line.split(', ')[-1]
108            keyval['timing_dimm_%d' % dimm] = max_timing
109            m = re.match(pattern, max_timing)
110            if not m:
111                logging.warning('Error parsing timings for dimm #%d (%s)',
112                             dimm, max_timing)
113                errors += 1
114                continue
115            logging.info('dimm #%d timings: %s', dimm, max_timing)
116            max_speed = int(m.group('speed'))
117            keyval['speed_dimm_%d' % dimm] = max_speed
118            if max_speed < speedref:
119                logging.warning('ram speed is %s', max_timing)
120                logging.warning('ram speed should be at least %d', speedref)
121                error_list += ['speed_dimm_%d' % dimm]
122                errors += 1
123
124        # If self.error is not zero, there were errors.
125        if errors > 0:
126            error_list_str = ', '.join(error_list)
127            raise error.TestFail('Found incorrect values: %s' % error_list_str)
128
129        self.write_perf_keyval(keyval)
130