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