From 61c51b699af21324113410a33b9d68af05a38258 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 11 Jul 2023 16:14:32 -0500 Subject: [PATCH] In scripts, adopted aggresive width-finding for unbounded recursion This makes it easier to read the output, at a cost of these scripts not terminating if the underlying call sctucture contains loops. Previously these scripts would not terminate, but at least output the call tree as they visit each function. This was hard to read, and wasn't really that useful? If you hit a case with infinite recursion, you can limit the output size explicitly with -Z. Note this also drops --tree in stack.py. Since we get more readable output, this flag is less useful. This simplifies the script a bit. --- scripts/perf.py | 33 +++++++++++++++++----- scripts/perfbd.py | 33 +++++++++++++++++----- scripts/stack.py | 71 +++++++++++++++++++++++++---------------------- 3 files changed, 90 insertions(+), 47 deletions(-) diff --git a/scripts/perf.py b/scripts/perf.py index 2ee006c0..60b82693 100755 --- a/scripts/perf.py +++ b/scripts/perf.py @@ -834,10 +834,31 @@ def table(Result, results, diff_results=None, *, it.chain([23], it.repeat(7)), range(len(lines[0])-1))] - # adjust the name width based on the expected call depth, though - # note this doesn't really work with unbounded recursion - if not summary and not m.isinf(depth): - widths[0] += 4*(depth-1) + # adjust the name width based on the call depth + if not summary: + depth_ = depth + if m.isinf(depth_): + # find the actual depth, this may not terminate! in which + # case it's up to the user to provide an explicit depth + def rec_depth(results_): + # rebuild our tables at each layer + table_ = { + ','.join(str(getattr(r, k) or '') for k in by): r + for r in results_} + names_ = list(table_.keys()) + + return max(( + rec_depth(table_[name].children) + for name in names_), + default=-1) + 1 + + depth_ = max(( + rec_depth(table[name].children) + for name in names + if name in table), + default=-1) + 1 + + widths[0] += 4*max(depth_-1, 0) # print the tree recursively print('%-*s %s%s' % ( @@ -874,9 +895,7 @@ def table(Result, results, diff_results=None, *, print('%s%-*s %s' % ( prefixes[0+is_last], - widths[0] - ( - len(prefixes[0+is_last]) - if not m.isinf(depth) else 0), + widths[0] - len(prefixes[0+is_last]), name, ' '.join('%*s' % (w, x) for w, x in zip( diff --git a/scripts/perfbd.py b/scripts/perfbd.py index bf57f601..b34fa586 100755 --- a/scripts/perfbd.py +++ b/scripts/perfbd.py @@ -800,10 +800,31 @@ def table(Result, results, diff_results=None, *, it.chain([23], it.repeat(7)), range(len(lines[0])-1))] - # adjust the name width based on the expected call depth, though - # note this doesn't really work with unbounded recursion - if not summary and not m.isinf(depth): - widths[0] += 4*(depth-1) + # adjust the name width based on the call depth + if not summary: + depth_ = depth + if m.isinf(depth_): + # find the actual depth, this may not terminate! in which + # case it's up to the user to provide an explicit depth + def rec_depth(results_): + # rebuild our tables at each layer + table_ = { + ','.join(str(getattr(r, k) or '') for k in by): r + for r in results_} + names_ = list(table_.keys()) + + return max(( + rec_depth(table_[name].children) + for name in names_), + default=-1) + 1 + + depth_ = max(( + rec_depth(table[name].children) + for name in names + if name in table), + default=-1) + 1 + + widths[0] += 4*max(depth_-1, 0) # print the tree recursively print('%-*s %s%s' % ( @@ -840,9 +861,7 @@ def table(Result, results, diff_results=None, *, print('%s%-*s %s' % ( prefixes[0+is_last], - widths[0] - ( - len(prefixes[0+is_last]) - if not m.isinf(depth) else 0), + widths[0] - len(prefixes[0+is_last]), name, ' '.join('%*s' % (w, x) for w, x in zip( diff --git a/scripts/stack.py b/scripts/stack.py index ba954333..ab92623c 100755 --- a/scripts/stack.py +++ b/scripts/stack.py @@ -315,7 +315,6 @@ def table(Result, results, diff_results=None, *, summary=False, all=False, percent=False, - tree=False, depth=1, **_): all_, all = all, __builtins__.all @@ -472,18 +471,34 @@ def table(Result, results, diff_results=None, *, it.chain([23], it.repeat(7)), range(len(lines[0])-1))] - # adjust the name width based on the expected call depth, though - # note this doesn't really work with unbounded recursion - if not summary and not m.isinf(depth): - widths[0] += 4*(depth-1) + # adjust the name width based on the call depth + if not summary: + depth_ = depth + if m.isinf(depth_): + # find the actual depth, this may not terminate! in which + # case it's up to the user to provide an explicit depth + def rec_depth(names_): + depth_ = -1 + for name in names_: + if name in table: + children = { + ','.join(str(getattr(Result(*c), k) or '') + for k in by) + for c in table[name].children} + depth_ = max(depth_, + rec_depth([n for n in names if n in children])) + return depth_ + 1 + + depth_ = rec_depth(names) + + widths[0] += 4*max(depth_-1, 0) # print the tree recursively - if not tree: - print('%-*s %s%s' % ( - widths[0], lines[0][0], - ' '.join('%*s' % (w, x) - for w, x in zip(widths[1:], lines[0][1:-1])), - lines[0][-1])) + print('%-*s %s%s' % ( + widths[0], lines[0][0], + ' '.join('%*s' % (w, x) + for w, x in zip(widths[1:], lines[0][1:-1])), + lines[0][-1])) if not summary: line_table = {n: l for n, l in zip(names, lines[1:-1])} @@ -497,17 +512,14 @@ def table(Result, results, diff_results=None, *, print('%s%-*s ' % ( prefixes[0+is_last], - widths[0] - ( - len(prefixes[0+is_last]) - if not m.isinf(depth) else 0), + widths[0] - len(prefixes[0+is_last]), line[0]), end='') - if not tree: - print(' %s%s' % ( - ' '.join('%*s' % (w, x) - for w, x in zip(widths[1:], line[1:-1])), - line[-1]), - end='') + print(' %s%s' % ( + ' '.join('%*s' % (w, x) + for w, x in zip(widths[1:], line[1:-1])), + line[-1]), + end='') print() # recurse? @@ -526,12 +538,11 @@ def table(Result, results, diff_results=None, *, recurse(names, depth) - if not tree: - print('%-*s %s%s' % ( - widths[0], lines[-1][0], - ' '.join('%*s' % (w, x) - for w, x in zip(widths[1:], lines[-1][1:-1])), - lines[-1][-1])) + print('%-*s %s%s' % ( + widths[0], lines[-1][0], + ' '.join('%*s' % (w, x) + for w, x in zip(widths[1:], lines[-1][1:-1])), + lines[-1][-1])) def main(ci_paths, @@ -540,10 +551,8 @@ def main(ci_paths, defines=None, sort=None, **args): - # it doesn't really make sense to not have a depth with tree, - # so assume depth=inf if tree by default if args.get('depth') is None: - args['depth'] = m.inf if args['tree'] else 1 + args['depth'] = 1 elif args.get('depth') == 0: args['depth'] = m.inf @@ -715,10 +724,6 @@ if __name__ == "__main__": '--everything', action='store_true', help="Include builtin and libc specific symbols.") - parser.add_argument( - '--tree', - action='store_true', - help="Only show the function call tree.") parser.add_argument( '-Z', '--depth', nargs='?',