Reimplemented tree rendering in dbg*.py scripts

The goal here was to add the option to show the combined rbyd trees in
dbgbtree.py/dbgmtree.py.

This was quite tricky, (and not really helped by the hackiness of these
scripts), but was made a bit easier by adding a general purpose tree renderer
that can render a precomputed set of branches into the tag output.

For example, a 2-deep rendering of a simple btree with a block size of
1KiB, where you can see a bit of the emergent data-structure:

  $ ./scripts/dbgbtree.py disk -B1024 0x223 -t -Z2 -i
  btree 0x223.90, rev 46, weight 1024
  rbyd                       ids       tag                     ...
  0223.0090:     .-+             0-199 btree w200 9            ...
  00cb.0048:     | |     .->      0-39 btree w40 7             ...
                 | | .---+->     40-79 btree w40 7             ...
                 | | | .--->    80-119 btree w40 7             ...
                 | | | | .->   120-159 btree w40 7             ...
                 | '-+-+-+->   160-199 btree w40 7             ...
  0223.0090: .---+-+           200-399 btree w200 9            ...
  013e.004b: |     |     .->   200-239 btree w40 7             ...
             |     | .---+->   240-279 btree w40 8             ...
             |     | | .--->   280-319 btree w40 8             ...
             |     | | | .->   320-359 btree w40 8             ...
             |     '-+-+-+->   360-399 btree w40 8             ...
  0223.0090: | .---+           400-599 btree w200 9            ...
  01a7.004c: | |   |     .->   400-439 btree w40 8             ...
             | |   | .---+->   440-479 btree w40 8             ...
             | |   | | .--->   480-519 btree w40 8             ...
             | |   | | | .->   520-559 btree w40 8             ...
             | |   '-+-+-+->   560-599 btree w40 8             ...
  0223.0090: | | .-+           600-799 btree w200 9            ...
  021e.004c: | | | |     .->   600-639 btree w40 8             ...
             | | | | .---+->   640-679 btree w40 8             ...
             | | | | | .--->   680-719 btree w40 8             ...
             | | | | | | .->   720-759 btree w40 8             ...
             | | | '-+-+-+->   760-799 btree w40 8             ...
  0223.0090: +-+-+-+          800-1023 btree w224 10           ...
  021f.0298:       |     .->   800-839 btree w40 8             ...
                   |   .-+->   840-879 btree w40 8             ...
                   |   | .->   880-919 btree w40 8             ...
                   '---+-+->  920-1023 btree w104 9            ...

This tree renderer also replaces the adhoc tree rendere in dbgrbyd.py
for consistency.
This commit is contained in:
Christopher Haster
2023-05-20 13:12:45 -05:00
parent b67fcb0ee5
commit 9b803f9625
2 changed files with 536 additions and 291 deletions

View File

