| #!/usr/bin/env python3 |
| |
| from multiprocessing import Pool |
| import multiprocessing |
| import argparse |
| import tempfile |
| import logging |
| import os |
| import subprocess |
| |
| |
| def run_reproducer(path): |
| proc = subprocess.Popen([LLDB, '--replay', path], |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE) |
| reason = None |
| try: |
| outs, errs = proc.communicate(timeout=TIMEOUT) |
| success = proc.returncode == 0 |
| result = 'PASSED' if success else 'FAILED' |
| if not success: |
| outs = outs.decode() |
| errs = errs.decode() |
| # Do some pattern matching to find out the cause of the failure. |
| if 'Encountered unexpected packet during replay' in errs: |
| reason = 'Unexpected packet' |
| elif 'Assertion failed' in errs: |
| reason = 'Assertion failed' |
| elif 'UNREACHABLE' in errs: |
| reason = 'Unreachable executed' |
| elif 'Segmentation fault' in errs: |
| reason = 'Segmentation fault' |
| elif 'Illegal instruction' in errs: |
| reason = 'Illegal instruction' |
| else: |
| reason = f'Exit code {proc.returncode}' |
| except subprocess.TimeoutExpired: |
| proc.kill() |
| success = False |
| outs, errs = proc.communicate() |
| result = 'TIMEOUT' |
| |
| if not FAILURE_ONLY or not success: |
| reason_str = f' ({reason})' if reason else '' |
| print(f'{result}: {path}{reason_str}') |
| if VERBOSE: |
| if outs: |
| print(outs) |
| if errs: |
| print(errs) |
| |
| |
| def find_reproducers(path): |
| for root, dirs, files in os.walk(path): |
| for dir in dirs: |
| _, extension = os.path.splitext(dir) |
| if dir.startswith('Test') and extension == '.py': |
| yield os.path.join(root, dir) |
| |
| |
| if __name__ == '__main__': |
| parser = argparse.ArgumentParser( |
| description='LLDB API Test Replay Driver. ' |
| 'Replay one or more reproducers in parallel using the specified LLDB driver. ' |
| 'The script will look for reproducers generated by the API lit test suite. ' |
| 'To generate the reproducers, pass --param \'lldb-run-with-repro=capture\' to lit.' |
| ) |
| parser.add_argument( |
| '-j', |
| '--threads', |
| type=int, |
| default=multiprocessing.cpu_count(), |
| help='Number of threads. The number of CPU threads if not specified.') |
| parser.add_argument( |
| '-t', |
| '--timeout', |
| type=int, |
| default=60, |
| help='Replay timeout in seconds. 60 seconds if not specified.') |
| parser.add_argument( |
| '-p', |
| '--path', |
| type=str, |
| default=os.getcwd(), |
| help= |
| 'Path to the directory containing the reproducers. The current working directory if not specified.' |
| ) |
| parser.add_argument('-l', |
| '--lldb', |
| type=str, |
| required=True, |
| help='Path to the LLDB command line driver') |
| parser.add_argument('-v', |
| '--verbose', |
| help='Print replay output.', |
| action='store_true') |
| parser.add_argument('--failure-only', |
| help='Only log failures.', |
| action='store_true') |
| args = parser.parse_args() |
| |
| global LLDB |
| global TIMEOUT |
| global VERBOSE |
| global FAILURE_ONLY |
| LLDB = args.lldb |
| TIMEOUT = args.timeout |
| VERBOSE = args.verbose |
| FAILURE_ONLY = args.failure_only |
| |
| print( |
| f'Replaying reproducers in {args.path} with {args.threads} threads and a {args.timeout} seconds timeout' |
| ) |
| |
| try: |
| pool = Pool(args.threads) |
| pool.map(run_reproducer, find_reproducers(args.path)) |
| except KeyboardInterrupt: |
| print('Interrupted') |