1# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6
7from autotest_lib.client.bin import test
8from autotest_lib.client.common_lib import error, utils
9from autotest_lib.client.cros import service_stopper
10
11class hardware_MemoryLatency(test.test):
12    """Autotest for measuring memory latency.
13
14    This uses lat_mem_rd with different parameters to measure memory and cache
15    latencies.
16    """
17    version = 1
18
19    def _run_benchmarks(self, warmup, num_iterations, max_size_mb,
20                        sample_size_kb, random, stride):
21        """Run the benchmark.
22
23        This runs the lat_mem_rd benchmark from lmbench 3.
24        Args:
25          warmup:  integer amount of time to spend warming up in microseconds.
26          num_iterations: integer number of times to run the benchmark on each
27            size.
28          max_size_mb: integer size in MB and if sample_size_kb isn't
29            specified, then we'll use this as the only number to report
30          sample_size_kb: a list of integers of specific points where we want
31            to sample the latency
32          random: a boolean which specifies whether a regular stride is used or
33            a fully randomized pointer chase
34          stride: power of two size integer for a stride between pointers
35        """
36        r = {}
37        sample_sizes = [ int(max_size_mb) * 1024 ]
38        sample_sizes.extend(sample_size_kb)
39
40        random_flag = '-t' if random else ''
41
42        cmd = 'lat_mem_rd %s -W %d -N %d %s %d 2>&1' % (random_flag, warmup,
43                                                   num_iterations, max_size_mb,
44                                                   stride)
45        logging.debug('cmd: %s', cmd)
46        out = utils.system_output(cmd)
47        logging.debug('Output: %s', out)
48
49        # split the output into lines and multiply the first column by
50        # 1024 to get kb, lmbench divides by 1024 but truncates the result
51        # so we have to use rounding to get the correct size
52        for line in out.splitlines():
53            s = line.split()
54            if len(s) == 2:
55                size = int(round(float(s[0]) * 1024))
56                latency = float(s[1])
57                if size in sample_sizes:
58                    logging.debug('Matched on size %d', size)
59                    if latency <= 0:
60                        raise error.TestFail('invalid latency %f' % latency)
61                    self._results['ns_' + str(size) + 'KB'] = latency
62            else:
63                logging.debug('Ignoring output line: %s', line)
64
65
66    def initialize(self):
67        """Perform necessary initialization prior to test run.
68
69        Private Attributes:
70          _results: dict containing keyvals with latency measurements
71          _services: service_stopper.ServiceStopper object
72        """
73        super(hardware_MemoryLatency, self).initialize()
74        self._results = {}
75        stop = [ 'ui' ]
76        stop.extend(service_stopper.ServiceStopper.POWER_DRAW_SERVICES)
77        self._services = service_stopper.ServiceStopper(stop)
78        self._services.stop_services()
79
80
81    def run_once(self, warmup=100, num_iterations=20, max_size_mb='32',
82                 sample_size_kb=[ int(2), int(192) ], random=False, stride=512):
83        self._run_benchmarks(warmup, num_iterations, max_size_mb,
84                             sample_size_kb, random, stride)
85        self.write_perf_keyval(self._results)
86
87
88    def cleanup(self):
89        self._services.restore_services()
90        super(hardware_MemoryLatency, self).cleanup()
91
92