@@ -552,32 +552,32 @@ def dbg_tree(data, block_size, rev, trunk, weight, *,
upper -= upper-lower-1-w if not alt & 0x4 else 0
j = j - jump
if args.get('tree'):
# figure out which color
if alt & 0x2:
_, nalt, _, _, _ = fromtag(data[j+jump+d:])
if nalt & 0x2:
path.append((j+jump, j, True, 'y'))
else:
path.append((j+jump, j, True, 'r'))
# figure out which color
if alt & 0x2:
_, nalt, _, _, _ = fromtag(data[j+jump+d:])
if nalt & 0x2:
path.append((j+jump, j, True, 'y'))
else:
path.append((j+jump, j, True, 'b'))
path.append((j+jump, j, True, 'r'))
else:
path.append((j+jump, j, True, 'b'))
# stay on path
else:
lower += w if not alt & 0x4 else 0
upper -= w if alt & 0x4 else 0
j = j + d
if args.get('tree'):
# figure out which color
if alt & 0x2:
_, nalt, _, _, _ = fromtag(data[j:])
if nalt & 0x2:
path.append((j-d, j, False, 'y'))
else:
path.append((j-d, j, False, 'r'))
# figure out which color
if alt & 0x2:
_, nalt, _, _, _ = fromtag(data[j:])
if nalt & 0x2:
path.append((j-d, j, False, 'y'))
else:
path.append((j-d, j, False, 'b'))
path.append((j-d, j, False, 'r'))
else:
path.append((j-d, j, False, 'b'))
# found tag
else:
id_ = upper-1
@@ -589,7 +589,7 @@ def dbg_tree(data, block_size, rev, trunk, weight, *,
return done, id_, tag_, w_, j, d, jump, path
# precompute tree
tree_width = 0
t_width = 0
if args.get('tree'):
trunks = co.defaultdict(lambda: (-1, 0))
alts = co.defaultdict(lambda: {})
@@ -630,100 +630,110 @@ def dbg_tree(data, block_size, rev, trunk, weight, *,
# didn't exist
def rec_trunk(j_):
if j_ not in alts:
return j_
return trunks[j_]
else:
if 't' not in alts[j_]:
alts[j_]['t'] = rec_trunk(alts[j_]['nf'])
return alts[j_]['t']
if 'nft' not in alts[j_]:
alts[j_]['nft'] = rec_trunk(alts[j_]['nf'])
return alts[j_]['nft']
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']
alt['ft'] = alts[alt['f']]['nft']
else:
alt['ft'] = alt['f']
alt['ft'] = trunks[alt['f']]
def rec_depth(j_):
def rec_height(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']
if 'h' not in alts[j_]:
alts[j_]['h'] = max(
rec_height(alts[j_]['f']),
rec_height(alts[j_]['nf'])) + 1
return alts[j_]['h']
for j_ in alts.keys():
rec_depth(j_)
rec_height(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
t_depth = max((alt['h']+1 for alt in alts.values()), default=0)
def treerepr(j):
if tree_depth == 0:
# convert to more general tree representation
tree = []
for j, alt in alts.items():
# note all non-trunk edges should be black
tree.append({
'a': alt['nft'],
'b': alt['nft'],
'd': t_depth-1 - alt['h'],
'c': alt['c'],
})
tree.append({
'a': alt['nft'],
'b': alt['ft'],
'd': t_depth-1 - alt['h'],
'c': 'b',
})
# find the max depth from the tree
t_depth = max((branch['d']+1 for branch in tree), default=0)
if t_depth > 0:
t_width = 2*t_depth + 2
def treerepr(id, tag):
if t_depth == 0:
return ''
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 '')
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
def branchrepr(x, d, was):
for branch in tree:
if branch['d'] == d and branch['b'] == x:
if any(branch['d'] == d and branch['a'] == x
for branch in tree):
return '+-', branch['c'], branch['c']
elif any(branch['d'] == d
and x > min(branch['a'], branch['b'])
and x < max(branch['a'], branch['b'])
for branch in tree):
return '|-', branch['c'], branch['c']
elif branch['a'] < branch['b']:
return '\'-', branch['c'], branch['c']
else:
return '.-', branch['c'], branch['c']
for branch in tree:
if branch['d'] == d and branch['a'] == x:
return '+ ', branch['c'], None
for branch in tree:
if (branch['d'] == d
and x > min(branch['a'], branch['b'])
and x < max(branch['a'], branch['b'])):
return '| ', branch['c'], was
if was:
return '--', was, was
return ' ', None, None
trunk = []
was = None
for x in reversed(range(1, tree_depth+1)):
t, c, was = altrepr(j, x, was)
for d in range(t_depth):
t, c, was = branchrepr((id, tag), d, was)
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 '\x1b[90m' if color and c == 'b'
else '',
t,
('>' if was else ' ')
if x == 1 else '',
'\x1b[m' if color else ''))
('>' if was else ' ') if d == t_depth-1 else '',
'\x1b[m' if color and c else ''))
return ' %s' % ''.join(trunk)
return '%s ' % ''.join(trunk)
# print header
w_width = 2*m.ceil(m.log10(max(1, weight)+1))+1
print('%-8s %*s%-*s %-22s %s' % (
'off',
tree_width, '',
t_width, '',
w_width, 'ids',
'tag',
'data (truncated)'
@@ -737,9 +747,9 @@ def dbg_tree(data, block_size, rev, trunk, weight, *,
break
# show human-readable tag representation
print('%08x:%s %-57s' % (
print('%08x: %s%-57s' % (
j,
treerepr(j) if args.get('tree') else '',
treerepr(id, tag) if args.get('tree') else '',
'%*s %-22s%s' % (
w_width, '%d-%d' % (id-(w-1), id)
if w > 1 else id
@@ -754,7 +764,7 @@ def dbg_tree(data, block_size, rev, trunk, weight, *,
if args.get('device'):
print('%8s %*s%*s %s' % (
'',
tree_width, '',
t_width, '',
w_width, '',
'%-22s%s' % (
'%04x %08x %07x' % (tag, w, size),
@@ -770,7 +780,7 @@ def dbg_tree(data, block_size, rev, trunk, weight, *,
for o, line in enumerate(xxd(data[j:j+d])):
print('%8s: %*s%*s %s' % (
'%04x' % (j + o*16),
tree_width, '',
t_width, '',
w_width, '',
line))
if args.get('raw') or args.get('no_truncate'):
@@ -778,7 +788,7 @@ def dbg_tree(data, block_size, rev, trunk, weight, *,
for o, line in enumerate(xxd(data[j+d:j+d+size])):
print('%8s: %*s%*s %s' % (
'%04x' % (j+d + o*16),
tree_width, '',
t_width, '',
w_width, '',
line))