def main():
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, description='Run the fuzz targets with all inputs from the seed_dir once.')
    parser.add_argument('-l', '--loglevel', dest='loglevel', default='INFO', help='log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console.')
    parser.add_argument('--valgrind', action='store_true', help='If true, run fuzzing binaries under the valgrind memory error detector')
    parser.add_argument('-x', '--exclude', help='A comma-separated list of targets to exclude')
    parser.add_argument('--par', '-j', type=int, default=4, help='How many targets to merge or execute in parallel.')
    parser.add_argument('seed_dir', help='The seed corpus to run on (must contain subfolders for each fuzz target).')
    parser.add_argument('target', nargs='*', help='The target(s) to run. Default is to run all targets.')
    parser.add_argument('--m_dir', help='Merge inputs from this directory into the seed_dir. Needs /target subdirectory.')
    parser.add_argument('-g', '--generate', action='store_true', help='Create new corpus seeds (or extend the existing ones) by running the given targets for a finite number of times. Outputs them to the passed seed_dir.')
    args = parser.parse_args()
    logging.basicConfig(format='%(message)s', level=(int(args.loglevel) if args.loglevel.isdigit() else args.loglevel.upper()))
    config = configparser.ConfigParser()
    configfile = (os.path.abspath(os.path.dirname(__file__)) + '/../config.ini')
    config.read_file(open(configfile, encoding='utf8'))
    if (not config['components'].getboolean('ENABLE_FUZZ')):
        logging.error('Must have fuzz targets built')
        sys.exit(1)
    test_list_all = parse_test_list(makefile=os.path.join(config['environment']['SRCDIR'], 'src', 'Makefile.test.include'))
    if (not test_list_all):
        logging.error('No fuzz targets found')
        sys.exit(1)
    logging.debug('{} fuzz target(s) found: {}'.format(len(test_list_all), ' '.join(sorted(test_list_all))))
    args.target = (args.target or test_list_all)
    test_list_error = list(set(args.target).difference(set(test_list_all)))
    if test_list_error:
        logging.error('Unknown fuzz targets selected: {}'.format(test_list_error))
    test_list_selection = list(set(test_list_all).intersection(set(args.target)))
    if (not test_list_selection):
        logging.error('No fuzz targets selected')
    if args.exclude:
        for excluded_target in args.exclude.split(','):
            if (excluded_target not in test_list_selection):
                logging.error('Target "{}" not found in current target list.'.format(excluded_target))
                continue
            test_list_selection.remove(excluded_target)
    test_list_selection.sort()
    logging.info('{} of {} detected fuzz target(s) selected: {}'.format(len(test_list_selection), len(test_list_all), ' '.join(test_list_selection)))
    if (not args.generate):
        test_list_seedless = []
        for t in test_list_selection:
            corpus_path = os.path.join(args.seed_dir, t)
            if ((not os.path.exists(corpus_path)) or (len(os.listdir(corpus_path)) == 0)):
                test_list_seedless.append(t)
        test_list_seedless.sort()
        if test_list_seedless:
            logging.info('Fuzzing harnesses lacking a seed corpus: {}'.format(' '.join(test_list_seedless)))
            logging.info('Please consider adding a fuzz seed corpus at https://github.com/crown-core/qa-assets')
    try:
        help_output = subprocess.run(args=[os.path.join(config['environment']['BUILDDIR'], 'src', 'test', 'fuzz', test_list_selection[0]), '-help=1'], timeout=20, check=True, stderr=subprocess.PIPE, universal_newlines=True).stderr
        if ('libFuzzer' not in help_output):
            logging.error('Must be built with libFuzzer')
            sys.exit(1)
    except subprocess.TimeoutExpired:
        logging.error('subprocess timed out: Currently only libFuzzer is supported')
        sys.exit(1)
    with ThreadPoolExecutor(max_workers=args.par) as fuzz_pool:
        if args.generate:
            return generate_corpus_seeds(fuzz_pool=fuzz_pool, build_dir=config['environment']['BUILDDIR'], seed_dir=args.seed_dir, targets=test_list_selection)
        if args.m_dir:
            merge_inputs(fuzz_pool=fuzz_pool, corpus=args.seed_dir, test_list=test_list_selection, build_dir=config['environment']['BUILDDIR'], merge_dir=args.m_dir)
            return
        run_once(fuzz_pool=fuzz_pool, corpus=args.seed_dir, test_list=test_list_selection, build_dir=config['environment']['BUILDDIR'], use_valgrind=args.valgrind)
