diff --git a/lldb/scripts/reproducer-replay.py b/lldb/scripts/reproducer-replay.py new file mode 100755 --- /dev/null +++ b/lldb/scripts/reproducer-replay.py @@ -0,0 +1,100 @@ +#! /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) + result = 'PASSED' if proc.returncode == 0 else 'FAILED' + if proc.returncode != 0: + 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() + outs, errs = proc.communicate() + result = 'TIMEOUT' + + reason_str = f' ({reason})' if reason else '' + print(f'{result}: {path}{reason_str}') + + +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') + args = parser.parse_args() + + global LLDB + global TIMEOUT + LLDB = args.lldb + TIMEOUT = args.timeout + + 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')