164562Sgshapiro#!/usr/bin/env python3
264562Sgshapiro
338032Speterfrom multiprocessing import Pool
438032Speterimport multiprocessing
538032Speterimport argparse
638032Speterimport tempfile
738032Speterimport logging
838032Speterimport os
938032Speterimport subprocess
1038032Speter
1138032Speter
1264562Sgshapirodef run_reproducer(path):
1338032Speter    proc = subprocess.Popen([LLDB, '--replay', path],
1438032Speter                            stdout=subprocess.PIPE,
1538032Speter                            stderr=subprocess.PIPE)
1638032Speter    reason = None
1738032Speter    try:
1838032Speter        outs, errs = proc.communicate(timeout=TIMEOUT)
1938032Speter        success = proc.returncode == 0
2064562Sgshapiro        result = 'PASSED' if success else 'FAILED'
2138032Speter        if not success:
2238032Speter            outs = outs.decode()
2338032Speter            errs = errs.decode()
2438032Speter            # Do some pattern matching to find out the cause of the failure.
2538032Speter            if 'Encountered unexpected packet during replay' in errs:
2638032Speter                reason = 'Unexpected packet'
2738032Speter            elif 'Assertion failed' in errs:
2838032Speter                reason = 'Assertion failed'
2938032Speter            elif 'UNREACHABLE' in errs:
3038032Speter                reason = 'Unreachable executed'
3138032Speter            elif 'Segmentation fault' in errs:
3238032Speter                reason = 'Segmentation fault'
3338032Speter            elif 'Illegal instruction' in errs:
3438032Speter                reason = 'Illegal instruction'
3538032Speter            else:
3638032Speter                reason = f'Exit code {proc.returncode}'
3738032Speter    except subprocess.TimeoutExpired:
3838032Speter        proc.kill()
3938032Speter        success = False
4038032Speter        outs, errs = proc.communicate()
4138032Speter        result = 'TIMEOUT'
4238032Speter
4338032Speter    if not FAILURE_ONLY or not success:
4438032Speter        reason_str = f' ({reason})' if reason else ''
4538032Speter        print(f'{result}: {path}{reason_str}')
4638032Speter        if VERBOSE:
4738032Speter            if outs:
4838032Speter                print(outs)
4938032Speter            if errs:
5038032Speter                print(errs)
5138032Speter
5238032Speter
5338032Speterdef find_reproducers(path):
5438032Speter    for root, dirs, files in os.walk(path):
5538032Speter        for dir in dirs:
5638032Speter            _, extension = os.path.splitext(dir)
5738032Speter            if dir.startswith('Test') and extension == '.py':
5838032Speter                yield os.path.join(root, dir)
5938032Speter
6038032Speter
6138032Speterif __name__ == '__main__':
6238032Speter    parser = argparse.ArgumentParser(
6338032Speter        description='LLDB API Test Replay Driver. '
6438032Speter        'Replay one or more reproducers in parallel using the specified LLDB driver. '
6538032Speter        'The script will look for reproducers generated by the API lit test suite. '
6638032Speter        'To generate the reproducers, pass --param \'lldb-run-with-repro=capture\' to lit.'
6738032Speter    )
6838032Speter    parser.add_argument(
6938032Speter        '-j',
7038032Speter        '--threads',
7138032Speter        type=int,
7238032Speter        default=multiprocessing.cpu_count(),
7338032Speter        help='Number of threads. The number of CPU threads if not specified.')
7464562Sgshapiro    parser.add_argument(
7538032Speter        '-t',
7638032Speter        '--timeout',
7738032Speter        type=int,
7838032Speter        default=60,
7938032Speter        help='Replay timeout in seconds. 60 seconds if not specified.')
8038032Speter    parser.add_argument(
8138032Speter        '-p',
8264562Sgshapiro        '--path',
8338032Speter        type=str,
8438032Speter        default=os.getcwd(),
8538032Speter        help=
8638032Speter        'Path to the directory containing the reproducers. The current working directory if not specified.'
8738032Speter    )
8838032Speter    parser.add_argument('-l',
8938032Speter                        '--lldb',
9038032Speter                        type=str,
9138032Speter                        required=True,
9238032Speter                        help='Path to the LLDB command line driver')
9338032Speter    parser.add_argument('-v',
9438032Speter                        '--verbose',
9538032Speter                        help='Print replay output.',
9638032Speter                        action='store_true')
9738032Speter    parser.add_argument('--failure-only',
9838032Speter                        help='Only log failures.',
9938032Speter                        action='store_true')
10038032Speter    args = parser.parse_args()
10138032Speter
10238032Speter    global LLDB
10338032Speter    global TIMEOUT
10438032Speter    global VERBOSE
10538032Speter    global FAILURE_ONLY
10638032Speter    LLDB = args.lldb
10764562Sgshapiro    TIMEOUT = args.timeout
10838032Speter    VERBOSE = args.verbose
10938032Speter    FAILURE_ONLY = args.failure_only
11038032Speter
11138032Speter    print(
11238032Speter        f'Replaying reproducers in {args.path} with {args.threads} threads and a {args.timeout} seconds timeout'
11338032Speter    )
11438032Speter
11538032Speter    try:
11638032Speter        pool = Pool(args.threads)
11738032Speter        pool.map(run_reproducer, find_reproducers(args.path))
11864562Sgshapiro    except KeyboardInterrupt:
11964562Sgshapiro        print('Interrupted')
12064562Sgshapiro