xref: /llvm-project/lldb/scripts/reproducer-replay.py (revision 602e47c5f9fd2e14c7bfb6111e6558fa0d27c87f)
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