Alien-Libjio

 view release on metacpan or  search on metacpan

libjio/tests/stress/jiostress  view on Meta::CPAN

	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

		l.append((prev, c))
		prev = b
		c = 1
	l.append((b, c))
	return l

def pread(fd, start, end):
	ppos = fd.tell()
	fd.seek(start, 0)
	r = bytes()
	c = 0
	total = end - start
	while c < total:
		n = fd.read(total - c)
		if (n == ''):
			break
		c += len(n)
		r += n
	fd.seek(ppos, 0)
	assert c == end - start
	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)


#
# A range of bytes inside a file, used inside the transactions
#
# Note it can't "remember" the fd as it may change between prepare() and
# verify().
#

class Range:
	def __init__(self, fsize, maxlen, lockmgr):
		# public
		self.start, self.end = randfrange(fsize, maxlen)
		self.new_data = None
		self.type = 'r'

		# private
		self.prev_data = None
		self.new_data_ctx = None
		self.read_buf = None
		self.lockmgr = lockmgr
		self.locked = False

		# read an extended range so we can check we
		# only wrote what we were supposed to
		self.ext_start = max(0, self.start - 32)
		self.ext_end = min(fsize, self.end + 32)

	def __lt__(self, other):

libjio/tests/stress/jiostress  view on Meta::CPAN

	def fiu_enable(self):
		if not self.use_fi:
			return

		# To improve code coverage, we randomize the probability each
		# time we enable failure points
		fiu.enable_random('jio/*',
				probability = randfloat(0.0005, 0.005))
		fiu.enable_random('linux/*',
				probability = randfloat(0.005, 0.03))
		fiu.enable_random('posix/*',
			probability = randfloat(0.005, 0.03))
		fiu.enable_random('libc/mm/*',
			probability = randfloat(0.003, 0.07))
		fiu.enable_random('libc/str/*',
			probability = randfloat(0.005, 0.07))

	def fiu_disable(self):
		if self.use_fi:
			fiu.disable('libc/mm/*')
			fiu.disable('posix/*')
			fiu.disable('jio/*')
			fiu.disable('linux/*')

	def run(self):
		nfailures = 0

		for i in range(1, self.nops + 1):
			trans = random.choice(t_list)(self.f, self.jf,
					self.fsize, self.lockmgr,
					self.do_verify)

			if self.use_fi:
				r = self.apply_fork(trans)
			else:
				r = self.apply(trans)

			if r:
				self.output.feed(success = True)
			else:
				self.output.feed(success = False)
				nfailures += 1
				r = self.reopen(trans)
				trans.verify(write_only = True)

		return nfailures


#
# Main
#

def run_stressers(nproc, fname, fsize, nops, use_fi, use_as, output, lockmgr,
		do_verify):
	pids = []
	print("Launching stress test")
	for i in range(nproc):
		# Calculate how many operations will this child perform. The
		# last one will work a little more so we get exactly nops.
		# Note we prefer to work extra in the end rather than having
		# the last process with 0 child_nops, that's why we use int()
		# instead of round() or ceil().
		child_nops = int(nops / nproc)
		if i == nproc - 1:
			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.789 second using v1.01-cache-2.11-cpan-df04353d9ac )