diff --git a/Makefile b/Makefile index b40fb7ab..c8ae6ab1 100644 --- a/Makefile +++ b/Makefile @@ -600,9 +600,7 @@ bench-marks: SUMMARYFLAGS+=-Si bench-marks: $(BENCH_CSV) $(strip ./scripts/csv.py \ <(./scripts/csv.py $^ \ - -Dprobe=append,remove,create,delete,fetch,lookup,$\ - commit,namelookup,$\ - write,stat,read \ + -Uprobe=stack,heap,usage \ -bprobe='%(case)s+%(probe)s' \ -Fi='min(enumerate())' \ -fn='max(n)' \ @@ -624,18 +622,14 @@ bench-marks-csv: $(BUILDDIR)/lfs3.bench.csv bench-marks-diff: $(BENCH_CSV) $(strip ./scripts/csv.py \ <(./scripts/csv.py $^ \ - -Dprobe=append,remove,create,delete,fetch,lookup,$\ - commit,namelookup,$\ - write,stat,read \ + -Uprobe=stack,heap,usage \ -bprobe='%(case)s+%(probe)s' \ -Fi='min(enumerate())' \ -fn='max(n)' \ -ft='max(float(bench_simtime)/1.0e9)' \ -o-) \ -d <(./scripts/csv.py $(BUILDDIR)/lfs3.bench.csv \ - -Dprobe=append,remove,create,delete,fetch,lookup,$\ - commit,namelookup,$\ - write,stat,read \ + -Uprobe=stack,heap,usage \ -bprobe='%(case)s+%(probe)s' \ -Fi='min(enumerate())' \ -fn='max(n)' \ @@ -652,9 +646,7 @@ bench-bottlenecks: SUMMARYFLAGS+=-Sruntime bench-bottlenecks: $(BENCH_CSV) $(strip ./scripts/csv.py \ <(./scripts/csv.py $^ \ - -Dprobe=append,remove,create,delete,fetch,lookup,$\ - commit,namelookup,$\ - write,stat,read \ + -Uprobe=stack,heap,usage \ -bprobe='%(case)s+%(probe)s' \ -Fi='min(enumerate())' \ -fn='max(n)' \ @@ -673,9 +665,7 @@ bench-bottlenecks: $(BENCH_CSV) bench-ops: SUMMARYFLAGS+=-Si bench-ops: $(BENCH_CSV) $(strip ./scripts/csv.py $^ \ - -Dprobe=append,remove,create,delete,fetch,lookup,$\ - commit,namelookup,$\ - write,stat,read \ + -Uprobe=stack,heap,usage \ -bprobe='%(case)s+%(probe)s' \ -Hprobe=bench+probe \ -Fi='min(enumerate())' \ @@ -692,9 +682,7 @@ bench-ops: $(BENCH_CSV) bench-widths: SUMMARYFLAGS+=-Si bench-widths: $(BENCH_CSV) $(strip ./scripts/csv.py $^ \ - -Dprobe=append,remove,create,delete,fetch,lookup,$\ - commit,namelookup,$\ - write,stat,read \ + -Uprobe=stack,heap,usage \ -bprobe='%(case)s+%(probe)s' \ -Hprobe=bench+probe \ -Fi='min(enumerate())' \ diff --git a/scripts/code.py b/scripts/code.py index 20f92133..8bdfef7e 100755 --- a/scripts/code.py +++ b/scripts/code.py @@ -529,6 +529,7 @@ class Rev(co.namedtuple('Rev', 'a')): def fold(Result, results, *, by=None, defines=[], + undefines=[], sort=None, depth=1, **_): @@ -540,20 +541,27 @@ def fold(Result, results, *, if by is None: by = Result._by - for k in it.chain(by or [], (k for k, _ in defines)): + for k in it.chain(by or [], + (k for k, _ in defines), + (k for k, _ in undefines)): if k not in Result._by and k not in Result._fields: print("error: could not find field %r?" % k, file=sys.stderr) sys.exit(-1) # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + if not all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ # organize results into conflicts @@ -1048,6 +1056,7 @@ def main(obj_paths, *, by=None, fields=None, defines=[], + undefines=[], sort=None, **args): # figure out what fields we're interested in @@ -1080,6 +1089,7 @@ def main(obj_paths, *, results = fold(CodeResult, results, by=by, defines=defines, + undefines=undefines, sort=sort) # find previous results? @@ -1096,7 +1106,8 @@ def main(obj_paths, *, # fold diff_results = fold(CodeResult, diff_results, by=by, - defines=defines) + defines=defines, + undefines=undefines) # write results to JSON if args.get('output_json'): @@ -1188,6 +1199,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/cov.py b/scripts/cov.py index d318cd84..f3e046b2 100755 --- a/scripts/cov.py +++ b/scripts/cov.py @@ -398,6 +398,7 @@ class Rev(co.namedtuple('Rev', 'a')): def fold(Result, results, *, by=None, defines=[], + undefines=[], sort=None, depth=1, **_): @@ -409,20 +410,27 @@ def fold(Result, results, *, if by is None: by = Result._by - for k in it.chain(by or [], (k for k, _ in defines)): + for k in it.chain(by or [], + (k for k, _ in defines), + (k for k, _ in undefines)): if k not in Result._by and k not in Result._fields: print("error: could not find field %r?" % k, file=sys.stderr) sys.exit(-1) # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + if not all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ # organize results into conflicts @@ -992,6 +1000,7 @@ def main(gcda_paths, *, by=None, fields=None, defines=[], + undefines=[], sort=None, hits=False, **args): @@ -1046,6 +1055,7 @@ def main(gcda_paths, *, results = fold(CovResult, results, by=by, defines=defines, + undefines=undefines, sort=sort) # find previous results? @@ -1062,7 +1072,8 @@ def main(gcda_paths, *, # fold diff_results = fold(CovResult, diff_results, by=by, - defines=defines) + defines=defines, + undefines=undefines) # annotate sources if (args.get('annotate') @@ -1167,6 +1178,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/csv.py b/scripts/csv.py index bc64f2f4..578d8524 100755 --- a/scripts/csv.py +++ b/scripts/csv.py @@ -2069,6 +2069,7 @@ def compile(fields_, results, def homogenize(Result, results, *, defines=[], + undefines=[], depth=1, depth_=0, **_): @@ -2087,6 +2088,10 @@ def homogenize(Result, results, *, for v in vs) for k, vs in defines): continue + if any(any(fnmatch.fnmatchcase(str(r.get(k, '')), v) + for v in vs) + for k, vs in undefines): + continue # append a result results_.append(Result( @@ -2125,6 +2130,7 @@ class Rev(co.namedtuple('Rev', 'a')): def fold(Result, results, *, by=None, defines=[], + undefines=[], sort=None, depth=1, **_): @@ -2136,20 +2142,27 @@ def fold(Result, results, *, if by is None: by = Result._by - for k in it.chain(by or [], (k for k, _ in defines)): + for k in it.chain(by or [], + (k for k, _ in defines), + (k for k, _ in undefines)): if k not in Result._by and k not in Result._fields: print("error: could not find field %r?" % k, file=sys.stderr) sys.exit(-1) # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + if not all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ # organize results into conflicts @@ -2920,6 +2933,7 @@ def main(csv_paths, *, by=None, fields=None, defines=[], + undefines=[], sort=None, depth=None, children=None, @@ -3039,6 +3053,7 @@ def main(csv_paths, *, if not any(k == k_ for (k_, _), _ in (by or [])) and not any(k == k_ for (k_, _), _ in (fields or [])) and not any(k == k_ for k_, _ in defines) + and not any(k == k_ for k_, _ in undefines) and not any(k == k_ for (k_, _), _ in (sort or [])) and not any(k == k_ for (k_, _), _ in (hot or [])) and k != z @@ -3054,6 +3069,7 @@ def main(csv_paths, *, if not any(k == k_ for (k_, _), _ in (by or [])) and not any(k == k_ for (k_, _), _ in (fields or [])) and not any(k == k_ for k_, _ in defines) + and not any(k == k_ for k_, _ in undefines) and not any(k == k_ for (k_, _), _ in (sort or [])) and not any(k == k_ for (k_, _), _ in (hot or [])) and k != z @@ -3093,6 +3109,7 @@ def main(csv_paths, *, # homogenize results = homogenize(Result, results, defines=defines, + undefines=undefines, depth=depth) # fold @@ -3128,6 +3145,7 @@ def main(csv_paths, *, # homogenize diff_results = homogenize(Result, diff_results, defines=defines, + undefines=undefines, depth=depth) # fold @@ -3319,6 +3337,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/ctx.py b/scripts/ctx.py index 7aa0a095..24176883 100755 --- a/scripts/ctx.py +++ b/scripts/ctx.py @@ -745,6 +745,7 @@ class Rev(co.namedtuple('Rev', 'a')): def fold(Result, results, *, by=None, defines=[], + undefines=[], sort=None, depth=1, **_): @@ -756,20 +757,27 @@ def fold(Result, results, *, if by is None: by = Result._by - for k in it.chain(by or [], (k for k, _ in defines)): + for k in it.chain(by or [], + (k for k, _ in defines), + (k for k, _ in undefines)): if k not in Result._by and k not in Result._fields: print("error: could not find field %r?" % k, file=sys.stderr) sys.exit(-1) # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + if not all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ # organize results into conflicts @@ -1307,6 +1315,7 @@ def main(obj_paths, *, by=None, fields=None, defines=[], + undefines=[], sort=None, depth=None, hot=None, @@ -1356,6 +1365,7 @@ def main(obj_paths, *, results = fold(CtxResult, results, by=by, defines=defines, + undefines=undefines, sort=sort, depth=depth) @@ -1381,6 +1391,7 @@ def main(obj_paths, *, diff_results = fold(CtxResult, diff_results, by=by, defines=defines, + undefines=undefines, depth=depth) # hotify? @@ -1483,6 +1494,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/data.py b/scripts/data.py index 004c9c54..af312985 100755 --- a/scripts/data.py +++ b/scripts/data.py @@ -529,6 +529,7 @@ class Rev(co.namedtuple('Rev', 'a')): def fold(Result, results, *, by=None, defines=[], + undefines=[], sort=None, depth=1, **_): @@ -540,20 +541,27 @@ def fold(Result, results, *, if by is None: by = Result._by - for k in it.chain(by or [], (k for k, _ in defines)): + for k in it.chain(by or [], + (k for k, _ in defines), + (k for k, _ in undefines)): if k not in Result._by and k not in Result._fields: print("error: could not find field %r?" % k, file=sys.stderr) sys.exit(-1) # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + if not all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ # organize results into conflicts @@ -1048,6 +1056,7 @@ def main(obj_paths, *, by=None, fields=None, defines=[], + undefines=[], sort=None, **args): # figure out what fields we're interested in @@ -1080,6 +1089,7 @@ def main(obj_paths, *, results = fold(DataResult, results, by=by, defines=defines, + undefines=undefines, sort=sort) # find previous results? @@ -1096,7 +1106,8 @@ def main(obj_paths, *, # fold diff_results = fold(DataResult, diff_results, by=by, - defines=defines) + defines=defines, + undefines=undefines) # write results to JSON if args.get('output_json'): @@ -1188,6 +1199,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/perf.py b/scripts/perf.py index 3aad1be5..b29aa525 100755 --- a/scripts/perf.py +++ b/scripts/perf.py @@ -844,6 +844,7 @@ class Rev(co.namedtuple('Rev', 'a')): def fold(Result, results, *, by=None, defines=[], + undefines=[], sort=None, depth=1, **_): @@ -855,20 +856,27 @@ def fold(Result, results, *, if by is None: by = Result._by - for k in it.chain(by or [], (k for k, _ in defines)): + for k in it.chain(by or [], + (k for k, _ in defines), + (k for k, _ in undefines)): if k not in Result._by and k not in Result._fields: print("error: could not find field %r?" % k, file=sys.stderr) sys.exit(-1) # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + if not all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ # organize results into conflicts @@ -1505,6 +1513,7 @@ def main_(perf_paths, *, by=None, fields=None, defines=[], + undefines=[], sort=None, branches=False, caches=False, @@ -1574,6 +1583,7 @@ def main_(perf_paths, *, results = fold(PerfResult, results, by=by, defines=defines, + undefines=undefines, sort=sort, depth=depth) @@ -1599,6 +1609,7 @@ def main_(perf_paths, *, diff_results = fold(PerfResult, diff_results, by=by, defines=defines, + undefines=undefines, depth=depth) # hotify? @@ -1727,6 +1738,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/perfbd.py b/scripts/perfbd.py index 3a835cd7..1da80ca4 100755 --- a/scripts/perfbd.py +++ b/scripts/perfbd.py @@ -818,6 +818,7 @@ class Rev(co.namedtuple('Rev', 'a')): def fold(Result, results, *, by=None, defines=[], + undefines=[], sort=None, depth=1, **_): @@ -829,20 +830,27 @@ def fold(Result, results, *, if by is None: by = Result._by - for k in it.chain(by or [], (k for k, _ in defines)): + for k in it.chain(by or [], + (k for k, _ in defines), + (k for k, _ in undefines)): if k not in Result._by and k not in Result._fields: print("error: could not find field %r?" % k, file=sys.stderr) sys.exit(-1) # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + if not all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ # organize results into conflicts @@ -1493,6 +1501,7 @@ def main_(paths, *, by=None, fields=None, defines=[], + undefines=[], sort=None, depth=None, hot=None, @@ -1570,6 +1579,7 @@ def main_(paths, *, results = fold(PerfBdResult, results, by=by, defines=defines, + undefines=undefines, sort=sort, depth=depth) @@ -1595,6 +1605,7 @@ def main_(paths, *, diff_results = fold(PerfBdResult, diff_results, by=by, defines=defines, + undefines=undefines, depth=depth) # hotify? @@ -1728,6 +1739,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/plot.py b/scripts/plot.py index c871a777..25bb6add 100755 --- a/scripts/plot.py +++ b/scripts/plot.py @@ -356,7 +356,9 @@ class Rev(co.namedtuple('Rev', 'a')): def __ge__(self, other): return self.a <= other.a -def collect(csv_paths, defines=[]): +def collect(csv_paths, *, + defines=[], + undefines=[]): # collect results from CSV files fields = [] results = [] @@ -373,22 +375,34 @@ def collect(csv_paths, defines=[]): for v in vs) for k, vs in defines): continue + if any(any(fnmatch.fnmatchcase(r.get(k, ''), v) + for v in vs) + for k, vs in undefines): + continue results.append(r) + except FileNotFoundError: pass return fields, results -def fold(results, by=None, x=None, y=None, defines=[]): +def fold(results, by=None, x=None, y=None, *, + defines=[], + undefines=[]): # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(r.get(k, ''), v) + if not all(any(fnmatch.fnmatchcase(r.get(k, ''), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(r.get(k, ''), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ if by: @@ -1232,7 +1246,8 @@ def main_(ring, csv_paths, *, by=None, x=None, y=None, - define=[], + defines=[], + undefines=[], sort=None, labels=[], chars=[], @@ -1330,10 +1345,17 @@ def main_(ring, csv_paths, *, all_x = (x or []) + subplots_get('x', **subplot, subplots=subplots) all_y = (y or []) + subplots_get('y', **subplot, subplots=subplots) all_defines = co.defaultdict(lambda: set()) - for k, vs in it.chain(define or [], - subplots_get('define', **subplot, subplots=subplots)): + for k, vs in it.chain( + defines, + subplots_get('defines', **subplot, subplots=subplots)): all_defines[k] |= vs all_defines = sorted(all_defines.items()) + all_undefines = co.defaultdict(lambda: set()) + for k, vs in it.chain( + undefines, + subplots_get('undefines', **subplot, subplots=subplots)): + all_undefines[k] |= vs + all_undefines = sorted(all_undefines.items()) if not all_by and not all_y: print("error: needs --by or -y to figure out fields", @@ -1423,14 +1445,17 @@ def main_(ring, csv_paths, *, ## our main drawing logic # first collect results from CSV files - fields_, results = collect(csv_paths) + fields_, results = collect(csv_paths, + defines=defines, + undefines=undefines) # if y not specified, guess it's anything not in by/defines/x all_y_ = all_y if not all_y: all_y_ = [k for k in fields_ if k not in all_by - and not any(k == k_ for k_, _ in all_defines)] + and not any(k == k_ for k_, _ in all_defines) + and not any(k == k_ for k_, _ in all_undefines)] # then extract the requested datasets # @@ -1603,7 +1628,8 @@ def main_(ring, csv_paths, *, # allow subplot params to override global params x_ = set((x or []) + s.args.get('x', [])) y_ = set((y or []) + s.args.get('y', [])) - define_ = define + s.args.get('define', []) + defines_ = defines + s.args.get('defines', []) + undefines_ = undefines + s.args.get('undefines', []) xlim_ = s.args.get('xlim', xlim) ylim_ = s.args.get('ylim', ylim) xlim_stddev_ = s.args.get('xlim_stddev', xlim_stddev) @@ -1630,7 +1656,9 @@ def main_(ring, csv_paths, *, # data can be constrained by subplot-specific defines, # so re-extract for each plot subdatasets, subdataattrs = fold( - results, all_by, all_x, all_y_, define_) + results, all_by, all_x, all_y_, + defines=defines_, + undefines=undefines_) # order by labels subdatasets = co.OrderedDict(sorted( @@ -1991,14 +2019,26 @@ if __name__ == "__main__": help="Field to use for the y-axis.") parser.add_argument( '-D', '--define', + dest='defines', + action='append', type=lambda x: ( lambda k, vs: ( k.strip(), {v.strip() for v in vs.split(',')}) )(*x.split('=', 1)), - action='append', help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/plotmpl.py b/scripts/plotmpl.py index 8a181cf2..abc6c03c 100755 --- a/scripts/plotmpl.py +++ b/scripts/plotmpl.py @@ -240,7 +240,9 @@ class Rev(co.namedtuple('Rev', 'a')): def __ge__(self, other): return self.a <= other.a -def collect(csv_paths, defines=[]): +def collect(csv_paths, *, + defines=[], + undefines=[]): # collect results from CSV files fields = [] results = [] @@ -257,22 +259,34 @@ def collect(csv_paths, defines=[]): for v in vs) for k, vs in defines): continue + if any(any(fnmatch.fnmatchcase(r.get(k, ''), v) + for v in vs) + for k, vs in undefines): + continue results.append(r) + except FileNotFoundError: pass return fields, results -def fold(results, by=None, x=None, y=None, defines=[]): +def fold(results, by=None, x=None, y=None, *, + defines=[], + undefines=[]): # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(r.get(k, ''), v) + if not all(any(fnmatch.fnmatchcase(r.get(k, ''), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(r.get(k, ''), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ if by: @@ -826,7 +840,8 @@ def main(csv_paths, output, *, by=None, x=None, y=None, - define=[], + defines=[], + undefines=[], sort=None, labels=[], colors=[], @@ -978,10 +993,17 @@ def main(csv_paths, output, *, all_x = (x or []) + subplots_get('x', **subplot, subplots=subplots) all_y = (y or []) + subplots_get('y', **subplot, subplots=subplots) all_defines = co.defaultdict(lambda: set()) - for k, vs in it.chain(define or [], - subplots_get('define', **subplot, subplots=subplots)): + for k, vs in it.chain( + defines, + subplots_get('defines', **subplot, subplots=subplots)): all_defines[k] |= vs all_defines = sorted(all_defines.items()) + all_undefines = co.defaultdict(lambda: set()) + for k, vs in it.chain( + undefines, + subplots_get('undefines', **subplot, subplots=subplots)): + all_undefines[k] |= vs + all_undefines = sorted(all_undefines.items()) if not all_by and not all_y: print("error: needs --by or -y to figure out fields", @@ -989,13 +1011,16 @@ def main(csv_paths, output, *, sys.exit(-1) # first collect results from CSV files - fields_, results = collect(csv_paths) + fields_, results = collect(csv_paths, + defines=defines, + undefines=undefines) # if y not specified, guess it's anything not in by/defines/x if not all_y: all_y = [k for k in fields_ if k not in all_by - and not any(k == k_ for k_, _ in all_defines)] + and not any(k == k_ for k_, _ in all_defines) + and not any(k == k_ for k_, _ in all_undefines)] # then extract the requested datasets # @@ -1065,7 +1090,8 @@ def main(csv_paths, output, *, # allow subplot params to override global params x_ = set((x or []) + s.args.get('x', [])) y_ = set((y or []) + s.args.get('y', [])) - define_ = define + s.args.get('define', []) + defines_ = defines + s.args.get('defines', []) + undefines_ = undefines + s.args.get('undefines', []) xlim_ = s.args.get('xlim', xlim) ylim_ = s.args.get('ylim', ylim) xlim_stddev_ = s.args.get('xlim_stddev', xlim_stddev) @@ -1105,7 +1131,9 @@ def main(csv_paths, output, *, # data can be constrained by subplot-specific defines, # so re-extract for each plot subdatasets, subdataattrs = fold( - results, all_by, all_x, all_y, define_) + results, all_by, all_x, all_y, + defines=defines_, + undefines=undefines_) # order by labels subdatasets = co.OrderedDict(sorted( @@ -1447,14 +1475,26 @@ if __name__ == "__main__": help="Field to use for the y-axis.") parser.add_argument( '-D', '--define', + dest='defines', + action='append', type=lambda x: ( lambda k, vs: ( k.strip(), {v.strip() for v in vs.split(',')}) )(*x.split('=', 1)), - action='append', help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/stack.py b/scripts/stack.py index 0f621f4a..2ce8f2bb 100755 --- a/scripts/stack.py +++ b/scripts/stack.py @@ -486,6 +486,7 @@ class Rev(co.namedtuple('Rev', 'a')): def fold(Result, results, *, by=None, defines=[], + undefines=[], sort=None, depth=1, **_): @@ -497,20 +498,27 @@ def fold(Result, results, *, if by is None: by = Result._by - for k in it.chain(by or [], (k for k, _ in defines)): + for k in it.chain(by or [], + (k for k, _ in defines), + (k for k, _ in undefines)): if k not in Result._by and k not in Result._fields: print("error: could not find field %r?" % k, file=sys.stderr) sys.exit(-1) # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + if not all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ # organize results into conflicts @@ -1048,6 +1056,7 @@ def main(ci_paths, by=None, fields=None, defines=[], + undefines=[], sort=None, depth=None, hot=None, @@ -1094,6 +1103,7 @@ def main(ci_paths, results = fold(StackResult, results, by=by, defines=defines, + undefines=undefines, sort=sort, depth=depth) @@ -1119,6 +1129,7 @@ def main(ci_paths, diff_results = fold(StackResult, diff_results, by=by, defines=defines, + undefines=undefines, depth=depth) # hotify? @@ -1226,6 +1237,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/structs.py b/scripts/structs.py index 3019318c..585fc188 100755 --- a/scripts/structs.py +++ b/scripts/structs.py @@ -634,6 +634,7 @@ class Rev(co.namedtuple('Rev', 'a')): def fold(Result, results, *, by=None, defines=[], + undefines=[], sort=None, depth=1, **_): @@ -645,20 +646,27 @@ def fold(Result, results, *, if by is None: by = Result._by - for k in it.chain(by or [], (k for k, _ in defines)): + for k in it.chain(by or [], + (k for k, _ in defines), + (k for k, _ in undefines)): if k not in Result._by and k not in Result._fields: print("error: could not find field %r?" % k, file=sys.stderr) sys.exit(-1) # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + if not all(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(str(getattr(r, k, '')), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ # organize results into conflicts @@ -1196,6 +1204,7 @@ def main(obj_paths, *, by=None, fields=None, defines=[], + undefines=[], sort=None, depth=None, hot=None, @@ -1245,6 +1254,7 @@ def main(obj_paths, *, results = fold(StructResult, results, by=by, defines=defines, + undefines=undefines, sort=sort, depth=depth) @@ -1270,6 +1280,7 @@ def main(obj_paths, *, diff_results = fold(StructResult, diff_results, by=by, defines=defines, + undefines=undefines, depth=depth) # hotify? @@ -1372,6 +1383,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") class AppendSort(argparse.Action): def __call__(self, parser, namespace, value, option): if namespace.sort is None: diff --git a/scripts/treemap.py b/scripts/treemap.py index 20e63162..aee3bdce 100755 --- a/scripts/treemap.py +++ b/scripts/treemap.py @@ -262,7 +262,9 @@ def dat(x, *args): else: raise -def collect(csv_paths, defines=[]): +def collect(csv_paths, *, + defines=[], + undefines=[]): # collect results from CSV files fields = [] results = [] @@ -279,22 +281,34 @@ def collect(csv_paths, defines=[]): for v in vs) for k, vs in defines): continue + if any(any(fnmatch.fnmatchcase(r.get(k, ''), v) + for v in vs) + for k, vs in undefines): + continue results.append(r) + except FileNotFoundError: pass return fields, results -def fold(results, by=None, fields=None, defines=[]): +def fold(results, by=None, fields=None, *, + defines=[], + undefines=[]): # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(r.get(k, ''), v) + if not all(any(fnmatch.fnmatchcase(r.get(k, ''), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(r.get(k, ''), v) + for v in vs) + for k, vs in undefines): + continue + results_.append(r) results = results_ if by: @@ -1017,6 +1031,7 @@ def main_(ring, csv_paths, *, by=None, fields=None, defines=[], + undefines=[], labels=[], chars=[], colors=[], @@ -1087,7 +1102,9 @@ def main_(ring, csv_paths, *, height_ = max(0, shutil.get_terminal_size((80, 5))[1] + height) # first collect results from CSV files - fields_, results = collect(csv_paths, defines) + fields_, results = collect(csv_paths, + defines=defines, + undefines=undefines) if not by and not fields: print("error: needs --by or --fields to figure out fields", @@ -1098,16 +1115,20 @@ def main_(ring, csv_paths, *, if not by: by = [k for k in fields_ if k not in (fields or []) - and not any(k == k_ for k_, _ in defines)] + and not any(k == k_ for k_, _ in defines) + and not any(k == k_ for k_, _ in undefines)] # if fields not specified, guess it's anything not in by/defines if not fields: fields = [k for k in fields_ if k not in (by or []) - and not any(k == k_ for k_, _ in defines)] + and not any(k == k_ for k_, _ in defines) + and not any(k == k_ for k_, _ in undefines)] # then extract the requested dataset - datasets, dataattrs = fold(results, by, fields, defines) + datasets, dataattrs = fold(results, by, fields, + defines=defines, + undefines=undefines) # build tile heirarchy children = [] @@ -1421,6 +1442,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") parser.add_argument( '-L', '--add-label', dest='labels', diff --git a/scripts/treemapsvg.py b/scripts/treemapsvg.py index 296c4790..4245823b 100755 --- a/scripts/treemapsvg.py +++ b/scripts/treemapsvg.py @@ -123,7 +123,9 @@ def dat(x, *args): else: raise -def collect(csv_paths, defines=[]): +def collect(csv_paths, *, + defines=[], + undefines=[]): # collect results from CSV files fields = [] results = [] @@ -140,22 +142,34 @@ def collect(csv_paths, defines=[]): for v in vs) for k, vs in defines): continue + if any(any(fnmatch.fnmatchcase(r.get(k, ''), v) + for v in vs) + for k, vs in undefines): + continue results.append(r) + except FileNotFoundError: pass return fields, results -def fold(results, by=None, fields=None, defines=[]): +def fold(results, by=None, fields=None, *, + defines=[], + undefines=[]): # filter by matching defines - if defines: + if defines or undefines: results_ = [] for r in results: - if all(any(fnmatch.fnmatchcase(r.get(k, ''), v) + if not all(any(fnmatch.fnmatchcase(r.get(k, ''), v) for v in vs) for k, vs in defines): - results_.append(r) + continue + if any(any(fnmatch.fnmatchcase(r.get(k, ''), v) + for v in vs) + for k, vs in defines): + continue + results_.append(r) results = results_ if by: @@ -708,6 +722,7 @@ def main(csv_paths, output, *, by=None, fields=None, defines=[], + undefines=[], labels=[], colors=[], width=None, @@ -761,7 +776,9 @@ def main(csv_paths, output, *, height_ = HEIGHT # first collect results from CSV files - fields_, results = collect(csv_paths, defines) + fields_, results = collect(csv_paths, + defines=defines, + undefines=undefines) if not by and not fields: print("error: needs --by or --fields to figure out fields", @@ -772,16 +789,20 @@ def main(csv_paths, output, *, if not by: by = [k for k in fields_ if k not in (fields or []) - and not any(k == k_ for k_, _ in defines)] + and not any(k == k_ for k_, _ in defines) + and not any(k == k_ for k_, _ in undefines)] # if fields not specified, guess it's anything not in by/labels/defines if not fields: fields = [k for k in fields_ if k not in (by or []) - and not any(k == k_ for k_, _ in defines)] + and not any(k == k_ for k_, _ in defines) + and not any(k == k_ for k_, _ in undefines)] # then extract the requested dataset - datasets, dataattrs = fold(results, by, fields, defines) + datasets, dataattrs = fold(results, by, fields, + defines=defines, + undefines=undefines) # build tile heirarchy children = [] @@ -1117,6 +1138,17 @@ if __name__ == "__main__": )(*x.split('=', 1)), help="Only include results where this field is this value. May " "include comma-separated options and globs.") + parser.add_argument( + '-U', '--undefine', + dest='undefines', + action='append', + type=lambda x: ( + lambda k, vs: ( + k.strip(), + {v.strip() for v in vs.split(',')}) + )(*x.split('=', 1)), + help="Don't include results where this field is this value. May " + "include comma-separated options and globs.") parser.add_argument( '-L', '--add-label', dest='labels',