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( 14 [LLDB, "--replay", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE 15 ) 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 ) 75 parser.add_argument( 76 "-t", 77 "--timeout", 78 type=int, 79 default=60, 80 help="Replay timeout in seconds. 60 seconds if not specified.", 81 ) 82 parser.add_argument( 83 "-p", 84 "--path", 85 type=str, 86 default=os.getcwd(), 87 help="Path to the directory containing the reproducers. The current working directory if not specified.", 88 ) 89 parser.add_argument( 90 "-l", 91 "--lldb", 92 type=str, 93 required=True, 94 help="Path to the LLDB command line driver", 95 ) 96 parser.add_argument( 97 "-v", "--verbose", help="Print replay output.", action="store_true" 98 ) 99 parser.add_argument( 100 "--failure-only", help="Only log failures.", action="store_true" 101 ) 102 args = parser.parse_args() 103 104 global LLDB 105 global TIMEOUT 106 global VERBOSE 107 global FAILURE_ONLY 108 LLDB = args.lldb 109 TIMEOUT = args.timeout 110 VERBOSE = args.verbose 111 FAILURE_ONLY = args.failure_only 112 113 print( 114 f"Replaying reproducers in {args.path} with {args.threads} threads and a {args.timeout} seconds timeout" 115 ) 116 117 try: 118 pool = Pool(args.threads) 119 pool.map(run_reproducer, find_reproducers(args.path)) 120 except KeyboardInterrupt: 121 print("Interrupted") 122