scripts: Fully connected graph in stack.py, no more recursive folding

This makes -D/--define more useful in stack.py/perf.py/perfbd.py by no
longer hiding undfined children entries.

For example:

  $ ./scripts/stack.py lfs.ci lfs_util.ci -Dfunction=lfsr_mount -t
  function                       frame    limit
  lfsr_mount                        96     2816
  |-> lfsr_fs_gc                    80     2720
  |-> lfsr_mtree_gc                176     2640
  |-> lfsr_mdir_commit             576     2464
  ... snip ...

Now shows all functions in the hot path of lfsr_mount, where before it
would only show functions in the hot path of lfsr_mount that were also
_named_ lfsr_mount.

The previous behavior was technically not wrong... but not very useful
(and confusing).

---

This was actually quite a bit annoying to get working because of the
possibility of function call cycles.

I ended up turning stack.py's result type into a fully connected graph,
which only works because Python has a cycle detector. (Actually this
script is so short-lived we probably wouldn't care if this leaked
memory.)

A nice side effect of this is now all the recursive scripts (stack.py,
perf.py, and perfbd.py) share the same internal result representation
and recursive printing logic, which is probably a good thing.
This commit is contained in:
Christopher Haster
2024-11-04 15:39:50 -06:00
parent 711cebfcf3
commit 0c3868f92c
3 changed files with 93 additions and 88 deletions
+8 -16
View File
@@ -657,15 +657,6 @@ def fold(Result, results, by=None, defines=[]):
for name, rs in folding.items():
folded.append(sum(rs[1:], start=rs[0]))
# fold recursively
folded_ = []
for r in folded:
folded_.append(r._replace(children=fold(
Result, r.children,
by=by,
defines=defines)))
folded = folded_
return folded
def table(Result, results, diff_results=None, *,
@@ -838,7 +829,8 @@ def table(Result, results, diff_results=None, *,
depth_ = 2
elif m.isinf(depth_):
def rec_depth(results_, seen=set()):
# rebuild our tables at each layer
# build the children table at each layer
results_ = fold(Result, results_, by=by)
table_ = {
','.join(str(getattr(r, k) or '') for k in by): r
for r in results_}
@@ -871,7 +863,8 @@ def table(Result, results, diff_results=None, *,
if hot:
def recurse(results_, depth_, seen=set(),
prefixes=('', '', '', '')):
# rebuild our tables at each layer
# build the children table at each layer
results_ = fold(Result, results_, by=by)
table_ = {
','.join(str(getattr(r, k) or '') for k in by): r
for r in results_}
@@ -929,14 +922,14 @@ def table(Result, results, diff_results=None, *,
else:
def recurse(results_, depth_, seen=set(),
prefixes=('', '', '', '')):
# rebuild our tables at each layer
# build the children table at each layer
results_ = fold(Result, results_, by=by)
table_ = {
','.join(str(getattr(r, k) or '') for k in by): r
for r in results_}
names_ = list(table_.keys())
# sort again at each layer, keep in mind the numbers are
# changing as we descend
# sort the children layer
names_.sort()
if sort:
for k, reverse in reversed(sort):
@@ -984,8 +977,7 @@ def table(Result, results, diff_results=None, *,
prefixes[2+is_last] + "| ",
prefixes[2+is_last] + " "))
# we have enough going on with diffing to make the top layer
# a special case
# the top layer is a bit of a special case
for name, line in zip(names, lines[1:-1]):
print('%-*s %s' % (
widths[0], line[0][0],