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