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