reproducer-replay.py 3.83 KB
#!/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')