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