blob: 459f117d0f062632363c8c9ea76e4d6bebb2ef46 [file] [log] [blame]
#!/usr/bin/python
# Copyright 2011 Google Inc. All Rights Reserved.
#
"""A tool for doing sequential and/or random disk reads/writes."""
__author__ = 'apenwarr@google.com (Avery Pennarun)'
import os
import os.path
import random
import subprocess
import sys
import time
import options
optspec = """
stress-disk [options...] <device-or-file-or-dir>
--
c,chunksize= Bytes per read() call, in bytes [1048576]
m,maxrate= Max chunks per second (default=unlimited)
p,print-interval= Seconds between printouts [1]
random Random blocks instead of sequential
w,write Write blocks into the file or dir (thus corrupting it)
no-delete If --write creates a file, don't delete it
destructive-rampage Allow --write to work on devices or files, not just dirs
"""
def Log(s, *args):
sys.stdout.flush()
if args:
sys.stderr.write((s + '\n') % args)
else:
sys.stderr.write(s + '\n')
sys.stderr.flush()
def main():
o = options.Options(optspec)
(opt, flags, extra) = o.parse(sys.argv[1:]) #pylint: disable-msg=W0612
if len(extra) != 1:
o.fatal('exactly one filename expected')
if opt.chunksize < 4096:
o.fatal('chunksize must be >= 4096')
filename = extra[0]
safe_to_write = opt.destructive_rampage
created = False
orig_filename = filename
if os.path.isdir(filename):
filename = os.path.join(filename, 'stress-disk.tmp')
if not os.path.exists(filename):
vfs = os.statvfs(orig_filename)
wantsize = vfs.f_bavail * vfs.f_bsize * 0.75
wantsize = min(wantsize, 100*1024*1024*1024) # limit to 100GB
Log('%s: allocating %dM.', filename, wantsize/1024/1024)
rv = subprocess.call(['hdparm', '--fallocate', '%d' % (wantsize/1024),
filename])
if rv:
raise Exception('hdparm returned code %d' % rv)
created = True
Log('%s: created.', filename)
safe_to_write = True
if opt.write and not safe_to_write:
o.fatal('--write can only write to a dir (or use --destructive-rampage)')
f = open(filename, opt.write and 'r+' or 'r')
if created and opt.delete:
# we still have the file open; this will ensure it gets deleted upon
# process exit.
os.unlink(filename)
fd = f.fileno()
size = os.lseek(fd, 0, os.SEEK_END)
if size < opt.chunksize:
o.fatal('%r: size (%r) must be >= chunksize (%r)'
% (filename, size, opt.chunksize))
t = last_print_time = time.time()
nbytes = 0
Log('%s %s, chunk=%d, rate=%s, %s',
opt.write and 'Writing to' or 'Reading from',
filename,
opt.chunksize,
opt.maxrate or 'unlimited',
opt.random and 'random' or 'sequential')
while 1:
if opt.random:
offset = random.randint(0, size - opt.chunksize)
os.lseek(fd, offset, os.SEEK_SET)
else:
offset = os.lseek(fd, 0, os.SEEK_CUR)
if offset + opt.chunksize > size:
os.lseek(fd, 0, os.SEEK_SET)
if opt.write:
assert safe_to_write
chunk = chr(offset & 0xff) * opt.chunksize
nbytes += os.write(fd, chunk)
else:
nbytes += len(os.read(fd, opt.chunksize))
now = time.time()
if opt.print_interval and now - last_print_time > opt.print_interval:
print ('%s %-15s %7.2fM in %5.2fs = %6.2fM/s'
% (opt.write and 'wr' or 'rd',
orig_filename + ':',
nbytes / 1024. / 1024.,
now - last_print_time,
nbytes / 1024. / 1024. / (now - last_print_time)))
sys.stdout.flush()
last_print_time = now
nbytes = 0
if opt.maxrate > 0:
t += 1.0 / opt.maxrate
while now < t:
time.sleep(t - now)
now = time.time()
if __name__ == '__main__':
main()