1*4882a593Smuzhiyun#! /usr/bin/env python3 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# Copyright (C) 2019 Garmin Ltd. 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun 8*4882a593Smuzhiyunimport argparse 9*4882a593Smuzhiyunimport hashlib 10*4882a593Smuzhiyunimport logging 11*4882a593Smuzhiyunimport os 12*4882a593Smuzhiyunimport pprint 13*4882a593Smuzhiyunimport sys 14*4882a593Smuzhiyunimport threading 15*4882a593Smuzhiyunimport time 16*4882a593Smuzhiyunimport warnings 17*4882a593Smuzhiyunwarnings.simplefilter("default") 18*4882a593Smuzhiyun 19*4882a593Smuzhiyuntry: 20*4882a593Smuzhiyun import tqdm 21*4882a593Smuzhiyun ProgressBar = tqdm.tqdm 22*4882a593Smuzhiyunexcept ImportError: 23*4882a593Smuzhiyun class ProgressBar(object): 24*4882a593Smuzhiyun def __init__(self, *args, **kwargs): 25*4882a593Smuzhiyun pass 26*4882a593Smuzhiyun 27*4882a593Smuzhiyun def __enter__(self): 28*4882a593Smuzhiyun return self 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun def __exit__(self, *args, **kwargs): 31*4882a593Smuzhiyun pass 32*4882a593Smuzhiyun 33*4882a593Smuzhiyun def update(self): 34*4882a593Smuzhiyun pass 35*4882a593Smuzhiyun 36*4882a593Smuzhiyunsys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)), 'lib')) 37*4882a593Smuzhiyun 38*4882a593Smuzhiyunimport hashserv 39*4882a593Smuzhiyun 40*4882a593SmuzhiyunDEFAULT_ADDRESS = 'unix://./hashserve.sock' 41*4882a593SmuzhiyunMETHOD = 'stress.test.method' 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun 44*4882a593Smuzhiyundef main(): 45*4882a593Smuzhiyun def handle_stats(args, client): 46*4882a593Smuzhiyun if args.reset: 47*4882a593Smuzhiyun s = client.reset_stats() 48*4882a593Smuzhiyun else: 49*4882a593Smuzhiyun s = client.get_stats() 50*4882a593Smuzhiyun pprint.pprint(s) 51*4882a593Smuzhiyun return 0 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun def handle_stress(args, client): 54*4882a593Smuzhiyun def thread_main(pbar, lock): 55*4882a593Smuzhiyun nonlocal found_hashes 56*4882a593Smuzhiyun nonlocal missed_hashes 57*4882a593Smuzhiyun nonlocal max_time 58*4882a593Smuzhiyun 59*4882a593Smuzhiyun client = hashserv.create_client(args.address) 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun for i in range(args.requests): 62*4882a593Smuzhiyun taskhash = hashlib.sha256() 63*4882a593Smuzhiyun taskhash.update(args.taskhash_seed.encode('utf-8')) 64*4882a593Smuzhiyun taskhash.update(str(i).encode('utf-8')) 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun start_time = time.perf_counter() 67*4882a593Smuzhiyun l = client.get_unihash(METHOD, taskhash.hexdigest()) 68*4882a593Smuzhiyun elapsed = time.perf_counter() - start_time 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun with lock: 71*4882a593Smuzhiyun if l: 72*4882a593Smuzhiyun found_hashes += 1 73*4882a593Smuzhiyun else: 74*4882a593Smuzhiyun missed_hashes += 1 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun max_time = max(elapsed, max_time) 77*4882a593Smuzhiyun pbar.update() 78*4882a593Smuzhiyun 79*4882a593Smuzhiyun max_time = 0 80*4882a593Smuzhiyun found_hashes = 0 81*4882a593Smuzhiyun missed_hashes = 0 82*4882a593Smuzhiyun lock = threading.Lock() 83*4882a593Smuzhiyun total_requests = args.clients * args.requests 84*4882a593Smuzhiyun start_time = time.perf_counter() 85*4882a593Smuzhiyun with ProgressBar(total=total_requests) as pbar: 86*4882a593Smuzhiyun threads = [threading.Thread(target=thread_main, args=(pbar, lock), daemon=False) for _ in range(args.clients)] 87*4882a593Smuzhiyun for t in threads: 88*4882a593Smuzhiyun t.start() 89*4882a593Smuzhiyun 90*4882a593Smuzhiyun for t in threads: 91*4882a593Smuzhiyun t.join() 92*4882a593Smuzhiyun 93*4882a593Smuzhiyun elapsed = time.perf_counter() - start_time 94*4882a593Smuzhiyun with lock: 95*4882a593Smuzhiyun print("%d requests in %.1fs. %.1f requests per second" % (total_requests, elapsed, total_requests / elapsed)) 96*4882a593Smuzhiyun print("Average request time %.8fs" % (elapsed / total_requests)) 97*4882a593Smuzhiyun print("Max request time was %.8fs" % max_time) 98*4882a593Smuzhiyun print("Found %d hashes, missed %d" % (found_hashes, missed_hashes)) 99*4882a593Smuzhiyun 100*4882a593Smuzhiyun if args.report: 101*4882a593Smuzhiyun with ProgressBar(total=args.requests) as pbar: 102*4882a593Smuzhiyun for i in range(args.requests): 103*4882a593Smuzhiyun taskhash = hashlib.sha256() 104*4882a593Smuzhiyun taskhash.update(args.taskhash_seed.encode('utf-8')) 105*4882a593Smuzhiyun taskhash.update(str(i).encode('utf-8')) 106*4882a593Smuzhiyun 107*4882a593Smuzhiyun outhash = hashlib.sha256() 108*4882a593Smuzhiyun outhash.update(args.outhash_seed.encode('utf-8')) 109*4882a593Smuzhiyun outhash.update(str(i).encode('utf-8')) 110*4882a593Smuzhiyun 111*4882a593Smuzhiyun client.report_unihash(taskhash.hexdigest(), METHOD, outhash.hexdigest(), taskhash.hexdigest()) 112*4882a593Smuzhiyun 113*4882a593Smuzhiyun with lock: 114*4882a593Smuzhiyun pbar.update() 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun parser = argparse.ArgumentParser(description='Hash Equivalence Client') 117*4882a593Smuzhiyun parser.add_argument('--address', default=DEFAULT_ADDRESS, help='Server address (default "%(default)s")') 118*4882a593Smuzhiyun parser.add_argument('--log', default='WARNING', help='Set logging level') 119*4882a593Smuzhiyun 120*4882a593Smuzhiyun subparsers = parser.add_subparsers() 121*4882a593Smuzhiyun 122*4882a593Smuzhiyun stats_parser = subparsers.add_parser('stats', help='Show server stats') 123*4882a593Smuzhiyun stats_parser.add_argument('--reset', action='store_true', 124*4882a593Smuzhiyun help='Reset server stats') 125*4882a593Smuzhiyun stats_parser.set_defaults(func=handle_stats) 126*4882a593Smuzhiyun 127*4882a593Smuzhiyun stress_parser = subparsers.add_parser('stress', help='Run stress test') 128*4882a593Smuzhiyun stress_parser.add_argument('--clients', type=int, default=10, 129*4882a593Smuzhiyun help='Number of simultaneous clients') 130*4882a593Smuzhiyun stress_parser.add_argument('--requests', type=int, default=1000, 131*4882a593Smuzhiyun help='Number of requests each client will perform') 132*4882a593Smuzhiyun stress_parser.add_argument('--report', action='store_true', 133*4882a593Smuzhiyun help='Report new hashes') 134*4882a593Smuzhiyun stress_parser.add_argument('--taskhash-seed', default='', 135*4882a593Smuzhiyun help='Include string in taskhash') 136*4882a593Smuzhiyun stress_parser.add_argument('--outhash-seed', default='', 137*4882a593Smuzhiyun help='Include string in outhash') 138*4882a593Smuzhiyun stress_parser.set_defaults(func=handle_stress) 139*4882a593Smuzhiyun 140*4882a593Smuzhiyun args = parser.parse_args() 141*4882a593Smuzhiyun 142*4882a593Smuzhiyun logger = logging.getLogger('hashserv') 143*4882a593Smuzhiyun 144*4882a593Smuzhiyun level = getattr(logging, args.log.upper(), None) 145*4882a593Smuzhiyun if not isinstance(level, int): 146*4882a593Smuzhiyun raise ValueError('Invalid log level: %s' % args.log) 147*4882a593Smuzhiyun 148*4882a593Smuzhiyun logger.setLevel(level) 149*4882a593Smuzhiyun console = logging.StreamHandler() 150*4882a593Smuzhiyun console.setLevel(level) 151*4882a593Smuzhiyun logger.addHandler(console) 152*4882a593Smuzhiyun 153*4882a593Smuzhiyun func = getattr(args, 'func', None) 154*4882a593Smuzhiyun if func: 155*4882a593Smuzhiyun client = hashserv.create_client(args.address) 156*4882a593Smuzhiyun 157*4882a593Smuzhiyun return func(args, client) 158*4882a593Smuzhiyun 159*4882a593Smuzhiyun return 0 160*4882a593Smuzhiyun 161*4882a593Smuzhiyun 162*4882a593Smuzhiyunif __name__ == '__main__': 163*4882a593Smuzhiyun try: 164*4882a593Smuzhiyun ret = main() 165*4882a593Smuzhiyun except Exception: 166*4882a593Smuzhiyun ret = 1 167*4882a593Smuzhiyun import traceback 168*4882a593Smuzhiyun traceback.print_exc() 169*4882a593Smuzhiyun sys.exit(ret) 170