1*dda28197Spatrick#!/usr/bin/env python3 2*dda28197Spatrick 3*dda28197Spatrickfrom multiprocessing import Pool 4*dda28197Spatrickimport multiprocessing 5*dda28197Spatrickimport argparse 6*dda28197Spatrickimport tempfile 7*dda28197Spatrickimport logging 8*dda28197Spatrickimport os 9*dda28197Spatrickimport subprocess 10*dda28197Spatrick 11*dda28197Spatrick 12*dda28197Spatrickdef run_reproducer(path): 13*dda28197Spatrick proc = subprocess.Popen([LLDB, '--replay', path], 14*dda28197Spatrick stdout=subprocess.PIPE, 15*dda28197Spatrick stderr=subprocess.PIPE) 16*dda28197Spatrick reason = None 17*dda28197Spatrick try: 18*dda28197Spatrick outs, errs = proc.communicate(timeout=TIMEOUT) 19*dda28197Spatrick success = proc.returncode == 0 20*dda28197Spatrick result = 'PASSED' if success else 'FAILED' 21*dda28197Spatrick if not success: 22*dda28197Spatrick outs = outs.decode() 23*dda28197Spatrick errs = errs.decode() 24*dda28197Spatrick # Do some pattern matching to find out the cause of the failure. 25*dda28197Spatrick if 'Encountered unexpected packet during replay' in errs: 26*dda28197Spatrick reason = 'Unexpected packet' 27*dda28197Spatrick elif 'Assertion failed' in errs: 28*dda28197Spatrick reason = 'Assertion failed' 29*dda28197Spatrick elif 'UNREACHABLE' in errs: 30*dda28197Spatrick reason = 'Unreachable executed' 31*dda28197Spatrick elif 'Segmentation fault' in errs: 32*dda28197Spatrick reason = 'Segmentation fault' 33*dda28197Spatrick elif 'Illegal instruction' in errs: 34*dda28197Spatrick reason = 'Illegal instruction' 35*dda28197Spatrick else: 36*dda28197Spatrick reason = f'Exit code {proc.returncode}' 37*dda28197Spatrick except subprocess.TimeoutExpired: 38*dda28197Spatrick proc.kill() 39*dda28197Spatrick success = False 40*dda28197Spatrick outs, errs = proc.communicate() 41*dda28197Spatrick result = 'TIMEOUT' 42*dda28197Spatrick 43*dda28197Spatrick if not FAILURE_ONLY or not success: 44*dda28197Spatrick reason_str = f' ({reason})' if reason else '' 45*dda28197Spatrick print(f'{result}: {path}{reason_str}') 46*dda28197Spatrick if VERBOSE: 47*dda28197Spatrick if outs: 48*dda28197Spatrick print(outs) 49*dda28197Spatrick if errs: 50*dda28197Spatrick print(errs) 51*dda28197Spatrick 52*dda28197Spatrick 53*dda28197Spatrickdef find_reproducers(path): 54*dda28197Spatrick for root, dirs, files in os.walk(path): 55*dda28197Spatrick for dir in dirs: 56*dda28197Spatrick _, extension = os.path.splitext(dir) 57*dda28197Spatrick if dir.startswith('Test') and extension == '.py': 58*dda28197Spatrick yield os.path.join(root, dir) 59*dda28197Spatrick 60*dda28197Spatrick 61*dda28197Spatrickif __name__ == '__main__': 62*dda28197Spatrick parser = argparse.ArgumentParser( 63*dda28197Spatrick description='LLDB API Test Replay Driver. ' 64*dda28197Spatrick 'Replay one or more reproducers in parallel using the specified LLDB driver. ' 65*dda28197Spatrick 'The script will look for reproducers generated by the API lit test suite. ' 66*dda28197Spatrick 'To generate the reproducers, pass --param \'lldb-run-with-repro=capture\' to lit.' 67*dda28197Spatrick ) 68*dda28197Spatrick parser.add_argument( 69*dda28197Spatrick '-j', 70*dda28197Spatrick '--threads', 71*dda28197Spatrick type=int, 72*dda28197Spatrick default=multiprocessing.cpu_count(), 73*dda28197Spatrick help='Number of threads. The number of CPU threads if not specified.') 74*dda28197Spatrick parser.add_argument( 75*dda28197Spatrick '-t', 76*dda28197Spatrick '--timeout', 77*dda28197Spatrick type=int, 78*dda28197Spatrick default=60, 79*dda28197Spatrick help='Replay timeout in seconds. 60 seconds if not specified.') 80*dda28197Spatrick parser.add_argument( 81*dda28197Spatrick '-p', 82*dda28197Spatrick '--path', 83*dda28197Spatrick type=str, 84*dda28197Spatrick default=os.getcwd(), 85*dda28197Spatrick help= 86*dda28197Spatrick 'Path to the directory containing the reproducers. The current working directory if not specified.' 87*dda28197Spatrick ) 88*dda28197Spatrick parser.add_argument('-l', 89*dda28197Spatrick '--lldb', 90*dda28197Spatrick type=str, 91*dda28197Spatrick required=True, 92*dda28197Spatrick help='Path to the LLDB command line driver') 93*dda28197Spatrick parser.add_argument('-v', 94*dda28197Spatrick '--verbose', 95*dda28197Spatrick help='Print replay output.', 96*dda28197Spatrick action='store_true') 97*dda28197Spatrick parser.add_argument('--failure-only', 98*dda28197Spatrick help='Only log failures.', 99*dda28197Spatrick action='store_true') 100*dda28197Spatrick args = parser.parse_args() 101*dda28197Spatrick 102*dda28197Spatrick global LLDB 103*dda28197Spatrick global TIMEOUT 104*dda28197Spatrick global VERBOSE 105*dda28197Spatrick global FAILURE_ONLY 106*dda28197Spatrick LLDB = args.lldb 107*dda28197Spatrick TIMEOUT = args.timeout 108*dda28197Spatrick VERBOSE = args.verbose 109*dda28197Spatrick FAILURE_ONLY = args.failure_only 110*dda28197Spatrick 111*dda28197Spatrick print( 112*dda28197Spatrick f'Replaying reproducers in {args.path} with {args.threads} threads and a {args.timeout} seconds timeout' 113*dda28197Spatrick ) 114*dda28197Spatrick 115*dda28197Spatrick try: 116*dda28197Spatrick pool = Pool(args.threads) 117*dda28197Spatrick pool.map(run_reproducer, find_reproducers(args.path)) 118*dda28197Spatrick except KeyboardInterrupt: 119*dda28197Spatrick print('Interrupted') 120