blob: e69927c417852f641cadb105ad96ab2d1a6caedb [file] [log] [blame]
#!/usr/bin/python
#
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import optparse
import os, re
from autotest_lib.client.bin import utils, test
from autotest_lib.client.common_lib import error
re_float = r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?"
class kernel_fs_Punybench(test.test):
"""Run a selected subset of the puny benchmarks
"""
version = 1
Bin = '/usr/local/opt/punybench/bin/'
def initialize(self):
self.results = []
self.job.drop_caches_between_iterations = True
def _run(self, cmd, args):
"""Run a puny benchmark
Prepends the path to the puny benchmark bin.
@param cmd: command to be run
@param args: arguments for the command
"""
result = utils.system_output(
os.path.join(self.Bin, cmd) + ' ' + args)
logging.debug(result)
return result
@staticmethod
def _ecrypt_mount(dir, mnt):
"""Mount the eCrypt File System
@param dir: directory where encrypted file system is stored
@param mnt: mount point for encrypted file system
"""
options = ('-o'
' key=passphrase:passphrase_passwd=secret'
',ecryptfs_cipher=aes'
',ecryptfs_key_bytes=32'
',no_sig_cache'
',ecryptfs_passthrough=no'
',ecryptfs_enable_filename_crypto=no')
utils.system_output('mkdir -p %s %s' % (dir, mnt))
utils.system_output('mount -t ecryptfs %s %s %s' %
(options, dir, mnt))
@staticmethod
def _ecrypt_unmount(dir, mnt):
"""Unmount the eCrypt File System and remove it and its mount point
@param dir: directory where encrypted file system was stored
@param mnt: mount point for encrypted file system
"""
utils.system_output('umount ' + mnt)
utils.system_output('rm -R ' + dir)
utils.system_output('rm -R ' + mnt)
@staticmethod
def _find_max(tag, text):
"""Find the max in a memcpy result.
@param tag: name of sub-test to select from text.
@param text: output from memcpy test.
@return Best result from that sub-test.
Example input text:
memcpy (Meg = 2**20)
0. 4746.96 MiB/sec
1. 4748.99 MiB/sec
2. 4748.14 MiB/sec
3. 4748.59 MiB/sec
simple (Meg = 2**20)
0. 727.996 MiB/sec
1. 728.031 MiB/sec
2. 728.22 MiB/sec
3. 728.049 MiB/sec
32bit (Meg = 2**20)
0. 2713.16 MiB/sec
1. 2719.93 MiB/sec
2. 2724.33 MiB/sec
3. 2711.5 MiB/sec
"""
r1 = re.search(tag + ".*\n(\d.*sec\n)+", text)
r2 = re.findall(r"\d+\. (" + re_float + r") M.*\n", r1.group(0))
return max(float(result) for result in r2)
def _memcpy(self):
"""Measure memory to memory copy.
The size has to be large enough that it doesn't fit
in the cache. We then take the best of serveral runs
so we have a guarenteed not to exceed number.
Several different ways are used to move memory.
"""
size = 64 * 1024 * 1024
loops = 4
iterations = 10
args = '-z %d -i %d -l %d' % (size, iterations, loops)
result = self._run('memcpy', args)
for tag in ['memcpy', '32bit', '64bit']:
value = self._find_max(tag, result)
self.write_perf_keyval({tag + '_MiB_s': value})
@staticmethod
def _get_mib_s(tag, text):
"""Extract the MiB/s for tag from text
@param tag: name of sub-test to select from text
@param text: extact MiB/s from this text
Example input text:
SDRAM:
memcpy_trivial: (2097152 bytes copy) = 727.6 MiB/s / 729.9 MiB/s
memcpy : (2097152 bytes copy) = 4514.2 MiB/s / 4746.9 MiB/s
memcpy_trivial: (3145728 bytes copy) = 727.7 MiB/s / 729.5 MiB/s
memcpy : (3145728 bytes copy) = 4489.5 MiB/s / 4701.5 MiB/s
"""
r1 = re.search(tag + ".*\n.*\n.*", text)
r2 = re.search(r"[^\s]+ MiB/s$", r1.group(0))
r3 = re.search(re_float, r2.group(0))
return r3.group(0)
def _memcpy_test(self):
"""Test the various caches and alignments
WARNING: test will have to be changed if cache sizes change.
"""
result = self._run('memcpy_test', "")
self.write_perf_keyval({'L1cache_MiB_s':
self._get_mib_s('L1 cache', result)})
self.write_perf_keyval({'L2cache_MiB_s':
self._get_mib_s('L2 cache', result)})
self.write_perf_keyval({'SDRAM_MiB_s':
self._get_mib_s('SDRAM', result)})
def _threadtree(self, prefix, dir):
"""Create and manipulate directory trees.
Threadtree creates a directory tree with files for each task.
It then copies that tree then deletes it.
@param prefix: prefix to use on name/value pair for identifying results
@param dir: directory path to use for test
Example results:
opens = 3641
created = 2914
dirs = 1456
files = 1458
deleted = 4372
read = 1046306816
written = 2095407104
51.7 2. timer avg= 57.9 stdv= 8.76
"""
iterations = 4
tasks = 2
width = 3
depth = 5
args = ('-d %s -i %d -t %d -w %d -k %d' %
(dir, iterations, tasks, width, depth))
result = self._run('threadtree', args)
r1 = re.search(r"timer avg= *([^\s]*).*$", result)
timer_avg = float(r1.group(1))
p = tasks * pow(width, depth + 1) / timer_avg
self.write_perf_keyval({prefix + 'threadtree_ops': p})
def _uread(self, prefix, file):
"""Read a large file.
@param prefix: prefix to use on name/value pair for identifying results
@param file: file path to use for test
The size should be picked so the file will
not fit in memory.
Example results:
size=8589934592 n=1 55.5 3. timer avg= 55.5 stdv=0.0693 147.6 MiB/s
size=8589934592 n=1 55.6 4. timer avg= 55.5 stdv=0.0817 147.5 MiB/s
"""
args = '-f %s' % file
result = self._run('uread', args)
r1 = re.search(r"[^\s]+ MiB/s.*$", result)
r2 = re.search(re_float, r1.group(0))
mib_s = r2.group(0)
self.write_perf_keyval({prefix + 'uread_MiB_s': mib_s})
def _ureadrand(self, prefix, file):
"""Read randomly a large file
@param prefix: prefix to use on name/value pair for identifying results
@param file: file path to use for test
Example results (modified to fit in 80 columes):
size=8589934592 n=10000 4.7 3. timer avg= 4 stdv= 4.6 9.1 MiB/s 2326 IOPs/sec
size=8589934592 n=10000 4.9 4. timer avg= 4.2 stdv= 4.5 8.8 MiB/s 2262 IOPs/sec
"""
args = '-f %s' % file
result = self._run('ureadrand', args)
r1 = re.search(r"([^\s]+ IOPs/sec).*$", result)
r2 = re.search(re_float, r1.group(0))
iops = r2.group(0)
self.write_perf_keyval({prefix + 'ureadrand_iops': iops})
def _uwrite(self, prefix, file):
"""Write a large file.
@param prefix: prefix to use on name/value pair for identifying results
@param file: file path to use for test
The size should be picked so the file will not fit in memory.
Example results:
size=8589934592 n=1 55.5 3. timer avg= 55.5 stdv=0.0693 147.6 MiB/s
size=8589934592 n=1 55.6 4. timer avg= 55.5 stdv=0.0817 147.5 MiB/s
"""
args = '-f %s' % file
result = self._run('uwrite', args)
r1 = re.search(r"[^\s]+ MiB/s.*$", result)
r2 = re.search(re_float, r1.group(0))
mib_s = r2.group(0)
self.write_perf_keyval({prefix + 'uwrite_MiB_s': mib_s})
def _uwriterand(self, prefix, file, size):
"""Write randomly a file
@param prefix: prefix to use on name/value pair for identifying results
@param file: file path to use for test
@param size: size of file - large files are much slower than small files
Example results (modified to fit in 80 columes):
size=16777216 n=1000 13.4 1. timer avg= 13.4 stdv= 0 0.29 MiB/s 74.8 IOPs/sec
size=16777216 n=1000 13.3 2. timer avg= 13.3 stdv=0.032 0.3 MiB/s 75.0 IOPs/sec
"""
loops = 4
iterations = 1000
args = ('-f %s -z %d -i %d -l %d -b12' %
(file, size, iterations, loops))
result = self._run('uwriterand', args)
r1 = re.search(r"([^\s]+ IOPs/sec).*$", result)
r2 = re.search(re_float, r1.group(0))
iops = r2.group(0)
self.write_perf_keyval({prefix + 'uwriterand_iops': iops})
def _uwritesync(self, prefix, file):
"""Synchronously writes a file
@param prefix: prefix to use on name/value pair for identifying results
@param file: file path to use for test
Example results (modified to fit in 80 columes):
size=409600 n=100 4.58 3. timer avg= 4.41 stdv=0.195 0.0887 MiB/s 22.7 IOPs/sec
size=409600 n=100 4.84 4. timer avg= 4.52 stdv= 0.27 0.0885 MiB/s 22.15 IOPs/sec
"""
loops = 4 # minimum loops to average or see trends
num_blocks_to_write = 100 # Because sync writes are slow,
# don't do too many
args = ('-f %s -i %d -l %d -b12' %
(file, num_blocks_to_write, loops))
result = self._run('uwritesync', args)
r1 = re.search(r"([^\s]+ IOPs/sec).*$", result)
r2 = re.search(re_float, r1.group(0))
iops = r2.group(0)
self.write_perf_keyval({prefix + 'uwritesync_iops': iops})
def _disk_tests(self, prefix, dir, file):
"""Run this collection of disk tests
@param prefix: prefix to use on name/value pair for identifying results
@param dir: directory path to use for tests
@param file: file path to use for tests
"""
self._uread(prefix, file)
self._ureadrand(prefix, file)
self._uwrite(prefix, file)
self._uwriterand(prefix + '_large_', file, 8 * 1024 * 1024 * 1024)
# This tests sometimes gives invalid results
# self._uwriterand(prefix + '_small_', file, 8 * 1024)
self._uwritesync(prefix, file)
self._threadtree(prefix, dir)
def _ecryptfs(self):
"""Setup up to run disk tests on encrypted volume
"""
dir = '/usr/local/ecrypt_tst'
mnt = '/usr/local/ecrypt_mnt'
self._ecrypt_mount(dir, mnt)
self._disk_tests('ecryptfs_', mnt + '/_Dir', mnt + '/xyzzy')
self._ecrypt_unmount(dir, mnt)
def _parse_args(self, args):
"""Parse input arguments to this autotest.
Args:
@param args: List of arguments to parse.
@return
opts: Options, as per optparse.
args: Non-option arguments, as per optparse.
"""
parser = optparse.OptionParser()
parser.add_option('--disk', dest='want_disk_tests',
action='store_true', default=False,
help='Run disk tests.')
parser.add_option('--ecryptfs', dest='want_ecryptfs_tests',
action='store_true', default=False,
help='Run ecryptfs tests.')
parser.add_option('--mem', dest='want_mem_tests',
action='store_true', default=False,
help='Run memory tests.')
parser.add_option('--nop', dest='want_nop_tests',
action='store_true', default=False,
help='Do nothing.')
return parser.parse_args(args)
def run_once(self, args=[]):
"""Run the PyAuto performance tests.
@param args: Either space-separated arguments or a list of string
arguments. If this is a space separated string, we'll just
call split() on it to get a list. The list will be sent to
optparse for parsing.
"""
if isinstance(args, str):
args = args.split()
options, test_args = self._parse_args(args)
if test_args:
raise error.TestFail("Unknown args: %s" % repr(test_args))
if not os.path.exists(self.Bin):
raise error.TestFail("%s does not exist" % self.Bin)
try:
restart_swap = True
utils.system_output('swapoff /dev/zram0')
except:
restart_swap = False
utils.system_output('stop ui')
if options.want_nop_tests:
pass
if options.want_mem_tests:
self._memcpy_test()
self._memcpy()
if options.want_disk_tests:
self._disk_tests('ext4_', '/usr/local/_Dir', '/usr/local/xyzzy')
if options.want_ecryptfs_tests:
self._ecryptfs()
if restart_swap:
utils.system_output('swapon /dev/zram0')
utils.system_output('start ui')