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:
Christopher Haster
2023-04-06 03:14:46 -05:00
parent 0ccf283321
commit 2142b4a09d
2 changed files with 121 additions and 84 deletions
+5 -4
View File
@@ -373,7 +373,7 @@ def main(disk, block_size=None, trunk=0, limit=None, *,
t_width = 2*t_depth+2 if t_depth > 0 else 0 t_width = 2*t_depth+2 if t_depth > 0 else 0
t_branches = [(0, trunk.weight)] t_branches = [(0, trunk.weight)]
def t_repr(id, w, leaf=True, depth=None): def treerepr(id, w, leaf=True, depth=None):
branches_ = [] branches_ = []
for i in range(len(t_branches)): for i in range(len(t_branches)):
if depth is not None and depth == i-1: if depth is not None and depth == i-1:
@@ -425,6 +425,7 @@ def main(disk, block_size=None, trunk=0, limit=None, *,
t_width, ''.join(branches_), t_width, ''.join(branches_),
'\x1b[m' if color else '') '\x1b[m' if color else '')
# print header # print header
w_width = 2*m.ceil(m.log10(max(1, trunk.weight)+1))+1 w_width = 2*m.ceil(m.log10(max(1, trunk.weight)+1))+1
print('%-9s %*s%-*s %-22s %s' % ( print('%-9s %*s%-*s %-22s %s' % (
@@ -450,7 +451,7 @@ def main(disk, block_size=None, trunk=0, limit=None, *,
'%04x.%04x:' % (rbyd.block, rbyd.limit) '%04x.%04x:' % (rbyd.block, rbyd.limit)
if prbyd is None or rbyd != prbyd if prbyd is None or rbyd != prbyd
else '', else '',
t_repr(id, w, True, depth) if args.get('tree') else '', treerepr(id, w, True, depth) if args.get('tree') else '',
w_width, '%d-%d' % (id-(w-1), id) if w > 1 w_width, '%d-%d' % (id-(w-1), id) if w > 1
else id if w > 0 else id if w > 0
else '', else '',
@@ -463,7 +464,7 @@ def main(disk, block_size=None, trunk=0, limit=None, *,
'%04x.%04x:' % (rbyd.block, rbyd.limit) '%04x.%04x:' % (rbyd.block, rbyd.limit)
if prbyd is None or rbyd != prbyd if prbyd is None or rbyd != prbyd
else '', else '',
t_repr(id, w, not name_tag, depth) if args.get('tree') else '', treerepr(id, w, not name_tag, depth) if args.get('tree') else '',
w_width, '' if name_tag w_width, '' if name_tag
else '%d-%d' % (id-(w-1), id) if w > 1 else '%d-%d' % (id-(w-1), id) if w > 1
else id if w > 0 else id if w > 0
@@ -576,7 +577,7 @@ def main(disk, block_size=None, trunk=0, limit=None, *,
if not rbyd: if not rbyd:
print('%04x.%04x: %s%s%s%s' % ( print('%04x.%04x: %s%s%s%s' % (
rbyd.block, rbyd.limit, rbyd.block, rbyd.limit,
t_repr(id, w) if args.get('tree') else '', treerepr(id, w) if args.get('tree') else '',
'\x1b[31m' if color else '', '\x1b[31m' if color else '',
'(corrupted rbyd 0x%x.%x)' % (rbyd.block, rbyd.limit), '(corrupted rbyd 0x%x.%x)' % (rbyd.block, rbyd.limit),
'\x1b[m' if color else '')) '\x1b[m' if color else ''))
+116 -80
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import bisect import bisect
import collections as co
import itertools as it import itertools as it
import math as m import math as m
import os import os
@@ -364,6 +365,7 @@ def show_log(block_size, data, rev, off, weight, *,
for r, c in zip(reprs, colors)), for r, c in zip(reprs, colors)),
lifetime_width - sum(len(r) for r in reprs), '') lifetime_width - sum(len(r) for r in reprs), '')
# print header # print header
w_width = 2*m.ceil(m.log10(max(1, weight)+1))+1 w_width = 2*m.ceil(m.log10(max(1, weight)+1))+1
print('%-8s %*s%-*s %-22s %s' % ( print('%-8s %*s%-*s %-22s %s' % (
@@ -516,11 +518,11 @@ def show_tree(block_size, data, rev, trunk, weight, *,
if alt & 0x2: if alt & 0x2:
_, nalt, _, _, _ = fromtag(data[j+jump+delta:]) _, nalt, _, _, _ = fromtag(data[j+jump+delta:])
if nalt & 0x2: if nalt & 0x2:
path.append((j+jump, j, 'y')) path.append((j+jump, j, True, 'y'))
else: else:
path.append((j+jump, j, 'r')) path.append((j+jump, j, True, 'r'))
else: else:
path.append((j+jump, j, 'b')) path.append((j+jump, j, True, 'b'))
# stay on path # stay on path
else: else:
lower += w if not alt & 0x4 else 0 lower += w if not alt & 0x4 else 0
@@ -532,11 +534,11 @@ def show_tree(block_size, data, rev, trunk, weight, *,
if alt & 0x2: if alt & 0x2:
_, nalt, _, _, _ = fromtag(data[j:]) _, nalt, _, _, _ = fromtag(data[j:])
if nalt & 0x2: if nalt & 0x2:
path.append((j-delta, j, 'y')) path.append((j-delta, j, False, 'y'))
else: else:
path.append((j-delta, j, 'r')) path.append((j-delta, j, False, 'r'))
else: else:
path.append((j-delta, j, 'b')) path.append((j-delta, j, False, 'b'))
# found tag # found tag
else: else:
tag_ = alt tag_ = alt
@@ -550,8 +552,8 @@ def show_tree(block_size, data, rev, trunk, weight, *,
# precompute tree # precompute tree
tree_width = 0 tree_width = 0
if args.get('tree'): if args.get('tree'):
tags = [] trunks = co.defaultdict(lambda: (-1, 0))
paths = {} alts = co.defaultdict(lambda: {})
tag, id = 0, -1 tag, id = 0, -1
while True: while True:
@@ -560,89 +562,123 @@ def show_tree(block_size, data, rev, trunk, weight, *,
if done: if done:
break break
tags.append((j, tag, id)) # keep track of trunks/alts
for x, (a, b, c) in enumerate(path): trunks[j] = (id, tag)
paths[a, b, x] = c
# align paths to nearest tag for j_, j__, followed, c in path:
tags.sort() if followed:
paths = {( alts[j_] |= {'f': j__, 'c': c}
tags[bisect.bisect_left(tags, (a, 0, -1), hi=len(tags)-1)], else:
tags[bisect.bisect_left(tags, (b, 0, -1), hi=len(tags)-1)], alts[j_] |= {'nf': j__, 'c': c}
x): c for (a, b, x), c in paths.items()}
# also find the maximum depth # prune any alts with unreachable edges
depth = max((x+1 for _, _, x in paths.keys()), default=0) pruned = {}
if depth > 0: for j_, alt in alts.items():
tree_width = 2*depth + 2 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): def treerepr(j):
if depth == 0: if tree_depth == 0:
return '' return ''
_, tag, id = tags[bisect.bisect_left( def c(s, c):
tags, (j, 0, -1), hi=len(tags)-1)] 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): trunk = []
return ('\x1b[33m' if color and c == 'y' def altrepr(j, x, was=None):
else '\x1b[31m' if color and c == 'r' # note all non-trunk edges should be black
else '\x1b[90m' if color for alt in alts.values():
else '') 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): was = None
return '\x1b[m' if color else '' for x in reversed(range(1, tree_depth+1)):
t, c, was = altrepr(j, x, was)
path = [] trunk.append('%s%s%s%s' % (
seen = None '\x1b[33m' if color and c == 'y'
for x in range(depth): else '\x1b[31m' if color and c == 'r'
if any(x == x_ and tag == a_tag and id == a_id else '\x1b[90m' if color
for (_, a_tag, a_id), _, x_ in paths.keys()): else '',
c = next(c t,
for ((_, a_tag, a_id), _, x_), c in paths.items() ('>' if was else ' ')
if x == x_ and tag == a_tag and id == a_id) if x == 1 else '',
path.append('%s+%s' % (c_start(c), c_stop(c))) '\x1b[m' if color else ''))
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(' ')
if any(x == x_ and tag == b_tag and id == b_id return ' %s' % ''.join(trunk)
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
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 # print header
w_width = 2*m.ceil(m.log10(max(1, weight)+1))+1 w_width = 2*m.ceil(m.log10(max(1, weight)+1))+1