Reworked dbgrbyd.py's tree renderer to make more sense
While the previous renderer was "technically correct", the attempt to map rotated alts to their nearest neighbor just made the resulting tree an unreadable mess. Now the renderer prunes alts with unreachable edges (like they would be during lfsr_rbyd_append). And aligns all alts with their destination trunk. This results in a much more readable, if slightly less accurate, rendering of the tree. Example: $ ./scripts/dbgrbyd.py -B4096 disk 0 -t rbyd 0x0, rev 1, size 1508, weight 40 off ids tag data (truncated) 0000032a: .-+-> 0 reg w1 1 73 s 00000026: | '-> 1-5 reg w5 1 62 b 00000259: .-------+---> 6-11 reg w6 1 6f o 00000224: | .-+-+-> 12-17 reg w6 1 6e n 0000028e: | | | '-> 18 reg w1 1 70 p 00000076: | | '---> 19-20 reg w2 1 64 d 0000038f: | | .-> 21-22 reg w2 1 75 u 0000041d: | .---+---+-> 23 reg w1 1 78 x 000001f3: | | .-> 24-27 reg w4 1 6d m 00000486: | | .-----+-> 28-29 reg w2 1 7a z 000004f3: | | | .-----> 30-31 reg w2 1 62 b 000004ba: | | | | .---> 32-35 reg w4 1 61 a 0000058d: | | | | | .-> 36-37 reg w2 1 65 e 000005c6: +-+-+-+-+-+-> 38-39 reg w2 1 66 f
This commit is contained in:
+116
-80
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import bisect
|
||||
import collections as co
|
||||
import itertools as it
|
||||
import math as m
|
||||
import os
|
||||
@@ -364,6 +365,7 @@ def show_log(block_size, data, rev, off, weight, *,
|
||||
for r, c in zip(reprs, colors)),
|
||||
lifetime_width - sum(len(r) for r in reprs), '')
|
||||
|
||||
|
||||
# print header
|
||||
w_width = 2*m.ceil(m.log10(max(1, weight)+1))+1
|
||||
print('%-8s %*s%-*s %-22s %s' % (
|
||||
@@ -516,11 +518,11 @@ def show_tree(block_size, data, rev, trunk, weight, *,
|
||||
if alt & 0x2:
|
||||
_, nalt, _, _, _ = fromtag(data[j+jump+delta:])
|
||||
if nalt & 0x2:
|
||||
path.append((j+jump, j, 'y'))
|
||||
path.append((j+jump, j, True, 'y'))
|
||||
else:
|
||||
path.append((j+jump, j, 'r'))
|
||||
path.append((j+jump, j, True, 'r'))
|
||||
else:
|
||||
path.append((j+jump, j, 'b'))
|
||||
path.append((j+jump, j, True, 'b'))
|
||||
# stay on path
|
||||
else:
|
||||
lower += w if not alt & 0x4 else 0
|
||||
@@ -532,11 +534,11 @@ def show_tree(block_size, data, rev, trunk, weight, *,
|
||||
if alt & 0x2:
|
||||
_, nalt, _, _, _ = fromtag(data[j:])
|
||||
if nalt & 0x2:
|
||||
path.append((j-delta, j, 'y'))
|
||||
path.append((j-delta, j, False, 'y'))
|
||||
else:
|
||||
path.append((j-delta, j, 'r'))
|
||||
path.append((j-delta, j, False, 'r'))
|
||||
else:
|
||||
path.append((j-delta, j, 'b'))
|
||||
path.append((j-delta, j, False, 'b'))
|
||||
# found tag
|
||||
else:
|
||||
tag_ = alt
|
||||
@@ -550,8 +552,8 @@ def show_tree(block_size, data, rev, trunk, weight, *,
|
||||
# precompute tree
|
||||
tree_width = 0
|
||||
if args.get('tree'):
|
||||
tags = []
|
||||
paths = {}
|
||||
trunks = co.defaultdict(lambda: (-1, 0))
|
||||
alts = co.defaultdict(lambda: {})
|
||||
|
||||
tag, id = 0, -1
|
||||
while True:
|
||||
@@ -560,89 +562,123 @@ def show_tree(block_size, data, rev, trunk, weight, *,
|
||||
if done:
|
||||
break
|
||||
|
||||
tags.append((j, tag, id))
|
||||
for x, (a, b, c) in enumerate(path):
|
||||
paths[a, b, x] = c
|
||||
# keep track of trunks/alts
|
||||
trunks[j] = (id, tag)
|
||||
|
||||
# align paths to nearest tag
|
||||
tags.sort()
|
||||
paths = {(
|
||||
tags[bisect.bisect_left(tags, (a, 0, -1), hi=len(tags)-1)],
|
||||
tags[bisect.bisect_left(tags, (b, 0, -1), hi=len(tags)-1)],
|
||||
x): c for (a, b, x), c in paths.items()}
|
||||
for j_, j__, followed, c in path:
|
||||
if followed:
|
||||
alts[j_] |= {'f': j__, 'c': c}
|
||||
else:
|
||||
alts[j_] |= {'nf': j__, 'c': c}
|
||||
|
||||
# also find the maximum depth
|
||||
depth = max((x+1 for _, _, x in paths.keys()), default=0)
|
||||
if depth > 0:
|
||||
tree_width = 2*depth + 2
|
||||
# prune any alts with unreachable edges
|
||||
pruned = {}
|
||||
for j_, alt in alts.items():
|
||||
if 'f' not in alt:
|
||||
pruned[j_] = alt['nf']
|
||||
elif 'nf' not in alt:
|
||||
pruned[j_] = alt['f']
|
||||
for j_ in pruned.keys():
|
||||
del alts[j_]
|
||||
|
||||
for j_, alt in alts.items():
|
||||
while alt['f'] in pruned:
|
||||
alt['f'] = pruned[alt['f']]
|
||||
while alt['nf'] in pruned:
|
||||
alt['nf'] = pruned[alt['nf']]
|
||||
|
||||
# find the trunk and depth of each alt, assuming pruned alts
|
||||
# didn't exist
|
||||
def rec_trunk(j_):
|
||||
if j_ not in alts:
|
||||
return j_
|
||||
else:
|
||||
if 't' not in alts[j_]:
|
||||
alts[j_]['t'] = rec_trunk(alts[j_]['nf'])
|
||||
return alts[j_]['t']
|
||||
|
||||
for j_ in alts.keys():
|
||||
rec_trunk(j_)
|
||||
for j_, alt in alts.items():
|
||||
if alt['f'] in alts:
|
||||
alt['ft'] = alts[alt['f']]['t']
|
||||
else:
|
||||
alt['ft'] = alt['f']
|
||||
|
||||
def rec_depth(j_):
|
||||
if j_ not in alts:
|
||||
return 0
|
||||
else:
|
||||
if 'd' not in alts[j_]:
|
||||
alts[j_]['d'] = max(
|
||||
rec_depth(alts[j_]['f']),
|
||||
rec_depth(alts[j_]['nf'])) + 1
|
||||
return alts[j_]['d']
|
||||
|
||||
for j_ in alts.keys():
|
||||
rec_depth(j_)
|
||||
|
||||
# oh hey this also gives us the max depth
|
||||
tree_depth = max((alt['d'] for alt in alts.values()), default=0)
|
||||
if tree_depth > 0:
|
||||
tree_width = 2*tree_depth + 2
|
||||
|
||||
def treerepr(j):
|
||||
if depth == 0:
|
||||
if tree_depth == 0:
|
||||
return ''
|
||||
|
||||
_, tag, id = tags[bisect.bisect_left(
|
||||
tags, (j, 0, -1), hi=len(tags)-1)]
|
||||
def c(s, c):
|
||||
return '%s%s%s' % (
|
||||
'\x1b[33m' if color and c == 'y'
|
||||
else '\x1b[31m' if color and c == 'r'
|
||||
else '\x1b[90m' if color
|
||||
else '',
|
||||
s,
|
||||
'\x1b[m' if color else '')
|
||||
|
||||
def c_start(c):
|
||||
return ('\x1b[33m' if color and c == 'y'
|
||||
else '\x1b[31m' if color and c == 'r'
|
||||
else '\x1b[90m' if color
|
||||
else '')
|
||||
trunk = []
|
||||
def altrepr(j, x, was=None):
|
||||
# note all non-trunk edges should be black
|
||||
for alt in alts.values():
|
||||
if alt['d'] == x and alt['t'] == j:
|
||||
return '+-', alt['c'], alt['c']
|
||||
for alt in alts.values():
|
||||
if (alt['d'] == x
|
||||
and alt['ft'] == j
|
||||
and trunks[j] <= trunks[alt['t']]):
|
||||
return '.-', 'b', 'b'
|
||||
for alt in alts.values():
|
||||
if (alt['d'] == x
|
||||
and alt['ft'] == j
|
||||
and trunks[j] >= trunks[alt['t']]):
|
||||
return '\'-', 'b', 'b'
|
||||
for alt in alts.values():
|
||||
if (alt['d'] == x
|
||||
and trunks[j] >= min(
|
||||
trunks[alt['t']], trunks[alt['ft']])
|
||||
and trunks[j] <= max(
|
||||
trunks[alt['t']], trunks[alt['ft']])):
|
||||
return '| ', 'b', was
|
||||
if was:
|
||||
return '--', was, was
|
||||
return ' ', None, None
|
||||
|
||||
def c_stop(c):
|
||||
return '\x1b[m' if color else ''
|
||||
was = None
|
||||
for x in reversed(range(1, tree_depth+1)):
|
||||
t, c, was = altrepr(j, x, was)
|
||||
|
||||
path = []
|
||||
seen = None
|
||||
for x in range(depth):
|
||||
if any(x == x_ and tag == a_tag and id == a_id
|
||||
for (_, a_tag, a_id), _, x_ in paths.keys()):
|
||||
c = next(c
|
||||
for ((_, a_tag, a_id), _, x_), c in paths.items()
|
||||
if x == x_ and tag == a_tag and id == a_id)
|
||||
path.append('%s+%s' % (c_start(c), c_stop(c)))
|
||||
elif any(x == x_ and tag == b_tag and id == b_id
|
||||
for _, (_, b_tag, b_id), x_ in paths.keys()):
|
||||
a_tag, a_id, c = next((a_tag, a_id, c)
|
||||
for ((_, a_tag, a_id), (_, b_tag, b_id), x_), c
|
||||
in paths.items()
|
||||
if x == x_ and tag == b_tag and id == b_id)
|
||||
if (a_id, a_tag) < (id, tag):
|
||||
path.append('%s\'%s' % (c_start(c), c_stop(c)))
|
||||
else:
|
||||
path.append('%s.%s' % (c_start(c), c_stop(c)))
|
||||
elif any(x == x_
|
||||
and (id, tag) >= min((a_id, a_tag), (b_id, b_tag))
|
||||
and (id, tag) <= max((a_id, a_tag), (b_id, b_tag))
|
||||
for (_, a_tag, a_id), (_, b_tag, b_id), x_
|
||||
in paths.keys()):
|
||||
c = next(c
|
||||
for ((_, a_tag, a_id), (_, b_tag, b_id), x_), c
|
||||
in paths.items()
|
||||
if x == x_
|
||||
and (id, tag) >= min((a_id, a_tag), (b_id, b_tag))
|
||||
and (id, tag) <= max((a_id, a_tag), (b_id, b_tag)))
|
||||
path.append('%s|%s' % (c_start(c), c_stop(c)))
|
||||
elif seen:
|
||||
path.append('%s-%s' % (c_start(seen), c_stop(seen)))
|
||||
else:
|
||||
path.append(' ')
|
||||
trunk.append('%s%s%s%s' % (
|
||||
'\x1b[33m' if color and c == 'y'
|
||||
else '\x1b[31m' if color and c == 'r'
|
||||
else '\x1b[90m' if color
|
||||
else '',
|
||||
t,
|
||||
('>' if was else ' ')
|
||||
if x == 1 else '',
|
||||
'\x1b[m' if color else ''))
|
||||
|
||||
if any(x == x_ and tag == b_tag and id == b_id
|
||||
for _, (_, b_tag, b_id), x_ in paths.keys()):
|
||||
c = next(c
|
||||
for (_, (_, b_tag, b_id), x_), c in paths.items()
|
||||
if x == x_ and tag == b_tag and id == b_id)
|
||||
seen = c
|
||||
return ' %s' % ''.join(trunk)
|
||||
|
||||
if seen and x == depth-1:
|
||||
path.append('%s->%s' % (c_start(seen), c_stop(seen)))
|
||||
elif seen:
|
||||
path.append('%s-%s' % (c_start(seen), c_stop(seen)))
|
||||
else:
|
||||
path.append(' ')
|
||||
|
||||
return ' %s' % ''.join(path)
|
||||
|
||||
# print header
|
||||
w_width = 2*m.ceil(m.log10(max(1, weight)+1))+1
|
||||
|
||||
Reference in New Issue
Block a user