From 3e03c2ee7fe2b3c080d7203f46608fb098598e6f Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 10 Dec 2024 14:45:46 -0600 Subject: [PATCH] scripts: Adopted better input file handling in result scripts - Error on no/insufficient files. Instead of just returning no results. This is more useful when debugging complicated bash scripts. - Use elf magic to allow any file order in perfbd.py/stack.py. This was already implemented in stack.py, now also adopted in perfbd.py. Elf files always start with the magic string "\x7fELF", so we can use this to figure out the types of input files without needing to rely on argument order. This is just one less thing to worry about when invoking these scripts. --- scripts/code.py | 8 +++++++ scripts/cov.py | 8 +++++++ scripts/csv.py | 8 ++++++- scripts/ctx.py | 8 +++++++ scripts/data.py | 8 +++++++ scripts/perf.py | 8 +++++++ scripts/perfbd.py | 54 ++++++++++++++++++++++++++++++++++++++++------ scripts/stack.py | 18 ++++++++++++---- scripts/structs.py | 8 +++++++ 9 files changed, 117 insertions(+), 11 deletions(-) diff --git a/scripts/code.py b/scripts/code.py index be982d74..042866d4 100755 --- a/scripts/code.py +++ b/scripts/code.py @@ -1015,7 +1015,15 @@ def main(obj_paths, *, **args): # find sizes if not args.get('use', None): + # not enough info? + if not obj_paths: + print("error: no *.o files?", + file=sys.stderr) + sys.exit(1) + + # collect info results = collect(obj_paths, **args) + else: results = [] with openio(args['use']) as f: diff --git a/scripts/cov.py b/scripts/cov.py index 452d386d..0dfb2c55 100755 --- a/scripts/cov.py +++ b/scripts/cov.py @@ -833,7 +833,15 @@ def main(gcda_paths, *, # find sizes if not args.get('use', None): + # not enough info? + if not gcda_paths: + print("error: no *.gcda files?", + file=sys.stderr) + sys.exit(1) + + # collect info results = collect(gcda_paths, **args) + else: results = [] with openio(args['use']) as f: diff --git a/scripts/csv.py b/scripts/csv.py index 9e6e43ce..ed5c2ca2 100755 --- a/scripts/csv.py +++ b/scripts/csv.py @@ -1789,7 +1789,13 @@ def main(csv_paths, *, if args.get('use'): csv_paths = csv_paths + [args['use']] - # find CSV files + # not enough info? + if not csv_paths: + print("error: no *.csv files?", + file=sys.stderr) + sys.exit(1) + + # collect info fields_, results = collect(csv_paths, defines) # homogenize diff --git a/scripts/ctx.py b/scripts/ctx.py index 5953142c..35a2defd 100755 --- a/scripts/ctx.py +++ b/scripts/ctx.py @@ -1230,7 +1230,15 @@ def main(obj_paths, *, # find sizes if not args.get('use', None): + # not enough info? + if not obj_paths: + print("error: no *.o files?", + file=sys.stderr) + sys.exit(1) + + # collect info results = collect(obj_paths, **args) + else: results = [] with openio(args['use']) as f: diff --git a/scripts/data.py b/scripts/data.py index 4e823f2c..2d55ade4 100755 --- a/scripts/data.py +++ b/scripts/data.py @@ -1015,7 +1015,15 @@ def main(obj_paths, *, **args): # find sizes if not args.get('use', None): + # not enough info? + if not obj_paths: + print("error: no *.o files?", + file=sys.stderr) + sys.exit(1) + + # collect info results = collect(obj_paths, **args) + else: results = [] with openio(args['use']) as f: diff --git a/scripts/perf.py b/scripts/perf.py index d029316f..d2daeff1 100755 --- a/scripts/perf.py +++ b/scripts/perf.py @@ -1310,7 +1310,15 @@ def report(perf_paths, *, # find sizes if not args.get('use', None): + # not enough info? + if not perf_paths: + print("error: no *.perf files?", + file=sys.stderr) + sys.exit(1) + + # collect info results = collect(perf_paths, **args) + else: results = [] with openio(args['use']) as f: diff --git a/scripts/perfbd.py b/scripts/perfbd.py index cd02425b..751b4a3d 100755 --- a/scripts/perfbd.py +++ b/scripts/perfbd.py @@ -170,6 +170,11 @@ def openio(path, mode='r', buffering=-1): else: return open(path, mode, buffering) +def iself(path): + # check for an elf file's magic string (\x7fELF) + with open(path, 'rb') as f: + return f.read(4) == b'\x7fELF' + class Sym(co.namedtuple('Sym', [ 'name', 'global_', 'section', 'addr', 'size'])): __slots__ = () @@ -714,7 +719,7 @@ def starapply(args): f, args, kwargs = args return f(*args, **kwargs) -def collect(obj_path, trace_paths, *, +def collect(elf_path, trace_paths, *, jobs=None, **args): # automatic job detection? @@ -722,10 +727,10 @@ def collect(obj_path, trace_paths, *, jobs = len(os.sched_getaffinity(0)) # find sym/line info to reverse ASLR - syms = collect_syms(obj_path, + syms = collect_syms(elf_path, sections=['.text'], **args) - lines = collect_dwarf_lines(obj_path, **args) + lines = collect_dwarf_lines(elf_path, **args) if jobs is not None: # try to split up files so that even single files can be processed @@ -1265,7 +1270,7 @@ def annotate(Result, results, *, print(line) -def report(obj_path='', trace_paths=[], *, +def report(paths, *, by=None, fields=None, defines=[], @@ -1287,7 +1292,32 @@ def report(obj_path='', trace_paths=[], *, # find sizes if not args.get('use', None): - results = collect(obj_path, trace_paths, **args) + # figure out paths + elf_paths = [] + trace_paths = [] + for path in paths: + if iself(path): + elf_paths.append(path) + else: + trace_paths.append(path) + # not enough info? + if not elf_paths: + print("error: no elf file?", + file=sys.stderr) + sys.exit(1) + if not trace_paths: + print("error: no *.trace files?", + file=sys.stderr) + sys.exit(1) + # too much info? + if len(elf_paths) != 1: + print("error: multiple elf files?", + file=sys.stderr) + sys.exit(1) + + # collect info + results = collect(elf_paths[0], trace_paths, **args) + else: results = [] with openio(args['use']) as f: @@ -1400,13 +1430,25 @@ if __name__ == "__main__": description="Aggregate and report call-stack propagated " "block-device operations from trace output.", allow_abbrev=False) + class AppendPath(argparse.Action): + def __call__(self, parser, namespace, value, option): + if getattr(namespace, 'paths', None) is None: + namespace.paths = [] + if value is None: + pass + elif isinstance(value, str): + namespace.paths.append(value) + else: + namespace.paths.extend(value) parser.add_argument( - 'obj_path', + 'elf_path', nargs='?', + action=AppendPath, help="Input executable for mapping addresses to symbols.") parser.add_argument( 'trace_paths', nargs='*', + action=AppendPath, help="Input *.trace files.") parser.add_argument( '-v', '--verbose', diff --git a/scripts/stack.py b/scripts/stack.py index 4a29fbf2..625971b6 100755 --- a/scripts/stack.py +++ b/scripts/stack.py @@ -1383,15 +1383,25 @@ if __name__ == "__main__": parser = argparse.ArgumentParser( description="Find stack usage at the function level.", allow_abbrev=False) + class AppendPath(argparse.Action): + def __call__(self, parser, namespace, value, option): + if getattr(namespace, 'paths', None) is None: + namespace.paths = [] + if value is None: + pass + elif isinstance(value, str): + namespace.paths.append(value) + else: + namespace.paths.extend(value) parser.add_argument( - 'paths', - metavar='obj_paths', + 'obj_paths', nargs='*', + action=AppendPath, help="Input *.o files.") parser.add_argument( - 'paths_', - metavar='ci_paths', + 'ci_paths', nargs='*', + action=AppendPath, help="Input *.ci files.") parser.add_argument( '-v', '--verbose', diff --git a/scripts/structs.py b/scripts/structs.py index c19fe2b7..32862622 100755 --- a/scripts/structs.py +++ b/scripts/structs.py @@ -1043,7 +1043,15 @@ def main(obj_paths, *, # find sizes if not args.get('use', None): + # not enough info? + if not obj_paths: + print("error: no *.o files?", + file=sys.stderr) + sys.exit(1) + + # collect info results = collect(obj_paths, **args) + else: results = [] with openio(args['use']) as f: