From 2142b4a09dc2a512bcb3501cc4dfdaa3767773e3 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 6 Apr 2023 03:14:46 -0500 Subject: [PATCH] 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 --- scripts/dbgbtree.py | 9 +- scripts/dbgrbyd.py | 196 ++++++++++++++++++++++++++------------------ 2 files changed, 121 insertions(+), 84 deletions(-) diff --git a/scripts/dbgbtree.py b/scripts/dbgbtree.py index 5efc92ee..82b7eade 100755 --- a/scripts/dbgbtree.py +++ b/scripts/dbgbtree.py @@ -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_branches = [(0, trunk.weight)] - def t_repr(id, w, leaf=True, depth=None): + def treerepr(id, w, leaf=True, depth=None): branches_ = [] for i in range(len(t_branches)): 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_), '\x1b[m' if color else '') + # print header w_width = 2*m.ceil(m.log10(max(1, trunk.weight)+1))+1 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) if prbyd is None or rbyd != prbyd 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 else id if w > 0 else '', @@ -463,7 +464,7 @@ def main(disk, block_size=None, trunk=0, limit=None, *, '%04x.%04x:' % (rbyd.block, rbyd.limit) if prbyd is None or rbyd != prbyd 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 else '%d-%d' % (id-(w-1), id) if w > 1 else id if w > 0 @@ -576,7 +577,7 @@ def main(disk, block_size=None, trunk=0, limit=None, *, if not rbyd: print('%04x.%04x: %s%s%s%s' % ( 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 '', '(corrupted rbyd 0x%x.%x)' % (rbyd.block, rbyd.limit), '\x1b[m' if color else '')) diff --git a/scripts/dbgrbyd.py b/scripts/dbgrbyd.py index 6f2d8a6e..721ad1e3 100755 --- a/scripts/dbgrbyd.py +++ b/scripts/dbgrbyd.py @@ -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