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 7import logging 8import optparse 9import os, re 10from autotest_lib.client.bin import utils, test 11from autotest_lib.client.common_lib import error 12 13re_float = r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?" 14 15class 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): 216size=8589934592 n=10000 4.7 3. timer avg= 4 stdv= 4.6 9.1 MiB/s 2326 IOPs/sec 217size=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): 255size=16777216 n=1000 13.4 1. timer avg= 13.4 stdv= 0 0.29 MiB/s 74.8 IOPs/sec 256size=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): 277size=409600 n=100 4.58 3. timer avg= 4.41 stdv=0.195 0.0887 MiB/s 22.7 IOPs/sec 278size=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