| #!/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") |