Home | History | Annotate | Download | only in kernel_fs_Punybench
      1 #!/usr/bin/python
      2 #
      3 # Copyright (c) 2011 The Chromium OS 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 logging
      8 import optparse
      9 import os, re
     10 from autotest_lib.client.bin import utils, test
     11 from autotest_lib.client.common_lib import error
     12 
     13 re_float = r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?"
     14 
     15 class kernel_fs_Punybench(test.test):
     16     """Run a selected subset of the puny benchmarks
     17     """
     18     version = 1
     19     Bin = '/usr/local/opt/punybench/bin/'
     20 
     21 
     22     def initialize(self):
     23         self.results = []
     24         self.job.drop_caches_between_iterations = True
     25 
     26 
     27     def _run(self, cmd, args):
     28         """Run a puny benchmark
     29 
     30         Prepends the path to the puny benchmark bin.
     31 
     32         @param cmd: command to be run
     33         @param args: arguments for the command
     34         """
     35         result = utils.system_output(
     36             os.path.join(self.Bin, cmd) + ' ' + args)
     37         logging.debug(result)
     38         return result
     39 
     40 
     41     @staticmethod
     42     def _ecrypt_mount(dir, mnt):
     43         """Mount the eCrypt File System
     44 
     45         @param dir: directory where encrypted file system is stored
     46         @param mnt: mount point for encrypted file system
     47         """
     48         options = ('-o'
     49                    ' key=passphrase:passphrase_passwd=secret'
     50                    ',ecryptfs_cipher=aes'
     51                    ',ecryptfs_key_bytes=32'
     52                    ',no_sig_cache'
     53                    ',ecryptfs_passthrough=no'
     54                    ',ecryptfs_enable_filename_crypto=no')
     55         utils.system_output('mkdir -p %s %s' % (dir, mnt))
     56         utils.system_output('mount -t ecryptfs %s %s %s' %
     57                            (options, dir, mnt))
     58 
     59 
     60     @staticmethod
     61     def _ecrypt_unmount(dir, mnt):
     62         """Unmount the eCrypt File System and remove it and its mount point
     63 
     64         @param dir: directory where encrypted file system was stored
     65         @param mnt: mount point for encrypted file system
     66         """
     67         utils.system_output('umount ' + mnt)
     68         utils.system_output('rm -R ' + dir)
     69         utils.system_output('rm -R ' + mnt)
     70 
     71 
     72     @staticmethod
     73     def _find_max(tag, text):
     74         """Find the max in a memcpy result.
     75 
     76         @param tag: name of sub-test to select from text.
     77         @param text: output from memcpy test.
     78         @return Best result from that sub-test.
     79 
     80         Example input text:
     81           memcpy (Meg = 2**20)
     82           0. 4746.96 MiB/sec
     83           1. 4748.99 MiB/sec
     84           2. 4748.14 MiB/sec
     85           3. 4748.59 MiB/sec
     86           simple (Meg = 2**20)
     87           0. 727.996 MiB/sec
     88           1. 728.031 MiB/sec
     89           2. 728.22 MiB/sec
     90           3. 728.049 MiB/sec
     91           32bit (Meg = 2**20)
     92           0. 2713.16 MiB/sec
     93           1. 2719.93 MiB/sec
     94           2. 2724.33 MiB/sec
     95           3. 2711.5 MiB/sec
     96         """
     97         r1 = re.search(tag + ".*\n(\d.*sec\n)+", text)
     98         r2 = re.findall(r"\d+\. (" + re_float + r") M.*\n", r1.group(0))
     99         return max(float(result) for result in r2)
    100 
    101 
    102     def _memcpy(self):
    103         """Measure memory to memory copy.
    104 
    105         The size has to be large enough that it doesn't fit
    106         in the cache. We then take the best of serveral runs
    107         so we have a guarenteed not to exceed number.
    108 
    109         Several different ways are used to move memory.
    110         """
    111         size = 64 * 1024 * 1024
    112         loops = 4
    113         iterations = 10
    114         args  = '-z %d -i %d -l %d' % (size, iterations, loops)
    115         result = self._run('memcpy', args)
    116 
    117         for tag in ['memcpy', '32bit', '64bit']:
    118             value = self._find_max(tag, result)
    119             self.write_perf_keyval({tag + '_MiB_s': value})
    120 
    121 
    122     @staticmethod
    123     def _get_mib_s(tag, text):
    124         """Extract the MiB/s for tag from text
    125 
    126         @param tag: name of sub-test to select from text
    127         @param text: extact MiB/s from this text
    128 
    129         Example input text:
    130           SDRAM:
    131           memcpy_trivial:  (2097152 bytes copy) =  727.6 MiB/s /  729.9 MiB/s
    132           memcpy        :  (2097152 bytes copy) = 4514.2 MiB/s / 4746.9 MiB/s
    133           memcpy_trivial:  (3145728 bytes copy) =  727.7 MiB/s /  729.5 MiB/s
    134           memcpy        :  (3145728 bytes copy) = 4489.5 MiB/s / 4701.5 MiB/s
    135         """
    136         r1 = re.search(tag + ".*\n.*\n.*", text)
    137         r2 = re.search(r"[^\s]+ MiB/s$", r1.group(0))
    138         r3 = re.search(re_float, r2.group(0))
    139         return r3.group(0)
    140 
    141 
    142     def _memcpy_test(self):
    143         """Test the various caches and alignments
    144 
    145         WARNING: test will have to be changed if cache sizes change.
    146         """
    147         result = self._run('memcpy_test', "")
    148         self.write_perf_keyval({'L1cache_MiB_s':
    149                                self._get_mib_s('L1 cache', result)})
    150         self.write_perf_keyval({'L2cache_MiB_s':
    151                                self._get_mib_s('L2 cache', result)})
    152         self.write_perf_keyval({'SDRAM_MiB_s':
    153                                self._get_mib_s('SDRAM', result)})
    154 
    155 
    156     def _threadtree(self, prefix, dir):
    157         """Create and manipulate directory trees.
    158 
    159         Threadtree creates a directory tree with files for each task.
    160         It then copies that tree then deletes it.
    161 
    162         @param prefix: prefix to use on name/value pair for identifying results
    163         @param dir: directory path to use for test
    164 
    165         Example results:
    166           opens   =       3641
    167           created =       2914
    168           dirs    =       1456
    169           files   =       1458
    170           deleted =       4372
    171           read    = 1046306816
    172           written = 2095407104
    173            51.7   2. timer avg= 57.9 stdv= 8.76
    174         """
    175         iterations = 4
    176         tasks = 2
    177         width = 3
    178         depth = 5
    179         args = ('-d %s -i %d -t %d -w %d -k %d' %
    180                (dir, iterations, tasks, width, depth))
    181         result = self._run('threadtree', args)
    182         r1 = re.search(r"timer avg= *([^\s]*).*$", result)
    183         timer_avg = float(r1.group(1))
    184         p = tasks * pow(width, depth + 1) / timer_avg
    185         self.write_perf_keyval({prefix + 'threadtree_ops': p})
    186 
    187 
    188     def _uread(self, prefix, file):
    189         """Read a large file.
    190 
    191         @param prefix: prefix to use on name/value pair for identifying results
    192         @param file: file path to use for test
    193 
    194         The size should be picked so the file will
    195         not fit in memory.
    196 
    197         Example results:
    198           size=8589934592 n=1 55.5 3. timer avg= 55.5 stdv=0.0693 147.6 MiB/s
    199           size=8589934592 n=1 55.6 4. timer avg= 55.5 stdv=0.0817 147.5 MiB/s
    200         """
    201         args = '-f %s' % file
    202         result = self._run('uread', args)
    203         r1 = re.search(r"[^\s]+ MiB/s.*$", result)
    204         r2 = re.search(re_float, r1.group(0))
    205         mib_s = r2.group(0)
    206         self.write_perf_keyval({prefix + 'uread_MiB_s': mib_s})
    207 
    208 
    209     def _ureadrand(self, prefix, file):
    210         """Read randomly a large file
    211 
    212         @param prefix: prefix to use on name/value pair for identifying results
    213         @param file: file path to use for test
    214 
    215         Example results (modified to fit in 80 columes):
    216 size=8589934592 n=10000 4.7 3. timer avg= 4 stdv= 4.6 9.1 MiB/s 2326 IOPs/sec
    217 size=8589934592 n=10000 4.9 4. timer avg= 4.2 stdv= 4.5 8.8 MiB/s 2262 IOPs/sec
    218         """
    219         args = '-f %s' % file
    220         result = self._run('ureadrand', args)
    221         r1 = re.search(r"([^\s]+ IOPs/sec).*$", result)
    222         r2 = re.search(re_float, r1.group(0))
    223         iops = r2.group(0)
    224         self.write_perf_keyval({prefix + 'ureadrand_iops': iops})
    225 
    226 
    227     def _uwrite(self, prefix, file):
    228         """Write a large file.
    229 
    230         @param prefix: prefix to use on name/value pair for identifying results
    231         @param file: file path to use for test
    232 
    233         The size should be picked so the file will not fit in memory.
    234 
    235         Example results:
    236           size=8589934592 n=1 55.5 3. timer avg= 55.5 stdv=0.0693 147.6 MiB/s
    237           size=8589934592 n=1 55.6 4. timer avg= 55.5 stdv=0.0817 147.5 MiB/s
    238         """
    239         args = '-f %s' % file
    240         result = self._run('uwrite', args)
    241         r1 = re.search(r"[^\s]+ MiB/s.*$", result)
    242         r2 = re.search(re_float, r1.group(0))
    243         mib_s = r2.group(0)
    244         self.write_perf_keyval({prefix + 'uwrite_MiB_s': mib_s})
    245 
    246 
    247     def _uwriterand(self, prefix, file, size):
    248         """Write randomly a file
    249 
    250         @param prefix: prefix to use on name/value pair for identifying results
    251         @param file: file path to use for test
    252         @param size: size of file - large files are much slower than small files
    253 
    254         Example results (modified to fit in 80 columes):
    255 size=16777216 n=1000 13.4 1. timer avg= 13.4 stdv= 0 0.29 MiB/s 74.8 IOPs/sec
    256 size=16777216 n=1000 13.3 2. timer avg= 13.3 stdv=0.032 0.3 MiB/s 75.0 IOPs/sec
    257 
    258         """
    259         loops = 4
    260         iterations = 1000
    261         args = ('-f %s -z %d -i %d -l %d -b12' %
    262                 (file, size, iterations, loops))
    263         result = self._run('uwriterand', args)
    264         r1 = re.search(r"([^\s]+ IOPs/sec).*$", result)
    265         r2 = re.search(re_float, r1.group(0))
    266         iops = r2.group(0)
    267         self.write_perf_keyval({prefix + 'uwriterand_iops': iops})
    268 
    269 
    270     def _uwritesync(self, prefix, file):
    271         """Synchronously writes a file
    272 
    273         @param prefix: prefix to use on name/value pair for identifying results
    274         @param file: file path to use for test
    275 
    276         Example results (modified to fit in 80 columes):
    277 size=409600 n=100 4.58 3. timer avg= 4.41 stdv=0.195 0.0887 MiB/s 22.7 IOPs/sec
    278 size=409600 n=100 4.84 4. timer avg= 4.52 stdv= 0.27 0.0885 MiB/s 22.15 IOPs/sec
    279         """
    280         loops = 4  # minimum loops to average or see trends
    281         num_blocks_to_write = 100  # Because sync writes are slow,
    282                                    # don't do too many
    283         args = ('-f %s -i %d -l %d -b12' %
    284                 (file, num_blocks_to_write, loops))
    285         result = self._run('uwritesync', args)
    286         r1 = re.search(r"([^\s]+ IOPs/sec).*$", result)
    287         r2 = re.search(re_float, r1.group(0))
    288         iops = r2.group(0)
    289         self.write_perf_keyval({prefix + 'uwritesync_iops': iops})
    290 
    291 
    292     def _disk_tests(self, prefix,  dir, file):
    293         """Run this collection of disk tests
    294 
    295         @param prefix: prefix to use on name/value pair for identifying results
    296         @param dir: directory path to use for tests
    297         @param file: file path to use for tests
    298         """
    299         self._uread(prefix, file)
    300         self._ureadrand(prefix, file)
    301         self._uwrite(prefix, file)
    302         self._uwriterand(prefix + '_large_', file, 8 * 1024 * 1024 * 1024)
    303         # This tests sometimes gives invalid results
    304         # self._uwriterand(prefix + '_small_', file, 8 * 1024)
    305         self._uwritesync(prefix, file)
    306         self._threadtree(prefix, dir)
    307 
    308 
    309     def _ecryptfs(self):
    310         """Setup up to run disk tests on encrypted volume
    311         """
    312         dir = '/usr/local/ecrypt_tst'
    313         mnt = '/usr/local/ecrypt_mnt'
    314         self._ecrypt_mount(dir, mnt)
    315         self._disk_tests('ecryptfs_', mnt + '/_Dir', mnt + '/xyzzy')
    316         self._ecrypt_unmount(dir, mnt)
    317 
    318 
    319     def _parse_args(self, args):
    320         """Parse input arguments to this autotest.
    321 
    322         Args:
    323         @param args: List of arguments to parse.
    324         @return
    325           opts: Options, as per optparse.
    326           args: Non-option arguments, as per optparse.
    327         """
    328         parser = optparse.OptionParser()
    329         parser.add_option('--disk', dest='want_disk_tests',
    330                           action='store_true', default=False,
    331                           help='Run disk tests.')
    332         parser.add_option('--ecryptfs', dest='want_ecryptfs_tests',
    333                           action='store_true', default=False,
    334                           help='Run ecryptfs tests.')
    335         parser.add_option('--mem', dest='want_mem_tests',
    336                           action='store_true', default=False,
    337                           help='Run memory tests.')
    338         parser.add_option('--nop', dest='want_nop_tests',
    339                           action='store_true', default=False,
    340                           help='Do nothing.')
    341         return parser.parse_args(args)
    342 
    343 
    344     def run_once(self, args=[]):
    345         """Run the PyAuto performance tests.
    346 
    347         @param args: Either space-separated arguments or a list of string
    348               arguments.  If this is a space separated string, we'll just
    349               call split() on it to get a list.  The list will be sent to
    350               optparse for parsing.
    351         """
    352         if isinstance(args, str):
    353             args = args.split()
    354         options, test_args = self._parse_args(args)
    355 
    356         if test_args:
    357             raise error.TestFail("Unknown args: %s" % repr(test_args))
    358 
    359         if not os.path.exists(self.Bin):
    360             raise error.TestFail("%s does not exist" % self.Bin)
    361 
    362         try:
    363             restart_swap = True
    364             utils.system_output('swapoff /dev/zram0')
    365         except:
    366             restart_swap = False
    367         utils.system_output('stop ui')
    368         if options.want_nop_tests:
    369             pass
    370         if options.want_mem_tests:
    371             self._memcpy_test()
    372             self._memcpy()
    373         if options.want_disk_tests:
    374             self._disk_tests('ext4_', '/usr/local/_Dir', '/usr/local/xyzzy')
    375         if options.want_ecryptfs_tests:
    376             self._ecryptfs()
    377 
    378         if restart_swap:
    379             utils.system_output('swapon /dev/zram0')
    380         utils.system_output('start ui')
    381