Alien-Libjio
view release on metacpan or search on metacpan
libjio/tests/stress/jiostress view on Meta::CPAN
#!/usr/bin/env python3
"""
This application is a stress tester for libjio. It's not a traditional stress
test like fsx (which can be used to test libjio using the preloading library),
but uses fault injection to check how the library behaves under random
failures.
"""
import sys
import os
import time
import select
import random
import fcntl
import traceback
from optparse import OptionParser
import libjio
try:
import fiu
except ImportError:
print()
print("Error: unable to load fiu module. This test needs libfiu")
print("support. Please install libfiu and recompile libjio with FI=1.")
print()
raise
#
# Auxiliary stuff
#
gbcount = 0
def getbytes(n):
global gbcount
gbcount = (gbcount + 1) % 10
return bytes(str(gbcount) * n, 'ascii')
def randfrange(maxend, maxsize):
start = random.randint(0, maxend - 1)
size = random.randint(0, (maxend - 1) - start) % maxsize
return start, start + size
def randfloat(min, max):
return min + random.random() % (max - min)
class ConsistencyError (Exception):
pass
def jfsck(fname, cleanup = False):
flags = 0
if cleanup:
flags = libjio.J_CLEANUP
try:
r = libjio.jfsck(fname, flags = flags)
return r
except IOError as e:
if e.args[0] == libjio.J_ENOJOURNAL:
return { 'total': 0 }
else:
raise
def comp_cont(bytes):
"'aaaabbcc' -> [ ('a', 4), ('b', 2), ('c', 2) ]"
l = []
prev = bytes[0]
c = 1
for b in bytes[1:]:
if (b == prev):
c += 1
continue
libjio/tests/stress/jiostress view on Meta::CPAN
return r
#
# Output handler, used to get a nice output when using multiple processes
#
class OutputHandler:
def __init__(self, every):
# fds to read from
self.rs = []
# we will report every this number of seconds
self.every = every
# how many transactions has each child processed; we use the
# read end of the pipe to identify them
self.ntrans = {}
# like self.ntrans but counts only the failed ones
self.nfailures = {}
# fd to write to, only relevant in the child
self.w = None
# p = parent, c = child
self.end = 'p'
# last transaction number print
self.last_print = 0
# time of the last print
self.last_print_time = 0
def prefork(self):
r, w = os.pipe()
self.rs.append(r)
self.ntrans[r] = 0
self.nfailures[r] = 0
self.w = w
def child(self):
self.end = 'c'
os.close(self.rs[-1])
self.rs = []
def parent(self):
os.close(self.w)
self.w = None
SUCCESS = bytes('1', encoding = 'ascii')
FAILURE = bytes('0', encoding = 'ascii')
def feed(self, success = True):
if success:
os.write(self.w, OutputHandler.SUCCESS)
else:
os.write(self.w, OutputHandler.FAILURE)
def output_loop(self):
while self.rs:
rr, rw, rx = select.select(self.rs, [], [], 1)
for r in rr:
d = os.read(r, 1)
if not d:
self.rs.remove(r)
else:
self.ntrans[r] += 1
if d == OutputHandler.FAILURE:
self.nfailures[r] += 1
self.cond_print()
self.print()
return sum(self.ntrans.values()), sum(self.nfailures.values())
def cond_print(self):
if time.time() - self.last_print_time >= self.every:
self.print()
def print(self):
self.last_print_time = time.time()
for r in sorted(self.ntrans):
print("%4d" % self.ntrans[r], end = ' ')
print()
#
# Lock manager, used to lock ranges between multiple processes
#
# We can't lock the real file because that would ruin libjio's locking, so we
# create a new file, remove it, and use fcntl locking. Not very elegant but it
# does the trick.
#
class VoidLockManager:
def __init__(self):
pass
def lock(self, start, end):
pass
def unlock(self, start, end):
pass
class LockManager:
def __init__(self):
fname = "/tmp/js-lock-tmp." + str(os.getpid())
self.fd = open(fname, 'w+')
os.unlink(fname)
def lock(self, start, end):
#print(os.getpid(), '\tlock:', start, end)
#sys.stdout.flush()
fcntl.lockf(self.fd, fcntl.LOCK_EX, end - start, start)
def unlock(self, start, end):
#print(os.getpid(), '\tunlock:', start, end)
#sys.stdout.flush()
fcntl.lockf(self.fd, fcntl.LOCK_UN, end - start, start)
#
libjio/tests/stress/jiostress view on Meta::CPAN
child_nops = nops - int(nops / nproc) * i
output.prefork()
sys.stdout.flush()
pid = os.fork()
if pid == 0:
# child
output.child()
s = Stresser(fname, fsize, child_nops, use_fi, use_as,
output, lockmgr, do_verify)
s.run()
sys.exit(0)
else:
output.parent()
pids.append(pid)
print("Launched stress tests")
totalops, nfailures = output.output_loop()
print("Stress test completed, waiting for children")
nerrors = 0
for pid in pids:
rpid, status = os.waitpid(pid, 0)
if os.WEXITSTATUS(status) != 0:
nerrors += 1
print(" %d operations" % totalops)
print(" %d simulated failures" % nfailures)
print(" %d processes ended with errors" % nerrors)
if nerrors:
return False
return True
def main():
usage = "Use: %prog [options] <file name> <file size in Mb>"
parser = OptionParser(usage = usage)
parser.add_option("-n", "--nops", dest = "nops", type = "int",
default = 100,
help = "number of operations (defaults to %default)")
parser.add_option("-p", "--nproc", dest = "nproc", type = "int",
default = 1,
help = "number of processes (defaults to %default)")
parser.add_option("", "--fi", dest = "use_fi",
action = "store_true", default = False,
help = "use fault injection (conflicts with --as and -p > 1)")
parser.add_option("", "--as", dest = "use_as",
action = "store_true", default = False,
help = "use J_LINGER + autosync (conflicts with --fi)")
parser.add_option("", "--no-internal-lock",
dest = "use_internal_locks", action = "store_false",
default = True,
help = "do not lock internally, disables verification")
parser.add_option("", "--no-verify", dest = "do_verify",
action = "store_false", default = True,
help = "do not perform verifications")
parser.add_option("", "--keep", dest = "keep",
action = "store_true", default = False,
help = "keep the file after completing the test")
parser.add_option("", "--force", dest = "force",
action = "store_true", default = False,
help = "force the tests to run, even if conflicting"
+ " options are selected")
options, args = parser.parse_args()
if len(args) != 2:
parser.print_help()
return 1
fname = args[0]
try:
fsize = int(args[1]) * 1024 * 1024
except ValueError:
print("Error: the size of the file must be numeric")
return 1
if not options.force:
if options.use_fi and options.use_as:
print("Error: --fi and --as cannot be used together")
return 1
if options.use_fi and options.nproc > 1:
print("Error: --fi cannot be used with multiple processes")
return 1
if not options.use_internal_locks:
options.do_verify = False
output = OutputHandler(every = 2)
if options.use_internal_locks:
lockmgr = LockManager()
else:
lockmgr = VoidLockManager()
success = run_stressers(options.nproc, fname, fsize, options.nops,
options.use_fi, options.use_as, output, lockmgr,
options.do_verify)
r = jfsck(fname)
print("Final check completed")
if success and not options.keep:
jfsck(fname, cleanup = True)
os.unlink(fname)
if not success:
print("Test failed")
return 1
return 0
if __name__ == '__main__':
sys.exit(main())
( run in 0.601 second using v1.01-cache-2.11-cpan-63c85eba8c4 )