119ea8026Sopenharmony_ci#!/usr/bin/env python3
219ea8026Sopenharmony_ci#
319ea8026Sopenharmony_ci# Script to compile and runs benches.
419ea8026Sopenharmony_ci#
519ea8026Sopenharmony_ci# Example:
619ea8026Sopenharmony_ci# ./scripts/bench.py runners/bench_runner -b
719ea8026Sopenharmony_ci#
819ea8026Sopenharmony_ci# Copyright (c) 2022, The littlefs authors.
919ea8026Sopenharmony_ci# SPDX-License-Identifier: BSD-3-Clause
1019ea8026Sopenharmony_ci#
1119ea8026Sopenharmony_ci
1219ea8026Sopenharmony_ciimport collections as co
1319ea8026Sopenharmony_ciimport csv
1419ea8026Sopenharmony_ciimport errno
1519ea8026Sopenharmony_ciimport glob
1619ea8026Sopenharmony_ciimport itertools as it
1719ea8026Sopenharmony_ciimport math as m
1819ea8026Sopenharmony_ciimport os
1919ea8026Sopenharmony_ciimport pty
2019ea8026Sopenharmony_ciimport re
2119ea8026Sopenharmony_ciimport shlex
2219ea8026Sopenharmony_ciimport shutil
2319ea8026Sopenharmony_ciimport signal
2419ea8026Sopenharmony_ciimport subprocess as sp
2519ea8026Sopenharmony_ciimport threading as th
2619ea8026Sopenharmony_ciimport time
2719ea8026Sopenharmony_ciimport toml
2819ea8026Sopenharmony_ci
2919ea8026Sopenharmony_ci
3019ea8026Sopenharmony_ciRUNNER_PATH = './runners/bench_runner'
3119ea8026Sopenharmony_ciHEADER_PATH = 'runners/bench_runner.h'
3219ea8026Sopenharmony_ci
3319ea8026Sopenharmony_ciGDB_PATH = ['gdb']
3419ea8026Sopenharmony_ciVALGRIND_PATH = ['valgrind']
3519ea8026Sopenharmony_ciPERF_SCRIPT = ['./scripts/perf.py']
3619ea8026Sopenharmony_ci
3719ea8026Sopenharmony_ci
3819ea8026Sopenharmony_cidef openio(path, mode='r', buffering=-1):
3919ea8026Sopenharmony_ci    # allow '-' for stdin/stdout
4019ea8026Sopenharmony_ci    if path == '-':
4119ea8026Sopenharmony_ci        if mode == 'r':
4219ea8026Sopenharmony_ci            return os.fdopen(os.dup(sys.stdin.fileno()), mode, buffering)
4319ea8026Sopenharmony_ci        else:
4419ea8026Sopenharmony_ci            return os.fdopen(os.dup(sys.stdout.fileno()), mode, buffering)
4519ea8026Sopenharmony_ci    else:
4619ea8026Sopenharmony_ci        return open(path, mode, buffering)
4719ea8026Sopenharmony_ci
4819ea8026Sopenharmony_ciclass BenchCase:
4919ea8026Sopenharmony_ci    # create a BenchCase object from a config
5019ea8026Sopenharmony_ci    def __init__(self, config, args={}):
5119ea8026Sopenharmony_ci        self.name = config.pop('name')
5219ea8026Sopenharmony_ci        self.path = config.pop('path')
5319ea8026Sopenharmony_ci        self.suite = config.pop('suite')
5419ea8026Sopenharmony_ci        self.lineno = config.pop('lineno', None)
5519ea8026Sopenharmony_ci        self.if_ = config.pop('if', None)
5619ea8026Sopenharmony_ci        if isinstance(self.if_, bool):
5719ea8026Sopenharmony_ci            self.if_ = 'true' if self.if_ else 'false'
5819ea8026Sopenharmony_ci        self.code = config.pop('code')
5919ea8026Sopenharmony_ci        self.code_lineno = config.pop('code_lineno', None)
6019ea8026Sopenharmony_ci        self.in_ = config.pop('in',
6119ea8026Sopenharmony_ci            config.pop('suite_in', None))
6219ea8026Sopenharmony_ci
6319ea8026Sopenharmony_ci        # figure out defines and build possible permutations
6419ea8026Sopenharmony_ci        self.defines = set()
6519ea8026Sopenharmony_ci        self.permutations = []
6619ea8026Sopenharmony_ci
6719ea8026Sopenharmony_ci        # defines can be a dict or a list or dicts
6819ea8026Sopenharmony_ci        suite_defines = config.pop('suite_defines', {})
6919ea8026Sopenharmony_ci        if not isinstance(suite_defines, list):
7019ea8026Sopenharmony_ci            suite_defines = [suite_defines]
7119ea8026Sopenharmony_ci        defines = config.pop('defines', {})
7219ea8026Sopenharmony_ci        if not isinstance(defines, list):
7319ea8026Sopenharmony_ci            defines = [defines]
7419ea8026Sopenharmony_ci
7519ea8026Sopenharmony_ci        def csplit(v):
7619ea8026Sopenharmony_ci            # split commas but only outside of parens
7719ea8026Sopenharmony_ci            parens = 0
7819ea8026Sopenharmony_ci            i_ = 0
7919ea8026Sopenharmony_ci            for i in range(len(v)):
8019ea8026Sopenharmony_ci                if v[i] == ',' and parens == 0:
8119ea8026Sopenharmony_ci                    yield v[i_:i]
8219ea8026Sopenharmony_ci                    i_ = i+1
8319ea8026Sopenharmony_ci                elif v[i] in '([{':
8419ea8026Sopenharmony_ci                    parens += 1
8519ea8026Sopenharmony_ci                elif v[i] in '}])':
8619ea8026Sopenharmony_ci                    parens -= 1
8719ea8026Sopenharmony_ci            if v[i_:].strip():
8819ea8026Sopenharmony_ci                yield v[i_:]
8919ea8026Sopenharmony_ci
9019ea8026Sopenharmony_ci        def parse_define(v):
9119ea8026Sopenharmony_ci            # a define entry can be a list
9219ea8026Sopenharmony_ci            if isinstance(v, list):
9319ea8026Sopenharmony_ci                for v_ in v:
9419ea8026Sopenharmony_ci                    yield from parse_define(v_)
9519ea8026Sopenharmony_ci            # or a string
9619ea8026Sopenharmony_ci            elif isinstance(v, str):
9719ea8026Sopenharmony_ci                # which can be comma-separated values, with optional
9819ea8026Sopenharmony_ci                # range statements. This matches the runtime define parser in
9919ea8026Sopenharmony_ci                # the runner itself.
10019ea8026Sopenharmony_ci                for v_ in csplit(v):
10119ea8026Sopenharmony_ci                    m = re.search(r'\brange\b\s*\('
10219ea8026Sopenharmony_ci                        '(?P<start>[^,\s]*)'
10319ea8026Sopenharmony_ci                        '\s*(?:,\s*(?P<stop>[^,\s]*)'
10419ea8026Sopenharmony_ci                        '\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
10519ea8026Sopenharmony_ci                        v_)
10619ea8026Sopenharmony_ci                    if m:
10719ea8026Sopenharmony_ci                        start = (int(m.group('start'), 0)
10819ea8026Sopenharmony_ci                            if m.group('start') else 0)
10919ea8026Sopenharmony_ci                        stop = (int(m.group('stop'), 0)
11019ea8026Sopenharmony_ci                            if m.group('stop') else None)
11119ea8026Sopenharmony_ci                        step = (int(m.group('step'), 0)
11219ea8026Sopenharmony_ci                            if m.group('step') else 1)
11319ea8026Sopenharmony_ci                        if m.lastindex <= 1:
11419ea8026Sopenharmony_ci                            start, stop = 0, start
11519ea8026Sopenharmony_ci                        for x in range(start, stop, step):
11619ea8026Sopenharmony_ci                            yield from parse_define('%s(%d)%s' % (
11719ea8026Sopenharmony_ci                                v_[:m.start()], x, v_[m.end():]))
11819ea8026Sopenharmony_ci                    else:
11919ea8026Sopenharmony_ci                        yield v_
12019ea8026Sopenharmony_ci            # or a literal value
12119ea8026Sopenharmony_ci            elif isinstance(v, bool):
12219ea8026Sopenharmony_ci                yield 'true' if v else 'false'
12319ea8026Sopenharmony_ci            else:
12419ea8026Sopenharmony_ci                yield v
12519ea8026Sopenharmony_ci
12619ea8026Sopenharmony_ci        # build possible permutations
12719ea8026Sopenharmony_ci        for suite_defines_ in suite_defines:
12819ea8026Sopenharmony_ci            self.defines |= suite_defines_.keys()
12919ea8026Sopenharmony_ci            for defines_ in defines:
13019ea8026Sopenharmony_ci                self.defines |= defines_.keys()
13119ea8026Sopenharmony_ci                self.permutations.extend(dict(perm) for perm in it.product(*(
13219ea8026Sopenharmony_ci                    [(k, v) for v in parse_define(vs)]
13319ea8026Sopenharmony_ci                    for k, vs in sorted((suite_defines_ | defines_).items()))))
13419ea8026Sopenharmony_ci
13519ea8026Sopenharmony_ci        for k in config.keys():
13619ea8026Sopenharmony_ci            print('%swarning:%s in %s, found unused key %r' % (
13719ea8026Sopenharmony_ci                '\x1b[01;33m' if args['color'] else '',
13819ea8026Sopenharmony_ci                '\x1b[m' if args['color'] else '',
13919ea8026Sopenharmony_ci                self.name,
14019ea8026Sopenharmony_ci                k),
14119ea8026Sopenharmony_ci                file=sys.stderr)
14219ea8026Sopenharmony_ci
14319ea8026Sopenharmony_ci
14419ea8026Sopenharmony_ciclass BenchSuite:
14519ea8026Sopenharmony_ci    # create a BenchSuite object from a toml file
14619ea8026Sopenharmony_ci    def __init__(self, path, args={}):
14719ea8026Sopenharmony_ci        self.path = path
14819ea8026Sopenharmony_ci        self.name = os.path.basename(path)
14919ea8026Sopenharmony_ci        if self.name.endswith('.toml'):
15019ea8026Sopenharmony_ci            self.name = self.name[:-len('.toml')]
15119ea8026Sopenharmony_ci
15219ea8026Sopenharmony_ci        # load toml file and parse bench cases
15319ea8026Sopenharmony_ci        with open(self.path) as f:
15419ea8026Sopenharmony_ci            # load benches
15519ea8026Sopenharmony_ci            config = toml.load(f)
15619ea8026Sopenharmony_ci
15719ea8026Sopenharmony_ci            # find line numbers
15819ea8026Sopenharmony_ci            f.seek(0)
15919ea8026Sopenharmony_ci            case_linenos = []
16019ea8026Sopenharmony_ci            code_linenos = []
16119ea8026Sopenharmony_ci            for i, line in enumerate(f):
16219ea8026Sopenharmony_ci                match = re.match(
16319ea8026Sopenharmony_ci                    '(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])'
16419ea8026Sopenharmony_ci                        '|' '(?P<code>code\s*=)',
16519ea8026Sopenharmony_ci                    line)
16619ea8026Sopenharmony_ci                if match and match.group('case'):
16719ea8026Sopenharmony_ci                    case_linenos.append((i+1, match.group('name')))
16819ea8026Sopenharmony_ci                elif match and match.group('code'):
16919ea8026Sopenharmony_ci                    code_linenos.append(i+2)
17019ea8026Sopenharmony_ci
17119ea8026Sopenharmony_ci            # sort in case toml parsing did not retain order
17219ea8026Sopenharmony_ci            case_linenos.sort()
17319ea8026Sopenharmony_ci
17419ea8026Sopenharmony_ci            cases = config.pop('cases')
17519ea8026Sopenharmony_ci            for (lineno, name), (nlineno, _) in it.zip_longest(
17619ea8026Sopenharmony_ci                    case_linenos, case_linenos[1:],
17719ea8026Sopenharmony_ci                    fillvalue=(float('inf'), None)):
17819ea8026Sopenharmony_ci                code_lineno = min(
17919ea8026Sopenharmony_ci                    (l for l in code_linenos if l >= lineno and l < nlineno),
18019ea8026Sopenharmony_ci                    default=None)
18119ea8026Sopenharmony_ci                cases[name]['lineno'] = lineno
18219ea8026Sopenharmony_ci                cases[name]['code_lineno'] = code_lineno
18319ea8026Sopenharmony_ci
18419ea8026Sopenharmony_ci            self.if_ = config.pop('if', None)
18519ea8026Sopenharmony_ci            if isinstance(self.if_, bool):
18619ea8026Sopenharmony_ci                self.if_ = 'true' if self.if_ else 'false'
18719ea8026Sopenharmony_ci
18819ea8026Sopenharmony_ci            self.code = config.pop('code', None)
18919ea8026Sopenharmony_ci            self.code_lineno = min(
19019ea8026Sopenharmony_ci                (l for l in code_linenos
19119ea8026Sopenharmony_ci                    if not case_linenos or l < case_linenos[0][0]),
19219ea8026Sopenharmony_ci                default=None)
19319ea8026Sopenharmony_ci
19419ea8026Sopenharmony_ci            # a couple of these we just forward to all cases
19519ea8026Sopenharmony_ci            defines = config.pop('defines', {})
19619ea8026Sopenharmony_ci            in_ = config.pop('in', None)
19719ea8026Sopenharmony_ci
19819ea8026Sopenharmony_ci            self.cases = []
19919ea8026Sopenharmony_ci            for name, case in sorted(cases.items(),
20019ea8026Sopenharmony_ci                    key=lambda c: c[1].get('lineno')):
20119ea8026Sopenharmony_ci                self.cases.append(BenchCase(config={
20219ea8026Sopenharmony_ci                    'name': name,
20319ea8026Sopenharmony_ci                    'path': path + (':%d' % case['lineno']
20419ea8026Sopenharmony_ci                        if 'lineno' in case else ''),
20519ea8026Sopenharmony_ci                    'suite': self.name,
20619ea8026Sopenharmony_ci                    'suite_defines': defines,
20719ea8026Sopenharmony_ci                    'suite_in': in_,
20819ea8026Sopenharmony_ci                    **case},
20919ea8026Sopenharmony_ci                    args=args))
21019ea8026Sopenharmony_ci
21119ea8026Sopenharmony_ci            # combine per-case defines
21219ea8026Sopenharmony_ci            self.defines = set.union(*(
21319ea8026Sopenharmony_ci                set(case.defines) for case in self.cases))
21419ea8026Sopenharmony_ci
21519ea8026Sopenharmony_ci        for k in config.keys():
21619ea8026Sopenharmony_ci            print('%swarning:%s in %s, found unused key %r' % (
21719ea8026Sopenharmony_ci                '\x1b[01;33m' if args['color'] else '',
21819ea8026Sopenharmony_ci                '\x1b[m' if args['color'] else '',
21919ea8026Sopenharmony_ci                self.name,
22019ea8026Sopenharmony_ci                k),
22119ea8026Sopenharmony_ci                file=sys.stderr)
22219ea8026Sopenharmony_ci
22319ea8026Sopenharmony_ci
22419ea8026Sopenharmony_ci
22519ea8026Sopenharmony_cidef compile(bench_paths, **args):
22619ea8026Sopenharmony_ci    # find .toml files
22719ea8026Sopenharmony_ci    paths = []
22819ea8026Sopenharmony_ci    for path in bench_paths:
22919ea8026Sopenharmony_ci        if os.path.isdir(path):
23019ea8026Sopenharmony_ci            path = path + '/*.toml'
23119ea8026Sopenharmony_ci
23219ea8026Sopenharmony_ci        for path in glob.glob(path):
23319ea8026Sopenharmony_ci            paths.append(path)
23419ea8026Sopenharmony_ci
23519ea8026Sopenharmony_ci    if not paths:
23619ea8026Sopenharmony_ci        print('no bench suites found in %r?' % bench_paths)
23719ea8026Sopenharmony_ci        sys.exit(-1)
23819ea8026Sopenharmony_ci
23919ea8026Sopenharmony_ci    # load the suites
24019ea8026Sopenharmony_ci    suites = [BenchSuite(path, args) for path in paths]
24119ea8026Sopenharmony_ci    suites.sort(key=lambda s: s.name)
24219ea8026Sopenharmony_ci
24319ea8026Sopenharmony_ci    # check for name conflicts, these will cause ambiguity problems later
24419ea8026Sopenharmony_ci    # when running benches
24519ea8026Sopenharmony_ci    seen = {}
24619ea8026Sopenharmony_ci    for suite in suites:
24719ea8026Sopenharmony_ci        if suite.name in seen:
24819ea8026Sopenharmony_ci            print('%swarning:%s conflicting suite %r, %s and %s' % (
24919ea8026Sopenharmony_ci                '\x1b[01;33m' if args['color'] else '',
25019ea8026Sopenharmony_ci                '\x1b[m' if args['color'] else '',
25119ea8026Sopenharmony_ci                suite.name,
25219ea8026Sopenharmony_ci                suite.path,
25319ea8026Sopenharmony_ci                seen[suite.name].path),
25419ea8026Sopenharmony_ci                file=sys.stderr)
25519ea8026Sopenharmony_ci        seen[suite.name] = suite
25619ea8026Sopenharmony_ci
25719ea8026Sopenharmony_ci        for case in suite.cases:
25819ea8026Sopenharmony_ci            # only allow conflicts if a case and its suite share a name
25919ea8026Sopenharmony_ci            if case.name in seen and not (
26019ea8026Sopenharmony_ci                    isinstance(seen[case.name], BenchSuite)
26119ea8026Sopenharmony_ci                    and seen[case.name].cases == [case]):
26219ea8026Sopenharmony_ci                print('%swarning:%s conflicting case %r, %s and %s' % (
26319ea8026Sopenharmony_ci                    '\x1b[01;33m' if args['color'] else '',
26419ea8026Sopenharmony_ci                    '\x1b[m' if args['color'] else '',
26519ea8026Sopenharmony_ci                    case.name,
26619ea8026Sopenharmony_ci                    case.path,
26719ea8026Sopenharmony_ci                    seen[case.name].path),
26819ea8026Sopenharmony_ci                    file=sys.stderr)
26919ea8026Sopenharmony_ci            seen[case.name] = case
27019ea8026Sopenharmony_ci
27119ea8026Sopenharmony_ci    # we can only compile one bench suite at a time
27219ea8026Sopenharmony_ci    if not args.get('source'):
27319ea8026Sopenharmony_ci        if len(suites) > 1:
27419ea8026Sopenharmony_ci            print('more than one bench suite for compilation? (%r)' % bench_paths)
27519ea8026Sopenharmony_ci            sys.exit(-1)
27619ea8026Sopenharmony_ci
27719ea8026Sopenharmony_ci        suite = suites[0]
27819ea8026Sopenharmony_ci
27919ea8026Sopenharmony_ci    # write generated bench source
28019ea8026Sopenharmony_ci    if 'output' in args:
28119ea8026Sopenharmony_ci        with openio(args['output'], 'w') as f:
28219ea8026Sopenharmony_ci            _write = f.write
28319ea8026Sopenharmony_ci            def write(s):
28419ea8026Sopenharmony_ci                f.lineno += s.count('\n')
28519ea8026Sopenharmony_ci                _write(s)
28619ea8026Sopenharmony_ci            def writeln(s=''):
28719ea8026Sopenharmony_ci                f.lineno += s.count('\n') + 1
28819ea8026Sopenharmony_ci                _write(s)
28919ea8026Sopenharmony_ci                _write('\n')
29019ea8026Sopenharmony_ci            f.lineno = 1
29119ea8026Sopenharmony_ci            f.write = write
29219ea8026Sopenharmony_ci            f.writeln = writeln
29319ea8026Sopenharmony_ci
29419ea8026Sopenharmony_ci            f.writeln("// Generated by %s:" % sys.argv[0])
29519ea8026Sopenharmony_ci            f.writeln("//")
29619ea8026Sopenharmony_ci            f.writeln("// %s" % ' '.join(sys.argv))
29719ea8026Sopenharmony_ci            f.writeln("//")
29819ea8026Sopenharmony_ci            f.writeln()
29919ea8026Sopenharmony_ci
30019ea8026Sopenharmony_ci            # include bench_runner.h in every generated file
30119ea8026Sopenharmony_ci            f.writeln("#include \"%s\"" % args['include'])
30219ea8026Sopenharmony_ci            f.writeln()
30319ea8026Sopenharmony_ci
30419ea8026Sopenharmony_ci            # write out generated functions, this can end up in different
30519ea8026Sopenharmony_ci            # files depending on the "in" attribute
30619ea8026Sopenharmony_ci            #
30719ea8026Sopenharmony_ci            # note it's up to the specific generated file to declare
30819ea8026Sopenharmony_ci            # the bench defines
30919ea8026Sopenharmony_ci            def write_case_functions(f, suite, case):
31019ea8026Sopenharmony_ci                    # create case define functions
31119ea8026Sopenharmony_ci                    if case.defines:
31219ea8026Sopenharmony_ci                        # deduplicate defines by value to try to reduce the
31319ea8026Sopenharmony_ci                        # number of functions we generate
31419ea8026Sopenharmony_ci                        define_cbs = {}
31519ea8026Sopenharmony_ci                        for i, defines in enumerate(case.permutations):
31619ea8026Sopenharmony_ci                            for k, v in sorted(defines.items()):
31719ea8026Sopenharmony_ci                                if v not in define_cbs:
31819ea8026Sopenharmony_ci                                    name = ('__bench__%s__%s__%d'
31919ea8026Sopenharmony_ci                                        % (case.name, k, i))
32019ea8026Sopenharmony_ci                                    define_cbs[v] = name
32119ea8026Sopenharmony_ci                                    f.writeln('intmax_t %s('
32219ea8026Sopenharmony_ci                                        '__attribute__((unused)) '
32319ea8026Sopenharmony_ci                                        'void *data) {' % name)
32419ea8026Sopenharmony_ci                                    f.writeln(4*' '+'return %s;' % v)
32519ea8026Sopenharmony_ci                                    f.writeln('}')
32619ea8026Sopenharmony_ci                                    f.writeln()
32719ea8026Sopenharmony_ci                        f.writeln('const bench_define_t '
32819ea8026Sopenharmony_ci                            '__bench__%s__defines[]['
32919ea8026Sopenharmony_ci                            'BENCH_IMPLICIT_DEFINE_COUNT+%d] = {'
33019ea8026Sopenharmony_ci                            % (case.name, len(suite.defines)))
33119ea8026Sopenharmony_ci                        for defines in case.permutations:
33219ea8026Sopenharmony_ci                            f.writeln(4*' '+'{')
33319ea8026Sopenharmony_ci                            for k, v in sorted(defines.items()):
33419ea8026Sopenharmony_ci                                f.writeln(8*' '+'[%-24s] = {%s, NULL},' % (
33519ea8026Sopenharmony_ci                                    k+'_i', define_cbs[v]))
33619ea8026Sopenharmony_ci                            f.writeln(4*' '+'},')
33719ea8026Sopenharmony_ci                        f.writeln('};')
33819ea8026Sopenharmony_ci                        f.writeln()
33919ea8026Sopenharmony_ci
34019ea8026Sopenharmony_ci                    # create case filter function
34119ea8026Sopenharmony_ci                    if suite.if_ is not None or case.if_ is not None:
34219ea8026Sopenharmony_ci                        f.writeln('bool __bench__%s__filter(void) {'
34319ea8026Sopenharmony_ci                            % (case.name))
34419ea8026Sopenharmony_ci                        f.writeln(4*' '+'return %s;'
34519ea8026Sopenharmony_ci                            % ' && '.join('(%s)' % if_
34619ea8026Sopenharmony_ci                                for if_ in [suite.if_, case.if_]
34719ea8026Sopenharmony_ci                                if if_ is not None))
34819ea8026Sopenharmony_ci                        f.writeln('}')
34919ea8026Sopenharmony_ci                        f.writeln()
35019ea8026Sopenharmony_ci
35119ea8026Sopenharmony_ci                    # create case run function
35219ea8026Sopenharmony_ci                    f.writeln('void __bench__%s__run('
35319ea8026Sopenharmony_ci                        '__attribute__((unused)) struct lfs_config *cfg) {'
35419ea8026Sopenharmony_ci                        % (case.name))
35519ea8026Sopenharmony_ci                    f.writeln(4*' '+'// bench case %s' % case.name)
35619ea8026Sopenharmony_ci                    if case.code_lineno is not None:
35719ea8026Sopenharmony_ci                        f.writeln(4*' '+'#line %d "%s"'
35819ea8026Sopenharmony_ci                            % (case.code_lineno, suite.path))
35919ea8026Sopenharmony_ci                    f.write(case.code)
36019ea8026Sopenharmony_ci                    if case.code_lineno is not None:
36119ea8026Sopenharmony_ci                        f.writeln(4*' '+'#line %d "%s"'
36219ea8026Sopenharmony_ci                            % (f.lineno+1, args['output']))
36319ea8026Sopenharmony_ci                    f.writeln('}')
36419ea8026Sopenharmony_ci                    f.writeln()
36519ea8026Sopenharmony_ci
36619ea8026Sopenharmony_ci            if not args.get('source'):
36719ea8026Sopenharmony_ci                if suite.code is not None:
36819ea8026Sopenharmony_ci                    if suite.code_lineno is not None:
36919ea8026Sopenharmony_ci                        f.writeln('#line %d "%s"'
37019ea8026Sopenharmony_ci                            % (suite.code_lineno, suite.path))
37119ea8026Sopenharmony_ci                    f.write(suite.code)
37219ea8026Sopenharmony_ci                    if suite.code_lineno is not None:
37319ea8026Sopenharmony_ci                        f.writeln('#line %d "%s"'
37419ea8026Sopenharmony_ci                            % (f.lineno+1, args['output']))
37519ea8026Sopenharmony_ci                    f.writeln()
37619ea8026Sopenharmony_ci
37719ea8026Sopenharmony_ci                if suite.defines:
37819ea8026Sopenharmony_ci                    for i, define in enumerate(sorted(suite.defines)):
37919ea8026Sopenharmony_ci                        f.writeln('#ifndef %s' % define)
38019ea8026Sopenharmony_ci                        f.writeln('#define %-24s '
38119ea8026Sopenharmony_ci                            'BENCH_IMPLICIT_DEFINE_COUNT+%d' % (define+'_i', i))
38219ea8026Sopenharmony_ci                        f.writeln('#define %-24s '
38319ea8026Sopenharmony_ci                            'BENCH_DEFINE(%s)' % (define, define+'_i'))
38419ea8026Sopenharmony_ci                        f.writeln('#endif')
38519ea8026Sopenharmony_ci                    f.writeln()
38619ea8026Sopenharmony_ci
38719ea8026Sopenharmony_ci                # create case functions
38819ea8026Sopenharmony_ci                for case in suite.cases:
38919ea8026Sopenharmony_ci                    if case.in_ is None:
39019ea8026Sopenharmony_ci                        write_case_functions(f, suite, case)
39119ea8026Sopenharmony_ci                    else:
39219ea8026Sopenharmony_ci                        if case.defines:
39319ea8026Sopenharmony_ci                            f.writeln('extern const bench_define_t '
39419ea8026Sopenharmony_ci                                '__bench__%s__defines[]['
39519ea8026Sopenharmony_ci                                'BENCH_IMPLICIT_DEFINE_COUNT+%d];'
39619ea8026Sopenharmony_ci                                % (case.name, len(suite.defines)))
39719ea8026Sopenharmony_ci                        if suite.if_ is not None or case.if_ is not None:
39819ea8026Sopenharmony_ci                            f.writeln('extern bool __bench__%s__filter('
39919ea8026Sopenharmony_ci                                'void);'
40019ea8026Sopenharmony_ci                                % (case.name))
40119ea8026Sopenharmony_ci                        f.writeln('extern void __bench__%s__run('
40219ea8026Sopenharmony_ci                            'struct lfs_config *cfg);'
40319ea8026Sopenharmony_ci                            % (case.name))
40419ea8026Sopenharmony_ci                        f.writeln()
40519ea8026Sopenharmony_ci
40619ea8026Sopenharmony_ci                # create suite struct
40719ea8026Sopenharmony_ci                #
40819ea8026Sopenharmony_ci                # note we place this in the custom bench_suites section with
40919ea8026Sopenharmony_ci                # minimum alignment, otherwise GCC ups the alignment to
41019ea8026Sopenharmony_ci                # 32-bytes for some reason
41119ea8026Sopenharmony_ci                f.writeln('__attribute__((section("_bench_suites"), '
41219ea8026Sopenharmony_ci                    'aligned(1)))')
41319ea8026Sopenharmony_ci                f.writeln('const struct bench_suite __bench__%s__suite = {'
41419ea8026Sopenharmony_ci                    % suite.name)
41519ea8026Sopenharmony_ci                f.writeln(4*' '+'.name = "%s",' % suite.name)
41619ea8026Sopenharmony_ci                f.writeln(4*' '+'.path = "%s",' % suite.path)
41719ea8026Sopenharmony_ci                f.writeln(4*' '+'.flags = 0,')
41819ea8026Sopenharmony_ci                if suite.defines:
41919ea8026Sopenharmony_ci                    # create suite define names
42019ea8026Sopenharmony_ci                    f.writeln(4*' '+'.define_names = (const char *const['
42119ea8026Sopenharmony_ci                        'BENCH_IMPLICIT_DEFINE_COUNT+%d]){' % (
42219ea8026Sopenharmony_ci                        len(suite.defines)))
42319ea8026Sopenharmony_ci                    for k in sorted(suite.defines):
42419ea8026Sopenharmony_ci                        f.writeln(8*' '+'[%-24s] = "%s",' % (k+'_i', k))
42519ea8026Sopenharmony_ci                    f.writeln(4*' '+'},')
42619ea8026Sopenharmony_ci                    f.writeln(4*' '+'.define_count = '
42719ea8026Sopenharmony_ci                        'BENCH_IMPLICIT_DEFINE_COUNT+%d,' % len(suite.defines))
42819ea8026Sopenharmony_ci                f.writeln(4*' '+'.cases = (const struct bench_case[]){')
42919ea8026Sopenharmony_ci                for case in suite.cases:
43019ea8026Sopenharmony_ci                    # create case structs
43119ea8026Sopenharmony_ci                    f.writeln(8*' '+'{')
43219ea8026Sopenharmony_ci                    f.writeln(12*' '+'.name = "%s",' % case.name)
43319ea8026Sopenharmony_ci                    f.writeln(12*' '+'.path = "%s",' % case.path)
43419ea8026Sopenharmony_ci                    f.writeln(12*' '+'.flags = 0,')
43519ea8026Sopenharmony_ci                    f.writeln(12*' '+'.permutations = %d,'
43619ea8026Sopenharmony_ci                        % len(case.permutations))
43719ea8026Sopenharmony_ci                    if case.defines:
43819ea8026Sopenharmony_ci                        f.writeln(12*' '+'.defines '
43919ea8026Sopenharmony_ci                            '= (const bench_define_t*)__bench__%s__defines,'
44019ea8026Sopenharmony_ci                            % (case.name))
44119ea8026Sopenharmony_ci                    if suite.if_ is not None or case.if_ is not None:
44219ea8026Sopenharmony_ci                        f.writeln(12*' '+'.filter = __bench__%s__filter,'
44319ea8026Sopenharmony_ci                            % (case.name))
44419ea8026Sopenharmony_ci                    f.writeln(12*' '+'.run = __bench__%s__run,'
44519ea8026Sopenharmony_ci                        % (case.name))
44619ea8026Sopenharmony_ci                    f.writeln(8*' '+'},')
44719ea8026Sopenharmony_ci                f.writeln(4*' '+'},')
44819ea8026Sopenharmony_ci                f.writeln(4*' '+'.case_count = %d,' % len(suite.cases))
44919ea8026Sopenharmony_ci                f.writeln('};')
45019ea8026Sopenharmony_ci                f.writeln()
45119ea8026Sopenharmony_ci
45219ea8026Sopenharmony_ci            else:
45319ea8026Sopenharmony_ci                # copy source
45419ea8026Sopenharmony_ci                f.writeln('#line 1 "%s"' % args['source'])
45519ea8026Sopenharmony_ci                with open(args['source']) as sf:
45619ea8026Sopenharmony_ci                    shutil.copyfileobj(sf, f)
45719ea8026Sopenharmony_ci                f.writeln()
45819ea8026Sopenharmony_ci
45919ea8026Sopenharmony_ci                # write any internal benches
46019ea8026Sopenharmony_ci                for suite in suites:
46119ea8026Sopenharmony_ci                    for case in suite.cases:
46219ea8026Sopenharmony_ci                        if (case.in_ is not None
46319ea8026Sopenharmony_ci                                and os.path.normpath(case.in_)
46419ea8026Sopenharmony_ci                                    == os.path.normpath(args['source'])):
46519ea8026Sopenharmony_ci                            # write defines, but note we need to undef any
46619ea8026Sopenharmony_ci                            # new defines since we're in someone else's file
46719ea8026Sopenharmony_ci                            if suite.defines:
46819ea8026Sopenharmony_ci                                for i, define in enumerate(
46919ea8026Sopenharmony_ci                                        sorted(suite.defines)):
47019ea8026Sopenharmony_ci                                    f.writeln('#ifndef %s' % define)
47119ea8026Sopenharmony_ci                                    f.writeln('#define %-24s '
47219ea8026Sopenharmony_ci                                        'BENCH_IMPLICIT_DEFINE_COUNT+%d' % (
47319ea8026Sopenharmony_ci                                        define+'_i', i))
47419ea8026Sopenharmony_ci                                    f.writeln('#define %-24s '
47519ea8026Sopenharmony_ci                                        'BENCH_DEFINE(%s)' % (
47619ea8026Sopenharmony_ci                                        define, define+'_i'))
47719ea8026Sopenharmony_ci                                    f.writeln('#define '
47819ea8026Sopenharmony_ci                                        '__BENCH__%s__NEEDS_UNDEF' % (
47919ea8026Sopenharmony_ci                                        define))
48019ea8026Sopenharmony_ci                                    f.writeln('#endif')
48119ea8026Sopenharmony_ci                                f.writeln()
48219ea8026Sopenharmony_ci
48319ea8026Sopenharmony_ci                            write_case_functions(f, suite, case)
48419ea8026Sopenharmony_ci
48519ea8026Sopenharmony_ci                            if suite.defines:
48619ea8026Sopenharmony_ci                                for define in sorted(suite.defines):
48719ea8026Sopenharmony_ci                                    f.writeln('#ifdef __BENCH__%s__NEEDS_UNDEF'
48819ea8026Sopenharmony_ci                                        % define)
48919ea8026Sopenharmony_ci                                    f.writeln('#undef __BENCH__%s__NEEDS_UNDEF'
49019ea8026Sopenharmony_ci                                        % define)
49119ea8026Sopenharmony_ci                                    f.writeln('#undef %s' % define)
49219ea8026Sopenharmony_ci                                    f.writeln('#undef %s' % (define+'_i'))
49319ea8026Sopenharmony_ci                                    f.writeln('#endif')
49419ea8026Sopenharmony_ci                                f.writeln()
49519ea8026Sopenharmony_ci
49619ea8026Sopenharmony_cidef find_runner(runner, **args):
49719ea8026Sopenharmony_ci    cmd = runner.copy()
49819ea8026Sopenharmony_ci
49919ea8026Sopenharmony_ci    # run under some external command?
50019ea8026Sopenharmony_ci    if args.get('exec'):
50119ea8026Sopenharmony_ci        cmd[:0] = args['exec']
50219ea8026Sopenharmony_ci
50319ea8026Sopenharmony_ci    # run under valgrind?
50419ea8026Sopenharmony_ci    if args.get('valgrind'):
50519ea8026Sopenharmony_ci        cmd[:0] = args['valgrind_path'] + [
50619ea8026Sopenharmony_ci            '--leak-check=full',
50719ea8026Sopenharmony_ci            '--track-origins=yes',
50819ea8026Sopenharmony_ci            '--error-exitcode=4',
50919ea8026Sopenharmony_ci            '-q']
51019ea8026Sopenharmony_ci
51119ea8026Sopenharmony_ci    # run under perf?
51219ea8026Sopenharmony_ci    if args.get('perf'):
51319ea8026Sopenharmony_ci        cmd[:0] = args['perf_script'] + list(filter(None, [
51419ea8026Sopenharmony_ci            '-R',
51519ea8026Sopenharmony_ci            '--perf-freq=%s' % args['perf_freq']
51619ea8026Sopenharmony_ci                if args.get('perf_freq') else None,
51719ea8026Sopenharmony_ci            '--perf-period=%s' % args['perf_period']
51819ea8026Sopenharmony_ci                if args.get('perf_period') else None,
51919ea8026Sopenharmony_ci            '--perf-events=%s' % args['perf_events']
52019ea8026Sopenharmony_ci                if args.get('perf_events') else None,
52119ea8026Sopenharmony_ci            '--perf-path=%s' % args['perf_path']
52219ea8026Sopenharmony_ci                if args.get('perf_path') else None,
52319ea8026Sopenharmony_ci            '-o%s' % args['perf']]))
52419ea8026Sopenharmony_ci
52519ea8026Sopenharmony_ci    # other context
52619ea8026Sopenharmony_ci    if args.get('geometry'):
52719ea8026Sopenharmony_ci        cmd.append('-G%s' % args['geometry'])
52819ea8026Sopenharmony_ci    if args.get('disk'):
52919ea8026Sopenharmony_ci        cmd.append('-d%s' % args['disk'])
53019ea8026Sopenharmony_ci    if args.get('trace'):
53119ea8026Sopenharmony_ci        cmd.append('-t%s' % args['trace'])
53219ea8026Sopenharmony_ci    if args.get('trace_backtrace'):
53319ea8026Sopenharmony_ci        cmd.append('--trace-backtrace')
53419ea8026Sopenharmony_ci    if args.get('trace_period'):
53519ea8026Sopenharmony_ci        cmd.append('--trace-period=%s' % args['trace_period'])
53619ea8026Sopenharmony_ci    if args.get('trace_freq'):
53719ea8026Sopenharmony_ci        cmd.append('--trace-freq=%s' % args['trace_freq'])
53819ea8026Sopenharmony_ci    if args.get('read_sleep'):
53919ea8026Sopenharmony_ci        cmd.append('--read-sleep=%s' % args['read_sleep'])
54019ea8026Sopenharmony_ci    if args.get('prog_sleep'):
54119ea8026Sopenharmony_ci        cmd.append('--prog-sleep=%s' % args['prog_sleep'])
54219ea8026Sopenharmony_ci    if args.get('erase_sleep'):
54319ea8026Sopenharmony_ci        cmd.append('--erase-sleep=%s' % args['erase_sleep'])
54419ea8026Sopenharmony_ci
54519ea8026Sopenharmony_ci    # defines?
54619ea8026Sopenharmony_ci    if args.get('define'):
54719ea8026Sopenharmony_ci        for define in args.get('define'):
54819ea8026Sopenharmony_ci            cmd.append('-D%s' % define)
54919ea8026Sopenharmony_ci
55019ea8026Sopenharmony_ci    return cmd
55119ea8026Sopenharmony_ci
55219ea8026Sopenharmony_cidef list_(runner, bench_ids=[], **args):
55319ea8026Sopenharmony_ci    cmd = find_runner(runner, **args) + bench_ids
55419ea8026Sopenharmony_ci    if args.get('summary'):          cmd.append('--summary')
55519ea8026Sopenharmony_ci    if args.get('list_suites'):      cmd.append('--list-suites')
55619ea8026Sopenharmony_ci    if args.get('list_cases'):       cmd.append('--list-cases')
55719ea8026Sopenharmony_ci    if args.get('list_suite_paths'): cmd.append('--list-suite-paths')
55819ea8026Sopenharmony_ci    if args.get('list_case_paths'):  cmd.append('--list-case-paths')
55919ea8026Sopenharmony_ci    if args.get('list_defines'):     cmd.append('--list-defines')
56019ea8026Sopenharmony_ci    if args.get('list_permutation_defines'):
56119ea8026Sopenharmony_ci                                     cmd.append('--list-permutation-defines')
56219ea8026Sopenharmony_ci    if args.get('list_implicit_defines'):
56319ea8026Sopenharmony_ci                                     cmd.append('--list-implicit-defines')
56419ea8026Sopenharmony_ci    if args.get('list_geometries'):  cmd.append('--list-geometries')
56519ea8026Sopenharmony_ci
56619ea8026Sopenharmony_ci    if args.get('verbose'):
56719ea8026Sopenharmony_ci        print(' '.join(shlex.quote(c) for c in cmd))
56819ea8026Sopenharmony_ci    return sp.call(cmd)
56919ea8026Sopenharmony_ci
57019ea8026Sopenharmony_ci
57119ea8026Sopenharmony_cidef find_perms(runner_, ids=[], **args):
57219ea8026Sopenharmony_ci    case_suites = {}
57319ea8026Sopenharmony_ci    expected_case_perms = co.defaultdict(lambda: 0)
57419ea8026Sopenharmony_ci    expected_perms = 0
57519ea8026Sopenharmony_ci    total_perms = 0
57619ea8026Sopenharmony_ci
57719ea8026Sopenharmony_ci    # query cases from the runner
57819ea8026Sopenharmony_ci    cmd = runner_ + ['--list-cases'] + ids
57919ea8026Sopenharmony_ci    if args.get('verbose'):
58019ea8026Sopenharmony_ci        print(' '.join(shlex.quote(c) for c in cmd))
58119ea8026Sopenharmony_ci    proc = sp.Popen(cmd,
58219ea8026Sopenharmony_ci        stdout=sp.PIPE,
58319ea8026Sopenharmony_ci        stderr=sp.PIPE if not args.get('verbose') else None,
58419ea8026Sopenharmony_ci        universal_newlines=True,
58519ea8026Sopenharmony_ci        errors='replace',
58619ea8026Sopenharmony_ci        close_fds=False)
58719ea8026Sopenharmony_ci    pattern = re.compile(
58819ea8026Sopenharmony_ci        '^(?P<case>[^\s]+)'
58919ea8026Sopenharmony_ci            '\s+(?P<flags>[^\s]+)'
59019ea8026Sopenharmony_ci            '\s+(?P<filtered>\d+)/(?P<perms>\d+)')
59119ea8026Sopenharmony_ci    # skip the first line
59219ea8026Sopenharmony_ci    for line in it.islice(proc.stdout, 1, None):
59319ea8026Sopenharmony_ci        m = pattern.match(line)
59419ea8026Sopenharmony_ci        if m:
59519ea8026Sopenharmony_ci            filtered = int(m.group('filtered'))
59619ea8026Sopenharmony_ci            perms = int(m.group('perms'))
59719ea8026Sopenharmony_ci            expected_case_perms[m.group('case')] += filtered
59819ea8026Sopenharmony_ci            expected_perms += filtered
59919ea8026Sopenharmony_ci            total_perms += perms
60019ea8026Sopenharmony_ci    proc.wait()
60119ea8026Sopenharmony_ci    if proc.returncode != 0:
60219ea8026Sopenharmony_ci        if not args.get('verbose'):
60319ea8026Sopenharmony_ci            for line in proc.stderr:
60419ea8026Sopenharmony_ci                sys.stdout.write(line)
60519ea8026Sopenharmony_ci        sys.exit(-1)
60619ea8026Sopenharmony_ci
60719ea8026Sopenharmony_ci    # get which suite each case belongs to via paths
60819ea8026Sopenharmony_ci    cmd = runner_ + ['--list-case-paths'] + ids
60919ea8026Sopenharmony_ci    if args.get('verbose'):
61019ea8026Sopenharmony_ci        print(' '.join(shlex.quote(c) for c in cmd))
61119ea8026Sopenharmony_ci    proc = sp.Popen(cmd,
61219ea8026Sopenharmony_ci        stdout=sp.PIPE,
61319ea8026Sopenharmony_ci        stderr=sp.PIPE if not args.get('verbose') else None,
61419ea8026Sopenharmony_ci        universal_newlines=True,
61519ea8026Sopenharmony_ci        errors='replace',
61619ea8026Sopenharmony_ci        close_fds=False)
61719ea8026Sopenharmony_ci    pattern = re.compile(
61819ea8026Sopenharmony_ci        '^(?P<case>[^\s]+)'
61919ea8026Sopenharmony_ci            '\s+(?P<path>[^:]+):(?P<lineno>\d+)')
62019ea8026Sopenharmony_ci    # skip the first line
62119ea8026Sopenharmony_ci    for line in it.islice(proc.stdout, 1, None):
62219ea8026Sopenharmony_ci        m = pattern.match(line)
62319ea8026Sopenharmony_ci        if m:
62419ea8026Sopenharmony_ci            path = m.group('path')
62519ea8026Sopenharmony_ci            # strip path/suffix here
62619ea8026Sopenharmony_ci            suite = os.path.basename(path)
62719ea8026Sopenharmony_ci            if suite.endswith('.toml'):
62819ea8026Sopenharmony_ci                suite = suite[:-len('.toml')]
62919ea8026Sopenharmony_ci            case_suites[m.group('case')] = suite
63019ea8026Sopenharmony_ci    proc.wait()
63119ea8026Sopenharmony_ci    if proc.returncode != 0:
63219ea8026Sopenharmony_ci        if not args.get('verbose'):
63319ea8026Sopenharmony_ci            for line in proc.stderr:
63419ea8026Sopenharmony_ci                sys.stdout.write(line)
63519ea8026Sopenharmony_ci        sys.exit(-1)
63619ea8026Sopenharmony_ci
63719ea8026Sopenharmony_ci    # figure out expected suite perms
63819ea8026Sopenharmony_ci    expected_suite_perms = co.defaultdict(lambda: 0)
63919ea8026Sopenharmony_ci    for case, suite in case_suites.items():
64019ea8026Sopenharmony_ci        expected_suite_perms[suite] += expected_case_perms[case]
64119ea8026Sopenharmony_ci
64219ea8026Sopenharmony_ci    return (
64319ea8026Sopenharmony_ci        case_suites,
64419ea8026Sopenharmony_ci        expected_suite_perms,
64519ea8026Sopenharmony_ci        expected_case_perms,
64619ea8026Sopenharmony_ci        expected_perms,
64719ea8026Sopenharmony_ci        total_perms)
64819ea8026Sopenharmony_ci
64919ea8026Sopenharmony_cidef find_path(runner_, id, **args):
65019ea8026Sopenharmony_ci    path = None
65119ea8026Sopenharmony_ci    # query from runner
65219ea8026Sopenharmony_ci    cmd = runner_ + ['--list-case-paths', id]
65319ea8026Sopenharmony_ci    if args.get('verbose'):
65419ea8026Sopenharmony_ci        print(' '.join(shlex.quote(c) for c in cmd))
65519ea8026Sopenharmony_ci    proc = sp.Popen(cmd,
65619ea8026Sopenharmony_ci        stdout=sp.PIPE,
65719ea8026Sopenharmony_ci        stderr=sp.PIPE if not args.get('verbose') else None,
65819ea8026Sopenharmony_ci        universal_newlines=True,
65919ea8026Sopenharmony_ci        errors='replace',
66019ea8026Sopenharmony_ci        close_fds=False)
66119ea8026Sopenharmony_ci    pattern = re.compile(
66219ea8026Sopenharmony_ci        '^(?P<case>[^\s]+)'
66319ea8026Sopenharmony_ci            '\s+(?P<path>[^:]+):(?P<lineno>\d+)')
66419ea8026Sopenharmony_ci    # skip the first line
66519ea8026Sopenharmony_ci    for line in it.islice(proc.stdout, 1, None):
66619ea8026Sopenharmony_ci        m = pattern.match(line)
66719ea8026Sopenharmony_ci        if m and path is None:
66819ea8026Sopenharmony_ci            path_ = m.group('path')
66919ea8026Sopenharmony_ci            lineno = int(m.group('lineno'))
67019ea8026Sopenharmony_ci            path = (path_, lineno)
67119ea8026Sopenharmony_ci    proc.wait()
67219ea8026Sopenharmony_ci    if proc.returncode != 0:
67319ea8026Sopenharmony_ci        if not args.get('verbose'):
67419ea8026Sopenharmony_ci            for line in proc.stderr:
67519ea8026Sopenharmony_ci                sys.stdout.write(line)
67619ea8026Sopenharmony_ci        sys.exit(-1)
67719ea8026Sopenharmony_ci
67819ea8026Sopenharmony_ci    return path
67919ea8026Sopenharmony_ci
68019ea8026Sopenharmony_cidef find_defines(runner_, id, **args):
68119ea8026Sopenharmony_ci    # query permutation defines from runner
68219ea8026Sopenharmony_ci    cmd = runner_ + ['--list-permutation-defines', id]
68319ea8026Sopenharmony_ci    if args.get('verbose'):
68419ea8026Sopenharmony_ci        print(' '.join(shlex.quote(c) for c in cmd))
68519ea8026Sopenharmony_ci    proc = sp.Popen(cmd,
68619ea8026Sopenharmony_ci        stdout=sp.PIPE,
68719ea8026Sopenharmony_ci        stderr=sp.PIPE if not args.get('verbose') else None,
68819ea8026Sopenharmony_ci        universal_newlines=True,
68919ea8026Sopenharmony_ci        errors='replace',
69019ea8026Sopenharmony_ci        close_fds=False)
69119ea8026Sopenharmony_ci    defines = co.OrderedDict()
69219ea8026Sopenharmony_ci    pattern = re.compile('^(?P<define>\w+)=(?P<value>.+)')
69319ea8026Sopenharmony_ci    for line in proc.stdout:
69419ea8026Sopenharmony_ci        m = pattern.match(line)
69519ea8026Sopenharmony_ci        if m:
69619ea8026Sopenharmony_ci            define = m.group('define')
69719ea8026Sopenharmony_ci            value = m.group('value')
69819ea8026Sopenharmony_ci            defines[define] = value
69919ea8026Sopenharmony_ci    proc.wait()
70019ea8026Sopenharmony_ci    if proc.returncode != 0:
70119ea8026Sopenharmony_ci        if not args.get('verbose'):
70219ea8026Sopenharmony_ci            for line in proc.stderr:
70319ea8026Sopenharmony_ci                sys.stdout.write(line)
70419ea8026Sopenharmony_ci        sys.exit(-1)
70519ea8026Sopenharmony_ci
70619ea8026Sopenharmony_ci    return defines
70719ea8026Sopenharmony_ci
70819ea8026Sopenharmony_ci
70919ea8026Sopenharmony_ci# Thread-safe CSV writer
71019ea8026Sopenharmony_ciclass BenchOutput:
71119ea8026Sopenharmony_ci    def __init__(self, path, head=None, tail=None):
71219ea8026Sopenharmony_ci        self.f = openio(path, 'w+', 1)
71319ea8026Sopenharmony_ci        self.lock = th.Lock()
71419ea8026Sopenharmony_ci        self.head = head or []
71519ea8026Sopenharmony_ci        self.tail = tail or []
71619ea8026Sopenharmony_ci        self.writer = csv.DictWriter(self.f, self.head + self.tail)
71719ea8026Sopenharmony_ci        self.rows = []
71819ea8026Sopenharmony_ci
71919ea8026Sopenharmony_ci    def close(self):
72019ea8026Sopenharmony_ci        self.f.close()
72119ea8026Sopenharmony_ci
72219ea8026Sopenharmony_ci    def __enter__(self):
72319ea8026Sopenharmony_ci        return self
72419ea8026Sopenharmony_ci
72519ea8026Sopenharmony_ci    def __exit__(self, *_):
72619ea8026Sopenharmony_ci        self.f.close()
72719ea8026Sopenharmony_ci
72819ea8026Sopenharmony_ci    def writerow(self, row):
72919ea8026Sopenharmony_ci        with self.lock:
73019ea8026Sopenharmony_ci            self.rows.append(row)
73119ea8026Sopenharmony_ci            if all(k in self.head or k in self.tail for k in row.keys()):
73219ea8026Sopenharmony_ci                # can simply append
73319ea8026Sopenharmony_ci                self.writer.writerow(row)
73419ea8026Sopenharmony_ci            else:
73519ea8026Sopenharmony_ci                # need to rewrite the file
73619ea8026Sopenharmony_ci                self.head.extend(row.keys() - (self.head + self.tail))
73719ea8026Sopenharmony_ci                self.f.seek(0)
73819ea8026Sopenharmony_ci                self.f.truncate()
73919ea8026Sopenharmony_ci                self.writer = csv.DictWriter(self.f, self.head + self.tail)
74019ea8026Sopenharmony_ci                self.writer.writeheader()
74119ea8026Sopenharmony_ci                for row in self.rows:
74219ea8026Sopenharmony_ci                    self.writer.writerow(row)
74319ea8026Sopenharmony_ci
74419ea8026Sopenharmony_ci# A bench failure
74519ea8026Sopenharmony_ciclass BenchFailure(Exception):
74619ea8026Sopenharmony_ci    def __init__(self, id, returncode, stdout, assert_=None):
74719ea8026Sopenharmony_ci        self.id = id
74819ea8026Sopenharmony_ci        self.returncode = returncode
74919ea8026Sopenharmony_ci        self.stdout = stdout
75019ea8026Sopenharmony_ci        self.assert_ = assert_
75119ea8026Sopenharmony_ci
75219ea8026Sopenharmony_cidef run_stage(name, runner_, ids, stdout_, trace_, output_, **args):
75319ea8026Sopenharmony_ci    # get expected suite/case/perm counts
75419ea8026Sopenharmony_ci    (case_suites,
75519ea8026Sopenharmony_ci        expected_suite_perms,
75619ea8026Sopenharmony_ci        expected_case_perms,
75719ea8026Sopenharmony_ci        expected_perms,
75819ea8026Sopenharmony_ci        total_perms) = find_perms(runner_, ids, **args)
75919ea8026Sopenharmony_ci
76019ea8026Sopenharmony_ci    passed_suite_perms = co.defaultdict(lambda: 0)
76119ea8026Sopenharmony_ci    passed_case_perms = co.defaultdict(lambda: 0)
76219ea8026Sopenharmony_ci    passed_perms = 0
76319ea8026Sopenharmony_ci    readed = 0
76419ea8026Sopenharmony_ci    proged = 0
76519ea8026Sopenharmony_ci    erased = 0
76619ea8026Sopenharmony_ci    failures = []
76719ea8026Sopenharmony_ci    killed = False
76819ea8026Sopenharmony_ci
76919ea8026Sopenharmony_ci    pattern = re.compile('^(?:'
77019ea8026Sopenharmony_ci            '(?P<op>running|finished|skipped|powerloss)'
77119ea8026Sopenharmony_ci                ' (?P<id>(?P<case>[^:]+)[^\s]*)'
77219ea8026Sopenharmony_ci                '(?: (?P<readed>\d+))?'
77319ea8026Sopenharmony_ci                '(?: (?P<proged>\d+))?'
77419ea8026Sopenharmony_ci                '(?: (?P<erased>\d+))?'
77519ea8026Sopenharmony_ci            '|' '(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):'
77619ea8026Sopenharmony_ci                ' *(?P<message>.*)'
77719ea8026Sopenharmony_ci        ')$')
77819ea8026Sopenharmony_ci    locals = th.local()
77919ea8026Sopenharmony_ci    children = set()
78019ea8026Sopenharmony_ci
78119ea8026Sopenharmony_ci    def run_runner(runner_, ids=[]):
78219ea8026Sopenharmony_ci        nonlocal passed_suite_perms
78319ea8026Sopenharmony_ci        nonlocal passed_case_perms
78419ea8026Sopenharmony_ci        nonlocal passed_perms
78519ea8026Sopenharmony_ci        nonlocal readed
78619ea8026Sopenharmony_ci        nonlocal proged
78719ea8026Sopenharmony_ci        nonlocal erased
78819ea8026Sopenharmony_ci        nonlocal locals
78919ea8026Sopenharmony_ci
79019ea8026Sopenharmony_ci        # run the benches!
79119ea8026Sopenharmony_ci        cmd = runner_ + ids
79219ea8026Sopenharmony_ci        if args.get('verbose'):
79319ea8026Sopenharmony_ci            print(' '.join(shlex.quote(c) for c in cmd))
79419ea8026Sopenharmony_ci
79519ea8026Sopenharmony_ci        mpty, spty = pty.openpty()
79619ea8026Sopenharmony_ci        proc = sp.Popen(cmd, stdout=spty, stderr=spty, close_fds=False)
79719ea8026Sopenharmony_ci        os.close(spty)
79819ea8026Sopenharmony_ci        children.add(proc)
79919ea8026Sopenharmony_ci        mpty = os.fdopen(mpty, 'r', 1)
80019ea8026Sopenharmony_ci
80119ea8026Sopenharmony_ci        last_id = None
80219ea8026Sopenharmony_ci        last_stdout = co.deque(maxlen=args.get('context', 5) + 1)
80319ea8026Sopenharmony_ci        last_assert = None
80419ea8026Sopenharmony_ci        try:
80519ea8026Sopenharmony_ci            while True:
80619ea8026Sopenharmony_ci                # parse a line for state changes
80719ea8026Sopenharmony_ci                try:
80819ea8026Sopenharmony_ci                    line = mpty.readline()
80919ea8026Sopenharmony_ci                except OSError as e:
81019ea8026Sopenharmony_ci                    if e.errno != errno.EIO:
81119ea8026Sopenharmony_ci                        raise
81219ea8026Sopenharmony_ci                    break
81319ea8026Sopenharmony_ci                if not line:
81419ea8026Sopenharmony_ci                    break
81519ea8026Sopenharmony_ci                last_stdout.append(line)
81619ea8026Sopenharmony_ci                if stdout_:
81719ea8026Sopenharmony_ci                    try:
81819ea8026Sopenharmony_ci                        stdout_.write(line)
81919ea8026Sopenharmony_ci                        stdout_.flush()
82019ea8026Sopenharmony_ci                    except BrokenPipeError:
82119ea8026Sopenharmony_ci                        pass
82219ea8026Sopenharmony_ci
82319ea8026Sopenharmony_ci                m = pattern.match(line)
82419ea8026Sopenharmony_ci                if m:
82519ea8026Sopenharmony_ci                    op = m.group('op') or m.group('op_')
82619ea8026Sopenharmony_ci                    if op == 'running':
82719ea8026Sopenharmony_ci                        locals.seen_perms += 1
82819ea8026Sopenharmony_ci                        last_id = m.group('id')
82919ea8026Sopenharmony_ci                        last_stdout.clear()
83019ea8026Sopenharmony_ci                        last_assert = None
83119ea8026Sopenharmony_ci                    elif op == 'finished':
83219ea8026Sopenharmony_ci                        case = m.group('case')
83319ea8026Sopenharmony_ci                        suite = case_suites[case]
83419ea8026Sopenharmony_ci                        readed_ = int(m.group('readed'))
83519ea8026Sopenharmony_ci                        proged_ = int(m.group('proged'))
83619ea8026Sopenharmony_ci                        erased_ = int(m.group('erased'))
83719ea8026Sopenharmony_ci                        passed_suite_perms[suite] += 1
83819ea8026Sopenharmony_ci                        passed_case_perms[case] += 1
83919ea8026Sopenharmony_ci                        passed_perms += 1
84019ea8026Sopenharmony_ci                        readed += readed_
84119ea8026Sopenharmony_ci                        proged += proged_
84219ea8026Sopenharmony_ci                        erased += erased_
84319ea8026Sopenharmony_ci                        if output_:
84419ea8026Sopenharmony_ci                            # get defines and write to csv
84519ea8026Sopenharmony_ci                            defines = find_defines(
84619ea8026Sopenharmony_ci                                runner_, m.group('id'), **args)
84719ea8026Sopenharmony_ci                            output_.writerow({
84819ea8026Sopenharmony_ci                                'suite': suite,
84919ea8026Sopenharmony_ci                                'case': case,
85019ea8026Sopenharmony_ci                                'bench_readed': readed_,
85119ea8026Sopenharmony_ci                                'bench_proged': proged_,
85219ea8026Sopenharmony_ci                                'bench_erased': erased_,
85319ea8026Sopenharmony_ci                                **defines})
85419ea8026Sopenharmony_ci                    elif op == 'skipped':
85519ea8026Sopenharmony_ci                        locals.seen_perms += 1
85619ea8026Sopenharmony_ci                    elif op == 'assert':
85719ea8026Sopenharmony_ci                        last_assert = (
85819ea8026Sopenharmony_ci                            m.group('path'),
85919ea8026Sopenharmony_ci                            int(m.group('lineno')),
86019ea8026Sopenharmony_ci                            m.group('message'))
86119ea8026Sopenharmony_ci                        # go ahead and kill the process, aborting takes a while
86219ea8026Sopenharmony_ci                        if args.get('keep_going'):
86319ea8026Sopenharmony_ci                            proc.kill()
86419ea8026Sopenharmony_ci        except KeyboardInterrupt:
86519ea8026Sopenharmony_ci            raise BenchFailure(last_id, 1, list(last_stdout))
86619ea8026Sopenharmony_ci        finally:
86719ea8026Sopenharmony_ci            children.remove(proc)
86819ea8026Sopenharmony_ci            mpty.close()
86919ea8026Sopenharmony_ci
87019ea8026Sopenharmony_ci        proc.wait()
87119ea8026Sopenharmony_ci        if proc.returncode != 0:
87219ea8026Sopenharmony_ci            raise BenchFailure(
87319ea8026Sopenharmony_ci                last_id,
87419ea8026Sopenharmony_ci                proc.returncode,
87519ea8026Sopenharmony_ci                list(last_stdout),
87619ea8026Sopenharmony_ci                last_assert)
87719ea8026Sopenharmony_ci
87819ea8026Sopenharmony_ci    def run_job(runner_, ids=[], start=None, step=None):
87919ea8026Sopenharmony_ci        nonlocal failures
88019ea8026Sopenharmony_ci        nonlocal killed
88119ea8026Sopenharmony_ci        nonlocal locals
88219ea8026Sopenharmony_ci
88319ea8026Sopenharmony_ci        start = start or 0
88419ea8026Sopenharmony_ci        step = step or 1
88519ea8026Sopenharmony_ci        while start < total_perms:
88619ea8026Sopenharmony_ci            job_runner = runner_.copy()
88719ea8026Sopenharmony_ci            if args.get('isolate') or args.get('valgrind'):
88819ea8026Sopenharmony_ci                job_runner.append('-s%s,%s,%s' % (start, start+step, step))
88919ea8026Sopenharmony_ci            else:
89019ea8026Sopenharmony_ci                job_runner.append('-s%s,,%s' % (start, step))
89119ea8026Sopenharmony_ci
89219ea8026Sopenharmony_ci            try:
89319ea8026Sopenharmony_ci                # run the benches
89419ea8026Sopenharmony_ci                locals.seen_perms = 0
89519ea8026Sopenharmony_ci                run_runner(job_runner, ids)
89619ea8026Sopenharmony_ci                assert locals.seen_perms > 0
89719ea8026Sopenharmony_ci                start += locals.seen_perms*step
89819ea8026Sopenharmony_ci
89919ea8026Sopenharmony_ci            except BenchFailure as failure:
90019ea8026Sopenharmony_ci                # keep track of failures
90119ea8026Sopenharmony_ci                if output_:
90219ea8026Sopenharmony_ci                    case, _ = failure.id.split(':', 1)
90319ea8026Sopenharmony_ci                    suite = case_suites[case]
90419ea8026Sopenharmony_ci                    # get defines and write to csv
90519ea8026Sopenharmony_ci                    defines = find_defines(runner_, failure.id, **args)
90619ea8026Sopenharmony_ci                    output_.writerow({
90719ea8026Sopenharmony_ci                        'suite': suite,
90819ea8026Sopenharmony_ci                        'case': case,
90919ea8026Sopenharmony_ci                        **defines})
91019ea8026Sopenharmony_ci
91119ea8026Sopenharmony_ci                # race condition for multiple failures?
91219ea8026Sopenharmony_ci                if failures and not args.get('keep_going'):
91319ea8026Sopenharmony_ci                    break
91419ea8026Sopenharmony_ci
91519ea8026Sopenharmony_ci                failures.append(failure)
91619ea8026Sopenharmony_ci
91719ea8026Sopenharmony_ci                if args.get('keep_going') and not killed:
91819ea8026Sopenharmony_ci                    # resume after failed bench
91919ea8026Sopenharmony_ci                    assert locals.seen_perms > 0
92019ea8026Sopenharmony_ci                    start += locals.seen_perms*step
92119ea8026Sopenharmony_ci                    continue
92219ea8026Sopenharmony_ci                else:
92319ea8026Sopenharmony_ci                    # stop other benches
92419ea8026Sopenharmony_ci                    killed = True
92519ea8026Sopenharmony_ci                    for child in children.copy():
92619ea8026Sopenharmony_ci                        child.kill()
92719ea8026Sopenharmony_ci                    break
92819ea8026Sopenharmony_ci
92919ea8026Sopenharmony_ci
93019ea8026Sopenharmony_ci    # parallel jobs?
93119ea8026Sopenharmony_ci    runners = []
93219ea8026Sopenharmony_ci    if 'jobs' in args:
93319ea8026Sopenharmony_ci        for job in range(args['jobs']):
93419ea8026Sopenharmony_ci            runners.append(th.Thread(
93519ea8026Sopenharmony_ci                target=run_job, args=(runner_, ids, job, args['jobs']),
93619ea8026Sopenharmony_ci                daemon=True))
93719ea8026Sopenharmony_ci    else:
93819ea8026Sopenharmony_ci        runners.append(th.Thread(
93919ea8026Sopenharmony_ci            target=run_job, args=(runner_, ids, None, None),
94019ea8026Sopenharmony_ci            daemon=True))
94119ea8026Sopenharmony_ci
94219ea8026Sopenharmony_ci    def print_update(done):
94319ea8026Sopenharmony_ci        if not args.get('verbose') and (args['color'] or done):
94419ea8026Sopenharmony_ci            sys.stdout.write('%s%srunning %s%s:%s %s%s' % (
94519ea8026Sopenharmony_ci                '\r\x1b[K' if args['color'] else '',
94619ea8026Sopenharmony_ci                '\x1b[?7l' if not done else '',
94719ea8026Sopenharmony_ci                ('\x1b[34m' if not failures else '\x1b[31m')
94819ea8026Sopenharmony_ci                    if args['color'] else '',
94919ea8026Sopenharmony_ci                name,
95019ea8026Sopenharmony_ci                '\x1b[m' if args['color'] else '',
95119ea8026Sopenharmony_ci                ', '.join(filter(None, [
95219ea8026Sopenharmony_ci                    '%d/%d suites' % (
95319ea8026Sopenharmony_ci                        sum(passed_suite_perms[k] == v
95419ea8026Sopenharmony_ci                            for k, v in expected_suite_perms.items()),
95519ea8026Sopenharmony_ci                        len(expected_suite_perms))
95619ea8026Sopenharmony_ci                        if (not args.get('by_suites')
95719ea8026Sopenharmony_ci                            and not args.get('by_cases')) else None,
95819ea8026Sopenharmony_ci                    '%d/%d cases' % (
95919ea8026Sopenharmony_ci                        sum(passed_case_perms[k] == v
96019ea8026Sopenharmony_ci                            for k, v in expected_case_perms.items()),
96119ea8026Sopenharmony_ci                        len(expected_case_perms))
96219ea8026Sopenharmony_ci                        if not args.get('by_cases') else None,
96319ea8026Sopenharmony_ci                    '%d/%d perms' % (passed_perms, expected_perms),
96419ea8026Sopenharmony_ci                    '%s%d/%d failures%s' % (
96519ea8026Sopenharmony_ci                            '\x1b[31m' if args['color'] else '',
96619ea8026Sopenharmony_ci                            len(failures),
96719ea8026Sopenharmony_ci                            expected_perms,
96819ea8026Sopenharmony_ci                            '\x1b[m' if args['color'] else '')
96919ea8026Sopenharmony_ci                        if failures else None])),
97019ea8026Sopenharmony_ci                '\x1b[?7h' if not done else '\n'))
97119ea8026Sopenharmony_ci            sys.stdout.flush()
97219ea8026Sopenharmony_ci
97319ea8026Sopenharmony_ci    for r in runners:
97419ea8026Sopenharmony_ci        r.start()
97519ea8026Sopenharmony_ci
97619ea8026Sopenharmony_ci    try:
97719ea8026Sopenharmony_ci        while any(r.is_alive() for r in runners):
97819ea8026Sopenharmony_ci            time.sleep(0.01)
97919ea8026Sopenharmony_ci            print_update(False)
98019ea8026Sopenharmony_ci    except KeyboardInterrupt:
98119ea8026Sopenharmony_ci        # this is handled by the runner threads, we just
98219ea8026Sopenharmony_ci        # need to not abort here
98319ea8026Sopenharmony_ci        killed = True
98419ea8026Sopenharmony_ci    finally:
98519ea8026Sopenharmony_ci        print_update(True)
98619ea8026Sopenharmony_ci
98719ea8026Sopenharmony_ci    for r in runners:
98819ea8026Sopenharmony_ci        r.join()
98919ea8026Sopenharmony_ci
99019ea8026Sopenharmony_ci    return (
99119ea8026Sopenharmony_ci        expected_perms,
99219ea8026Sopenharmony_ci        passed_perms,
99319ea8026Sopenharmony_ci        readed,
99419ea8026Sopenharmony_ci        proged,
99519ea8026Sopenharmony_ci        erased,
99619ea8026Sopenharmony_ci        failures,
99719ea8026Sopenharmony_ci        killed)
99819ea8026Sopenharmony_ci
99919ea8026Sopenharmony_ci
100019ea8026Sopenharmony_cidef run(runner, bench_ids=[], **args):
100119ea8026Sopenharmony_ci    # query runner for benches
100219ea8026Sopenharmony_ci    runner_ = find_runner(runner, **args)
100319ea8026Sopenharmony_ci    print('using runner: %s' % ' '.join(shlex.quote(c) for c in runner_))
100419ea8026Sopenharmony_ci    (_,
100519ea8026Sopenharmony_ci        expected_suite_perms,
100619ea8026Sopenharmony_ci        expected_case_perms,
100719ea8026Sopenharmony_ci        expected_perms,
100819ea8026Sopenharmony_ci        total_perms) = find_perms(runner_, bench_ids, **args)
100919ea8026Sopenharmony_ci    print('found %d suites, %d cases, %d/%d permutations' % (
101019ea8026Sopenharmony_ci        len(expected_suite_perms),
101119ea8026Sopenharmony_ci        len(expected_case_perms),
101219ea8026Sopenharmony_ci        expected_perms,
101319ea8026Sopenharmony_ci        total_perms))
101419ea8026Sopenharmony_ci    print()
101519ea8026Sopenharmony_ci
101619ea8026Sopenharmony_ci    # automatic job detection?
101719ea8026Sopenharmony_ci    if args.get('jobs') == 0:
101819ea8026Sopenharmony_ci        args['jobs'] = len(os.sched_getaffinity(0))
101919ea8026Sopenharmony_ci
102019ea8026Sopenharmony_ci    # truncate and open logs here so they aren't disconnected between benches
102119ea8026Sopenharmony_ci    stdout = None
102219ea8026Sopenharmony_ci    if args.get('stdout'):
102319ea8026Sopenharmony_ci        stdout = openio(args['stdout'], 'w', 1)
102419ea8026Sopenharmony_ci    trace = None
102519ea8026Sopenharmony_ci    if args.get('trace'):
102619ea8026Sopenharmony_ci        trace = openio(args['trace'], 'w', 1)
102719ea8026Sopenharmony_ci    output = None
102819ea8026Sopenharmony_ci    if args.get('output'):
102919ea8026Sopenharmony_ci        output = BenchOutput(args['output'],
103019ea8026Sopenharmony_ci            ['suite', 'case'],
103119ea8026Sopenharmony_ci            ['bench_readed', 'bench_proged', 'bench_erased'])
103219ea8026Sopenharmony_ci
103319ea8026Sopenharmony_ci    # measure runtime
103419ea8026Sopenharmony_ci    start = time.time()
103519ea8026Sopenharmony_ci
103619ea8026Sopenharmony_ci    # spawn runners
103719ea8026Sopenharmony_ci    expected = 0
103819ea8026Sopenharmony_ci    passed = 0
103919ea8026Sopenharmony_ci    readed = 0
104019ea8026Sopenharmony_ci    proged = 0
104119ea8026Sopenharmony_ci    erased = 0
104219ea8026Sopenharmony_ci    failures = []
104319ea8026Sopenharmony_ci    for by in (bench_ids if bench_ids
104419ea8026Sopenharmony_ci            else expected_case_perms.keys() if args.get('by_cases')
104519ea8026Sopenharmony_ci            else expected_suite_perms.keys() if args.get('by_suites')
104619ea8026Sopenharmony_ci            else [None]):
104719ea8026Sopenharmony_ci        # spawn jobs for stage
104819ea8026Sopenharmony_ci        (expected_,
104919ea8026Sopenharmony_ci            passed_,
105019ea8026Sopenharmony_ci            readed_,
105119ea8026Sopenharmony_ci            proged_,
105219ea8026Sopenharmony_ci            erased_,
105319ea8026Sopenharmony_ci            failures_,
105419ea8026Sopenharmony_ci            killed) = run_stage(
105519ea8026Sopenharmony_ci                by or 'benches',
105619ea8026Sopenharmony_ci                runner_,
105719ea8026Sopenharmony_ci                [by] if by is not None else [],
105819ea8026Sopenharmony_ci                stdout,
105919ea8026Sopenharmony_ci                trace,
106019ea8026Sopenharmony_ci                output,
106119ea8026Sopenharmony_ci                **args)
106219ea8026Sopenharmony_ci        # collect passes/failures
106319ea8026Sopenharmony_ci        expected += expected_
106419ea8026Sopenharmony_ci        passed += passed_
106519ea8026Sopenharmony_ci        readed += readed_
106619ea8026Sopenharmony_ci        proged += proged_
106719ea8026Sopenharmony_ci        erased += erased_
106819ea8026Sopenharmony_ci        failures.extend(failures_)
106919ea8026Sopenharmony_ci        if (failures and not args.get('keep_going')) or killed:
107019ea8026Sopenharmony_ci            break
107119ea8026Sopenharmony_ci
107219ea8026Sopenharmony_ci    stop = time.time()
107319ea8026Sopenharmony_ci
107419ea8026Sopenharmony_ci    if stdout:
107519ea8026Sopenharmony_ci        try:
107619ea8026Sopenharmony_ci            stdout.close()
107719ea8026Sopenharmony_ci        except BrokenPipeError:
107819ea8026Sopenharmony_ci            pass
107919ea8026Sopenharmony_ci    if trace:
108019ea8026Sopenharmony_ci        try:
108119ea8026Sopenharmony_ci            trace.close()
108219ea8026Sopenharmony_ci        except BrokenPipeError:
108319ea8026Sopenharmony_ci            pass
108419ea8026Sopenharmony_ci    if output:
108519ea8026Sopenharmony_ci        output.close()
108619ea8026Sopenharmony_ci
108719ea8026Sopenharmony_ci    # show summary
108819ea8026Sopenharmony_ci    print()
108919ea8026Sopenharmony_ci    print('%sdone:%s %s' % (
109019ea8026Sopenharmony_ci        ('\x1b[34m' if not failures else '\x1b[31m')
109119ea8026Sopenharmony_ci            if args['color'] else '',
109219ea8026Sopenharmony_ci        '\x1b[m' if args['color'] else '',
109319ea8026Sopenharmony_ci        ', '.join(filter(None, [
109419ea8026Sopenharmony_ci            '%d readed' % readed,
109519ea8026Sopenharmony_ci            '%d proged' % proged,
109619ea8026Sopenharmony_ci            '%d erased' % erased,
109719ea8026Sopenharmony_ci            'in %.2fs' % (stop-start)]))))
109819ea8026Sopenharmony_ci    print()
109919ea8026Sopenharmony_ci
110019ea8026Sopenharmony_ci    # print each failure
110119ea8026Sopenharmony_ci    for failure in failures:
110219ea8026Sopenharmony_ci        assert failure.id is not None, '%s broken? %r' % (
110319ea8026Sopenharmony_ci            ' '.join(shlex.quote(c) for c in runner_),
110419ea8026Sopenharmony_ci            failure)
110519ea8026Sopenharmony_ci
110619ea8026Sopenharmony_ci        # get some extra info from runner
110719ea8026Sopenharmony_ci        path, lineno = find_path(runner_, failure.id, **args)
110819ea8026Sopenharmony_ci        defines = find_defines(runner_, failure.id, **args)
110919ea8026Sopenharmony_ci
111019ea8026Sopenharmony_ci        # show summary of failure
111119ea8026Sopenharmony_ci        print('%s%s:%d:%sfailure:%s %s%s failed' % (
111219ea8026Sopenharmony_ci            '\x1b[01m' if args['color'] else '',
111319ea8026Sopenharmony_ci            path, lineno,
111419ea8026Sopenharmony_ci            '\x1b[01;31m' if args['color'] else '',
111519ea8026Sopenharmony_ci            '\x1b[m' if args['color'] else '',
111619ea8026Sopenharmony_ci            failure.id,
111719ea8026Sopenharmony_ci            ' (%s)' % ', '.join('%s=%s' % (k,v) for k,v in defines.items())
111819ea8026Sopenharmony_ci                if defines else ''))
111919ea8026Sopenharmony_ci
112019ea8026Sopenharmony_ci        if failure.stdout:
112119ea8026Sopenharmony_ci            stdout = failure.stdout
112219ea8026Sopenharmony_ci            if failure.assert_ is not None:
112319ea8026Sopenharmony_ci                stdout = stdout[:-1]
112419ea8026Sopenharmony_ci            for line in stdout[-args.get('context', 5):]:
112519ea8026Sopenharmony_ci                sys.stdout.write(line)
112619ea8026Sopenharmony_ci
112719ea8026Sopenharmony_ci        if failure.assert_ is not None:
112819ea8026Sopenharmony_ci            path, lineno, message = failure.assert_
112919ea8026Sopenharmony_ci            print('%s%s:%d:%sassert:%s %s' % (
113019ea8026Sopenharmony_ci                '\x1b[01m' if args['color'] else '',
113119ea8026Sopenharmony_ci                path, lineno,
113219ea8026Sopenharmony_ci                '\x1b[01;31m' if args['color'] else '',
113319ea8026Sopenharmony_ci                '\x1b[m' if args['color'] else '',
113419ea8026Sopenharmony_ci                message))
113519ea8026Sopenharmony_ci            with open(path) as f:
113619ea8026Sopenharmony_ci                line = next(it.islice(f, lineno-1, None)).strip('\n')
113719ea8026Sopenharmony_ci                print(line)
113819ea8026Sopenharmony_ci        print()
113919ea8026Sopenharmony_ci
114019ea8026Sopenharmony_ci    # drop into gdb?
114119ea8026Sopenharmony_ci    if failures and (args.get('gdb')
114219ea8026Sopenharmony_ci            or args.get('gdb_case')
114319ea8026Sopenharmony_ci            or args.get('gdb_main')):
114419ea8026Sopenharmony_ci        failure = failures[0]
114519ea8026Sopenharmony_ci        cmd = runner_ + [failure.id]
114619ea8026Sopenharmony_ci
114719ea8026Sopenharmony_ci        if args.get('gdb_main'):
114819ea8026Sopenharmony_ci            # we don't really need the case breakpoint here, but it
114919ea8026Sopenharmony_ci            # can be helpful
115019ea8026Sopenharmony_ci            path, lineno = find_path(runner_, failure.id, **args)
115119ea8026Sopenharmony_ci            cmd[:0] = args['gdb_path'] + [
115219ea8026Sopenharmony_ci                '-ex', 'break main',
115319ea8026Sopenharmony_ci                '-ex', 'break %s:%d' % (path, lineno),
115419ea8026Sopenharmony_ci                '-ex', 'run',
115519ea8026Sopenharmony_ci                '--args']
115619ea8026Sopenharmony_ci        elif args.get('gdb_case'):
115719ea8026Sopenharmony_ci            path, lineno = find_path(runner_, failure.id, **args)
115819ea8026Sopenharmony_ci            cmd[:0] = args['gdb_path'] + [
115919ea8026Sopenharmony_ci                '-ex', 'break %s:%d' % (path, lineno),
116019ea8026Sopenharmony_ci                '-ex', 'run',
116119ea8026Sopenharmony_ci                '--args']
116219ea8026Sopenharmony_ci        elif failure.assert_ is not None:
116319ea8026Sopenharmony_ci            cmd[:0] = args['gdb_path'] + [
116419ea8026Sopenharmony_ci                '-ex', 'run',
116519ea8026Sopenharmony_ci                '-ex', 'frame function raise',
116619ea8026Sopenharmony_ci                '-ex', 'up 2',
116719ea8026Sopenharmony_ci                '--args']
116819ea8026Sopenharmony_ci        else:
116919ea8026Sopenharmony_ci            cmd[:0] = args['gdb_path'] + [
117019ea8026Sopenharmony_ci                '-ex', 'run',
117119ea8026Sopenharmony_ci                '--args']
117219ea8026Sopenharmony_ci
117319ea8026Sopenharmony_ci        # exec gdb interactively
117419ea8026Sopenharmony_ci        if args.get('verbose'):
117519ea8026Sopenharmony_ci            print(' '.join(shlex.quote(c) for c in cmd))
117619ea8026Sopenharmony_ci        os.execvp(cmd[0], cmd)
117719ea8026Sopenharmony_ci
117819ea8026Sopenharmony_ci    return 1 if failures else 0
117919ea8026Sopenharmony_ci
118019ea8026Sopenharmony_ci
118119ea8026Sopenharmony_cidef main(**args):
118219ea8026Sopenharmony_ci    # figure out what color should be
118319ea8026Sopenharmony_ci    if args.get('color') == 'auto':
118419ea8026Sopenharmony_ci        args['color'] = sys.stdout.isatty()
118519ea8026Sopenharmony_ci    elif args.get('color') == 'always':
118619ea8026Sopenharmony_ci        args['color'] = True
118719ea8026Sopenharmony_ci    else:
118819ea8026Sopenharmony_ci        args['color'] = False
118919ea8026Sopenharmony_ci
119019ea8026Sopenharmony_ci    if args.get('compile'):
119119ea8026Sopenharmony_ci        return compile(**args)
119219ea8026Sopenharmony_ci    elif (args.get('summary')
119319ea8026Sopenharmony_ci            or args.get('list_suites')
119419ea8026Sopenharmony_ci            or args.get('list_cases')
119519ea8026Sopenharmony_ci            or args.get('list_suite_paths')
119619ea8026Sopenharmony_ci            or args.get('list_case_paths')
119719ea8026Sopenharmony_ci            or args.get('list_defines')
119819ea8026Sopenharmony_ci            or args.get('list_permutation_defines')
119919ea8026Sopenharmony_ci            or args.get('list_implicit_defines')
120019ea8026Sopenharmony_ci            or args.get('list_geometries')):
120119ea8026Sopenharmony_ci        return list_(**args)
120219ea8026Sopenharmony_ci    else:
120319ea8026Sopenharmony_ci        return run(**args)
120419ea8026Sopenharmony_ci
120519ea8026Sopenharmony_ci
120619ea8026Sopenharmony_ciif __name__ == "__main__":
120719ea8026Sopenharmony_ci    import argparse
120819ea8026Sopenharmony_ci    import sys
120919ea8026Sopenharmony_ci    argparse.ArgumentParser._handle_conflict_ignore = lambda *_: None
121019ea8026Sopenharmony_ci    argparse._ArgumentGroup._handle_conflict_ignore = lambda *_: None
121119ea8026Sopenharmony_ci    parser = argparse.ArgumentParser(
121219ea8026Sopenharmony_ci        description="Build and run benches.",
121319ea8026Sopenharmony_ci        allow_abbrev=False,
121419ea8026Sopenharmony_ci        conflict_handler='ignore')
121519ea8026Sopenharmony_ci    parser.add_argument(
121619ea8026Sopenharmony_ci        '-v', '--verbose',
121719ea8026Sopenharmony_ci        action='store_true',
121819ea8026Sopenharmony_ci        help="Output commands that run behind the scenes.")
121919ea8026Sopenharmony_ci    parser.add_argument(
122019ea8026Sopenharmony_ci        '--color',
122119ea8026Sopenharmony_ci        choices=['never', 'always', 'auto'],
122219ea8026Sopenharmony_ci        default='auto',
122319ea8026Sopenharmony_ci        help="When to use terminal colors. Defaults to 'auto'.")
122419ea8026Sopenharmony_ci
122519ea8026Sopenharmony_ci    # bench flags
122619ea8026Sopenharmony_ci    bench_parser = parser.add_argument_group('bench options')
122719ea8026Sopenharmony_ci    bench_parser.add_argument(
122819ea8026Sopenharmony_ci        'runner',
122919ea8026Sopenharmony_ci        nargs='?',
123019ea8026Sopenharmony_ci        type=lambda x: x.split(),
123119ea8026Sopenharmony_ci        help="Bench runner to use for benching. Defaults to %r." % RUNNER_PATH)
123219ea8026Sopenharmony_ci    bench_parser.add_argument(
123319ea8026Sopenharmony_ci        'bench_ids',
123419ea8026Sopenharmony_ci        nargs='*',
123519ea8026Sopenharmony_ci        help="Description of benches to run.")
123619ea8026Sopenharmony_ci    bench_parser.add_argument(
123719ea8026Sopenharmony_ci        '-Y', '--summary',
123819ea8026Sopenharmony_ci        action='store_true',
123919ea8026Sopenharmony_ci        help="Show quick summary.")
124019ea8026Sopenharmony_ci    bench_parser.add_argument(
124119ea8026Sopenharmony_ci        '-l', '--list-suites',
124219ea8026Sopenharmony_ci        action='store_true',
124319ea8026Sopenharmony_ci        help="List bench suites.")
124419ea8026Sopenharmony_ci    bench_parser.add_argument(
124519ea8026Sopenharmony_ci        '-L', '--list-cases',
124619ea8026Sopenharmony_ci        action='store_true',
124719ea8026Sopenharmony_ci        help="List bench cases.")
124819ea8026Sopenharmony_ci    bench_parser.add_argument(
124919ea8026Sopenharmony_ci        '--list-suite-paths',
125019ea8026Sopenharmony_ci        action='store_true',
125119ea8026Sopenharmony_ci        help="List the path for each bench suite.")
125219ea8026Sopenharmony_ci    bench_parser.add_argument(
125319ea8026Sopenharmony_ci        '--list-case-paths',
125419ea8026Sopenharmony_ci        action='store_true',
125519ea8026Sopenharmony_ci        help="List the path and line number for each bench case.")
125619ea8026Sopenharmony_ci    bench_parser.add_argument(
125719ea8026Sopenharmony_ci        '--list-defines',
125819ea8026Sopenharmony_ci        action='store_true',
125919ea8026Sopenharmony_ci        help="List all defines in this bench-runner.")
126019ea8026Sopenharmony_ci    bench_parser.add_argument(
126119ea8026Sopenharmony_ci        '--list-permutation-defines',
126219ea8026Sopenharmony_ci        action='store_true',
126319ea8026Sopenharmony_ci        help="List explicit defines in this bench-runner.")
126419ea8026Sopenharmony_ci    bench_parser.add_argument(
126519ea8026Sopenharmony_ci        '--list-implicit-defines',
126619ea8026Sopenharmony_ci        action='store_true',
126719ea8026Sopenharmony_ci        help="List implicit defines in this bench-runner.")
126819ea8026Sopenharmony_ci    bench_parser.add_argument(
126919ea8026Sopenharmony_ci        '--list-geometries',
127019ea8026Sopenharmony_ci        action='store_true',
127119ea8026Sopenharmony_ci        help="List the available disk geometries.")
127219ea8026Sopenharmony_ci    bench_parser.add_argument(
127319ea8026Sopenharmony_ci        '-D', '--define',
127419ea8026Sopenharmony_ci        action='append',
127519ea8026Sopenharmony_ci        help="Override a bench define.")
127619ea8026Sopenharmony_ci    bench_parser.add_argument(
127719ea8026Sopenharmony_ci        '-G', '--geometry',
127819ea8026Sopenharmony_ci        help="Comma-separated list of disk geometries to bench.")
127919ea8026Sopenharmony_ci    bench_parser.add_argument(
128019ea8026Sopenharmony_ci        '-d', '--disk',
128119ea8026Sopenharmony_ci        help="Direct block device operations to this file.")
128219ea8026Sopenharmony_ci    bench_parser.add_argument(
128319ea8026Sopenharmony_ci        '-t', '--trace',
128419ea8026Sopenharmony_ci        help="Direct trace output to this file.")
128519ea8026Sopenharmony_ci    bench_parser.add_argument(
128619ea8026Sopenharmony_ci        '--trace-backtrace',
128719ea8026Sopenharmony_ci        action='store_true',
128819ea8026Sopenharmony_ci        help="Include a backtrace with every trace statement.")
128919ea8026Sopenharmony_ci    bench_parser.add_argument(
129019ea8026Sopenharmony_ci        '--trace-period',
129119ea8026Sopenharmony_ci        help="Sample trace output at this period in cycles.")
129219ea8026Sopenharmony_ci    bench_parser.add_argument(
129319ea8026Sopenharmony_ci        '--trace-freq',
129419ea8026Sopenharmony_ci        help="Sample trace output at this frequency in hz.")
129519ea8026Sopenharmony_ci    bench_parser.add_argument(
129619ea8026Sopenharmony_ci        '-O', '--stdout',
129719ea8026Sopenharmony_ci        help="Direct stdout to this file. Note stderr is already merged here.")
129819ea8026Sopenharmony_ci    bench_parser.add_argument(
129919ea8026Sopenharmony_ci        '-o', '--output',
130019ea8026Sopenharmony_ci        help="CSV file to store results.")
130119ea8026Sopenharmony_ci    bench_parser.add_argument(
130219ea8026Sopenharmony_ci        '--read-sleep',
130319ea8026Sopenharmony_ci        help="Artificial read delay in seconds.")
130419ea8026Sopenharmony_ci    bench_parser.add_argument(
130519ea8026Sopenharmony_ci        '--prog-sleep',
130619ea8026Sopenharmony_ci        help="Artificial prog delay in seconds.")
130719ea8026Sopenharmony_ci    bench_parser.add_argument(
130819ea8026Sopenharmony_ci        '--erase-sleep',
130919ea8026Sopenharmony_ci        help="Artificial erase delay in seconds.")
131019ea8026Sopenharmony_ci    bench_parser.add_argument(
131119ea8026Sopenharmony_ci        '-j', '--jobs',
131219ea8026Sopenharmony_ci        nargs='?',
131319ea8026Sopenharmony_ci        type=lambda x: int(x, 0),
131419ea8026Sopenharmony_ci        const=0,
131519ea8026Sopenharmony_ci        help="Number of parallel runners to run. 0 runs one runner per core.")
131619ea8026Sopenharmony_ci    bench_parser.add_argument(
131719ea8026Sopenharmony_ci        '-k', '--keep-going',
131819ea8026Sopenharmony_ci        action='store_true',
131919ea8026Sopenharmony_ci        help="Don't stop on first error.")
132019ea8026Sopenharmony_ci    bench_parser.add_argument(
132119ea8026Sopenharmony_ci        '-i', '--isolate',
132219ea8026Sopenharmony_ci        action='store_true',
132319ea8026Sopenharmony_ci        help="Run each bench permutation in a separate process.")
132419ea8026Sopenharmony_ci    bench_parser.add_argument(
132519ea8026Sopenharmony_ci        '-b', '--by-suites',
132619ea8026Sopenharmony_ci        action='store_true',
132719ea8026Sopenharmony_ci        help="Step through benches by suite.")
132819ea8026Sopenharmony_ci    bench_parser.add_argument(
132919ea8026Sopenharmony_ci        '-B', '--by-cases',
133019ea8026Sopenharmony_ci        action='store_true',
133119ea8026Sopenharmony_ci        help="Step through benches by case.")
133219ea8026Sopenharmony_ci    bench_parser.add_argument(
133319ea8026Sopenharmony_ci        '--context',
133419ea8026Sopenharmony_ci        type=lambda x: int(x, 0),
133519ea8026Sopenharmony_ci        default=5,
133619ea8026Sopenharmony_ci        help="Show this many lines of stdout on bench failure. "
133719ea8026Sopenharmony_ci            "Defaults to 5.")
133819ea8026Sopenharmony_ci    bench_parser.add_argument(
133919ea8026Sopenharmony_ci        '--gdb',
134019ea8026Sopenharmony_ci        action='store_true',
134119ea8026Sopenharmony_ci        help="Drop into gdb on bench failure.")
134219ea8026Sopenharmony_ci    bench_parser.add_argument(
134319ea8026Sopenharmony_ci        '--gdb-case',
134419ea8026Sopenharmony_ci        action='store_true',
134519ea8026Sopenharmony_ci        help="Drop into gdb on bench failure but stop at the beginning "
134619ea8026Sopenharmony_ci            "of the failing bench case.")
134719ea8026Sopenharmony_ci    bench_parser.add_argument(
134819ea8026Sopenharmony_ci        '--gdb-main',
134919ea8026Sopenharmony_ci        action='store_true',
135019ea8026Sopenharmony_ci        help="Drop into gdb on bench failure but stop at the beginning "
135119ea8026Sopenharmony_ci            "of main.")
135219ea8026Sopenharmony_ci    bench_parser.add_argument(
135319ea8026Sopenharmony_ci        '--gdb-path',
135419ea8026Sopenharmony_ci        type=lambda x: x.split(),
135519ea8026Sopenharmony_ci        default=GDB_PATH,
135619ea8026Sopenharmony_ci        help="Path to the gdb executable, may include flags. "
135719ea8026Sopenharmony_ci            "Defaults to %r." % GDB_PATH)
135819ea8026Sopenharmony_ci    bench_parser.add_argument(
135919ea8026Sopenharmony_ci        '--exec',
136019ea8026Sopenharmony_ci        type=lambda e: e.split(),
136119ea8026Sopenharmony_ci        help="Run under another executable.")
136219ea8026Sopenharmony_ci    bench_parser.add_argument(
136319ea8026Sopenharmony_ci        '--valgrind',
136419ea8026Sopenharmony_ci        action='store_true',
136519ea8026Sopenharmony_ci        help="Run under Valgrind to find memory errors. Implicitly sets "
136619ea8026Sopenharmony_ci            "--isolate.")
136719ea8026Sopenharmony_ci    bench_parser.add_argument(
136819ea8026Sopenharmony_ci        '--valgrind-path',
136919ea8026Sopenharmony_ci        type=lambda x: x.split(),
137019ea8026Sopenharmony_ci        default=VALGRIND_PATH,
137119ea8026Sopenharmony_ci        help="Path to the Valgrind executable, may include flags. "
137219ea8026Sopenharmony_ci            "Defaults to %r." % VALGRIND_PATH)
137319ea8026Sopenharmony_ci    bench_parser.add_argument(
137419ea8026Sopenharmony_ci        '-p', '--perf',
137519ea8026Sopenharmony_ci        help="Run under Linux's perf to sample performance counters, writing "
137619ea8026Sopenharmony_ci            "samples to this file.")
137719ea8026Sopenharmony_ci    bench_parser.add_argument(
137819ea8026Sopenharmony_ci        '--perf-freq',
137919ea8026Sopenharmony_ci        help="perf sampling frequency. This is passed directly to the perf "
138019ea8026Sopenharmony_ci            "script.")
138119ea8026Sopenharmony_ci    bench_parser.add_argument(
138219ea8026Sopenharmony_ci        '--perf-period',
138319ea8026Sopenharmony_ci        help="perf sampling period. This is passed directly to the perf "
138419ea8026Sopenharmony_ci            "script.")
138519ea8026Sopenharmony_ci    bench_parser.add_argument(
138619ea8026Sopenharmony_ci        '--perf-events',
138719ea8026Sopenharmony_ci        help="perf events to record. This is passed directly to the perf "
138819ea8026Sopenharmony_ci            "script.")
138919ea8026Sopenharmony_ci    bench_parser.add_argument(
139019ea8026Sopenharmony_ci        '--perf-script',
139119ea8026Sopenharmony_ci        type=lambda x: x.split(),
139219ea8026Sopenharmony_ci        default=PERF_SCRIPT,
139319ea8026Sopenharmony_ci        help="Path to the perf script to use. Defaults to %r." % PERF_SCRIPT)
139419ea8026Sopenharmony_ci    bench_parser.add_argument(
139519ea8026Sopenharmony_ci        '--perf-path',
139619ea8026Sopenharmony_ci        type=lambda x: x.split(),
139719ea8026Sopenharmony_ci        help="Path to the perf executable, may include flags. This is passed "
139819ea8026Sopenharmony_ci            "directly to the perf script")
139919ea8026Sopenharmony_ci
140019ea8026Sopenharmony_ci    # compilation flags
140119ea8026Sopenharmony_ci    comp_parser = parser.add_argument_group('compilation options')
140219ea8026Sopenharmony_ci    comp_parser.add_argument(
140319ea8026Sopenharmony_ci        'bench_paths',
140419ea8026Sopenharmony_ci        nargs='*',
140519ea8026Sopenharmony_ci        help="Description of *.toml files to compile. May be a directory "
140619ea8026Sopenharmony_ci            "or a list of paths.")
140719ea8026Sopenharmony_ci    comp_parser.add_argument(
140819ea8026Sopenharmony_ci        '-c', '--compile',
140919ea8026Sopenharmony_ci        action='store_true',
141019ea8026Sopenharmony_ci        help="Compile a bench suite or source file.")
141119ea8026Sopenharmony_ci    comp_parser.add_argument(
141219ea8026Sopenharmony_ci        '-s', '--source',
141319ea8026Sopenharmony_ci        help="Source file to compile, possibly injecting internal benches.")
141419ea8026Sopenharmony_ci    comp_parser.add_argument(
141519ea8026Sopenharmony_ci        '--include',
141619ea8026Sopenharmony_ci        default=HEADER_PATH,
141719ea8026Sopenharmony_ci        help="Inject this header file into every compiled bench file. "
141819ea8026Sopenharmony_ci            "Defaults to %r." % HEADER_PATH)
141919ea8026Sopenharmony_ci    comp_parser.add_argument(
142019ea8026Sopenharmony_ci        '-o', '--output',
142119ea8026Sopenharmony_ci        help="Output file.")
142219ea8026Sopenharmony_ci
142319ea8026Sopenharmony_ci    # runner/bench_paths overlap, so need to do some munging here
142419ea8026Sopenharmony_ci    args = parser.parse_intermixed_args()
142519ea8026Sopenharmony_ci    args.bench_paths = [' '.join(args.runner or [])] + args.bench_ids
142619ea8026Sopenharmony_ci    args.runner = args.runner or [RUNNER_PATH]
142719ea8026Sopenharmony_ci
142819ea8026Sopenharmony_ci    sys.exit(main(**{k: v
142919ea8026Sopenharmony_ci        for k, v in vars(args).items()
143019ea8026Sopenharmony_ci        if v is not None}))
1431