def main():
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, description='Run the fuzz targets with all inputs from the seed_dir once.')
    parser.add_argument('-l', '--loglevel', dest='loglevel', default='INFO', help='log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console.')
    parser.add_argument('--valgrind', action='store_true', help='If true, run fuzzing binaries under the valgrind memory error detector')
    parser.add_argument('-x', '--exclude', help='A comma-separated list of targets to exclude')
    parser.add_argument('--par', '-j', type=int, default=4, help='How many targets to merge or execute in parallel.')
    parser.add_argument('seed_dir', help='The seed corpus to run on (must contain subfolders for each fuzz target).')
    parser.add_argument('target', nargs='*', help='The target(s) to run. Default is to run all targets.')
    parser.add_argument('--m_dir', help='Merge inputs from this directory into the seed_dir. Needs /target subdirectory.')
    parser.add_argument('-g', '--generate', action='store_true', help='Create new corpus seeds (or extend the existing ones) by running the given targets for a finite number of times. Outputs them to the passed seed_dir.')
    args = parser.parse_args()
    logging.basicConfig(format='%(message)s', level=(int(args.loglevel) if args.loglevel.isdigit() else args.loglevel.upper()))
    config = configparser.ConfigParser()
    configfile = (os.path.abspath(os.path.dirname(__file__)) + '/../config.ini')
    config.read_file(open(configfile, encoding='utf8'))
    if (not config['components'].getboolean('ENABLE_FUZZ')):
        logging.error('Must have fuzz targets built')
        sys.exit(1)
    test_list_all = parse_test_list(makefile=os.path.join(config['environment']['SRCDIR'], 'src', 'Makefile.test.include'))
    if (not test_list_all):
        logging.error('No fuzz targets found')
        sys.exit(1)
    logging.debug('{} fuzz target(s) found: {}'.format(len(test_list_all), ' '.join(sorted(test_list_all))))
    args.target = (args.target or test_list_all)
    test_list_error = list(set(args.target).difference(set(test_list_all)))
    if test_list_error:
        logging.error('Unknown fuzz targets selected: {}'.format(test_list_error))
    test_list_selection = list(set(test_list_all).intersection(set(args.target)))
    if (not test_list_selection):
        logging.error('No fuzz targets selected')
    if args.exclude:
        for excluded_target in args.exclude.split(','):
            if (excluded_target not in test_list_selection):
                logging.error('Target "{}" not found in current target list.'.format(excluded_target))
                continue
            test_list_selection.remove(excluded_target)
    test_list_selection.sort()
    logging.info('{} of {} detected fuzz target(s) selected: {}'.format(len(test_list_selection), len(test_list_all), ' '.join(test_list_selection)))
    if (not args.generate):
        test_list_seedless = []
        for t in test_list_selection:
            corpus_path = os.path.join(args.seed_dir, t)
            if ((not os.path.exists(corpus_path)) or (len(os.listdir(corpus_path)) == 0)):
                test_list_seedless.append(t)
        test_list_seedless.sort()
        if test_list_seedless:
            logging.info('Fuzzing harnesses lacking a seed corpus: {}'.format(' '.join(test_list_seedless)))
            logging.info('Please consider adding a fuzz seed corpus at https://github.com/crown-core/qa-assets')
    try:
        help_output = subprocess.run(args=[os.path.join(config['environment']['BUILDDIR'], 'src', 'test', 'fuzz', test_list_selection[0]), '-help=1'], timeout=20, check=True, stderr=subprocess.PIPE, universal_newlines=True).stderr
        if ('libFuzzer' not in help_output):
            logging.error('Must be built with libFuzzer')
            sys.exit(1)
    except subprocess.TimeoutExpired:
        logging.error('subprocess timed out: Currently only libFuzzer is supported')
        sys.exit(1)
    with ThreadPoolExecutor(max_workers=args.par) as fuzz_pool:
        if args.generate:
            return generate_corpus_seeds(fuzz_pool=fuzz_pool, build_dir=config['environment']['BUILDDIR'], seed_dir=args.seed_dir, targets=test_list_selection)
        if args.m_dir:
            merge_inputs(fuzz_pool=fuzz_pool, corpus=args.seed_dir, test_list=test_list_selection, build_dir=config['environment']['BUILDDIR'], merge_dir=args.m_dir)
            return
        run_once(fuzz_pool=fuzz_pool, corpus=args.seed_dir, test_list=test_list_selection, build_dir=config['environment']['BUILDDIR'], use_valgrind=args.valgrind)