forked from Imagelibrary/littlefs
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:
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import bisect
|
||||
import collections as co
|
||||
import itertools as it
|
||||
import math as m
|
||||
import os
|
||||
@@ -295,10 +296,11 @@ class Rbyd:
|
||||
|
||||
def lookup(self, id, tag):
|
||||
if not self:
|
||||
return True, 0, -1, 0, 0, 0, b''
|
||||
return True, 0, -1, 0, 0, 0, b'', []
|
||||
|
||||
lower = -1
|
||||
upper = self.weight
|
||||
path = []
|
||||
|
||||
# descend down tree
|
||||
j = self.trunk
|
||||
@@ -314,11 +316,33 @@ class Rbyd:
|
||||
lower += upper-lower-1-weight_ if alt & 0x4 else 0
|
||||
upper -= upper-lower-1-weight_ if not alt & 0x4 else 0
|
||||
j = j - jump
|
||||
|
||||
# figure out which color
|
||||
if alt & 0x2:
|
||||
_, nalt, _, _, _ = fromtag(self.data[j+jump+d:])
|
||||
if nalt & 0x2:
|
||||
path.append((j+jump, j, True, 'y'))
|
||||
else:
|
||||
path.append((j+jump, j, True, 'r'))
|
||||
else:
|
||||
path.append((j+jump, j, True, 'b'))
|
||||
|
||||
# stay on path
|
||||
else:
|
||||
lower += weight_ if not alt & 0x4 else 0
|
||||
upper -= weight_ if alt & 0x4 else 0
|
||||
j = j + d
|
||||
|
||||
# figure out which color
|
||||
if alt & 0x2:
|
||||
_, nalt, _, _, _ = fromtag(self.data[j:])
|
||||
if nalt & 0x2:
|
||||
path.append((j-d, j, False, 'y'))
|
||||
else:
|
||||
path.append((j-d, j, False, 'r'))
|
||||
else:
|
||||
path.append((j-d, j, False, 'b'))
|
||||
|
||||
# found tag
|
||||
else:
|
||||
id_ = upper-1
|
||||
@@ -327,8 +351,7 @@ class Rbyd:
|
||||
|
||||
done = (id_, tag_) < (id, tag) or tag_ & 2
|
||||
|
||||
return (done, id_, tag_, w_,
|
||||
j, d, self.data[j+d:j+d+jump])
|
||||
return done, id_, tag_, w_, j, d, self.data[j+d:j+d+jump], path
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.trunk)
|
||||
@@ -350,6 +373,95 @@ class Rbyd:
|
||||
|
||||
yield id, tag, w, j, d, data
|
||||
|
||||
# create tree representation for debugging
|
||||
def tree(self):
|
||||
trunks = co.defaultdict(lambda: (-1, 0))
|
||||
alts = co.defaultdict(lambda: {})
|
||||
|
||||
id, tag = -1, 0
|
||||
while True:
|
||||
done, id, tag, w, j, d, data, path = self.lookup(id, tag+0x10)
|
||||
# found end of tree?
|
||||
if done:
|
||||
break
|
||||
|
||||
# keep track of trunks/alts
|
||||
trunks[j] = (id, tag)
|
||||
|
||||
for j_, j__, followed, c in path:
|
||||
if followed:
|
||||
alts[j_] |= {'f': j__, 'c': c}
|
||||
else:
|
||||
alts[j_] |= {'nf': j__, 'c': c}
|
||||
|
||||
# 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 trunks[j_]
|
||||
else:
|
||||
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']]['nft']
|
||||
else:
|
||||
alt['ft'] = trunks[alt['f']]
|
||||
|
||||
def rec_height(j_):
|
||||
if j_ not in alts:
|
||||
return 0
|
||||
else:
|
||||
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_height(j_)
|
||||
|
||||
t_depth = max((alt['h']+1 for alt in alts.values()), default=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',
|
||||
})
|
||||
|
||||
return tree, t_depth
|
||||
|
||||
|
||||
def main(disk, roots=None, *,
|
||||
block_size=None,
|
||||
@@ -382,138 +494,300 @@ def main(disk, roots=None, *,
|
||||
btree.addr(), btree.rev, btree.weight))
|
||||
|
||||
# look up an id, while keeping track of the search path
|
||||
def lookup(id, depth=None):
|
||||
def btree_lookup(bid, depth=None):
|
||||
rbyd = btree
|
||||
rid = id
|
||||
rid = bid
|
||||
depth_ = 1
|
||||
path = []
|
||||
|
||||
# corrupted? return a corrupted block once
|
||||
if not rbyd:
|
||||
return (id > 0, id, 0, rbyd, -1,
|
||||
(0, 0, 0, b''),
|
||||
(0, 0, 0, b''),
|
||||
path)
|
||||
return bid > 0, bid, 0, rbyd, -1, [], path
|
||||
|
||||
while True:
|
||||
# first lookup id/name
|
||||
(done, rid_, name_tag, w,
|
||||
name_j, name_d, name) = rbyd.lookup(rid, 0)
|
||||
if done:
|
||||
return (True, id, 0, rbyd, -1,
|
||||
(0, 0, 0, b''),
|
||||
(0, 0, 0, b''),
|
||||
path)
|
||||
# collect all tags, normally you don't need to do this
|
||||
# but we are debugging here
|
||||
name = None
|
||||
tags = []
|
||||
branch = None
|
||||
rid_ = rid
|
||||
tag = 0
|
||||
w = 0
|
||||
for i in it.count():
|
||||
done, rid__, tag, w_, j, d, data, _ = rbyd.lookup(
|
||||
rid_, tag+0x10)
|
||||
if done or (i != 0 and rid__ != rid_):
|
||||
break
|
||||
|
||||
if name_tag & 0xf00f == TAG_NAME:
|
||||
# then lookup struct
|
||||
(done, _, struct_tag, _,
|
||||
struct_j, struct_d, struct_) = rbyd.lookup(
|
||||
rid_, TAG_STRUCT)
|
||||
if done:
|
||||
return (True, id, 0, rbyd, -1,
|
||||
(0, 0, 0, b''),
|
||||
(0, 0, 0, b''),
|
||||
path)
|
||||
else:
|
||||
tag = name_tag
|
||||
struct_tag, struct_j, struct_d, struct_ = (
|
||||
name_tag, name_j, name_d, name)
|
||||
name_tag, name_j, name_d, name = 0, 0, 0, b''
|
||||
# first tag indicates the branch's weight
|
||||
if i == 0:
|
||||
rid_, w = rid__, w_
|
||||
|
||||
path.append((id + (rid_-rid), w, rbyd, rid_,
|
||||
(name_tag, name_j, name_d, name),
|
||||
(struct_tag, struct_j, struct_d, struct_)))
|
||||
# catch any branches
|
||||
if tag == TAG_BTREE:
|
||||
branch = (tag, j, d, data)
|
||||
|
||||
# is it another branch? continue down tree
|
||||
if struct_tag == TAG_BTREE and (
|
||||
tags.append((tag, j, d, data))
|
||||
|
||||
# keep track of path
|
||||
path.append((bid + (rid_-rid), w, rbyd, rid_, tags))
|
||||
|
||||
# descend down branch?
|
||||
if branch is not None and (
|
||||
not depth or depth_ < depth):
|
||||
w, trunk, block, crc = frombtree(struct_)
|
||||
tag, j, d, data = branch
|
||||
w_, trunk, block, crc = frombtree(data)
|
||||
rbyd = Rbyd.fetch(f, block_size, block, trunk)
|
||||
|
||||
# corrupted? bail here so we can keep traversing the tree
|
||||
if not rbyd:
|
||||
return (False, id + (rid_-rid), w, rbyd, -1,
|
||||
(0, 0, 0, b''),
|
||||
(0, 0, 0, b''),
|
||||
path)
|
||||
return False, bid + (rid_-rid), w, rbyd, -1, [], path
|
||||
|
||||
rid -= (rid_-(w-1))
|
||||
depth_ += 1
|
||||
else:
|
||||
return (False, id + (rid_-rid), w, rbyd, rid_,
|
||||
(name_tag, name_j, name_d, name),
|
||||
(struct_tag, struct_j, struct_d, struct_),
|
||||
path)
|
||||
return not tags, bid + (rid_-rid), w, rbyd, rid_, tags, path
|
||||
|
||||
# if we're printing the tree, first find the max depth so we know how
|
||||
# much space to reserve
|
||||
# precompute rbyd-trees if requested
|
||||
t_width = 0
|
||||
if args.get('tree'):
|
||||
t_depth = 0
|
||||
id = -1
|
||||
# find the max depth of each layer to nicely align trees
|
||||
# TODO memoize
|
||||
bdepths = {}
|
||||
bid = -1
|
||||
while True:
|
||||
(done, id, w, rbyd, rid, _, _,
|
||||
path) = (lookup(id+1, depth=args.get('depth')))
|
||||
done, bid, w, rbyd, rid, tags, path = btree_lookup(
|
||||
bid+1, depth=args.get('depth'))
|
||||
if done:
|
||||
break
|
||||
|
||||
t_depth = max(t_depth, len(path))
|
||||
for d, (bid, w, rbyd, rid, tags) in enumerate(path):
|
||||
_, rdepth = rbyd.tree()
|
||||
bdepths[d] = max(bdepths.get(d, 0), rdepth)
|
||||
|
||||
t_width = 2*t_depth+2 if t_depth > 0 else 0
|
||||
t_branches = [(0, btree.weight)]
|
||||
# find all branches
|
||||
tree = []
|
||||
root = None
|
||||
branches = {}
|
||||
bid = -1
|
||||
while True:
|
||||
done, bid, w, rbyd, rid, tags, path = btree_lookup(
|
||||
bid+1, depth=args.get('depth'))
|
||||
if done:
|
||||
break
|
||||
|
||||
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:
|
||||
branches_.append('+' if leaf else '|')
|
||||
break
|
||||
d_ = 0
|
||||
leaf = None
|
||||
for d, (bid, w, rbyd, rid, tags) in enumerate(path):
|
||||
if not tags:
|
||||
continue
|
||||
|
||||
if i+1 < len(t_branches):
|
||||
if (leaf
|
||||
and id-(w-1) == t_branches[i+1][0]
|
||||
and t_branches[i][0] == t_branches[i+1][0]
|
||||
and (not args.get('inner')
|
||||
or (i == 0 and depth == 0))):
|
||||
branches_.append('+-')
|
||||
elif (leaf
|
||||
and id-(w-1) == t_branches[i+1][0]
|
||||
and t_branches[i][1] == t_branches[i+1][1]
|
||||
and (not args.get('inner') or depth == i)):
|
||||
branches_.append('\'-')
|
||||
elif (leaf
|
||||
and id-(w-1) == t_branches[i+1][0]
|
||||
and (not args.get('inner') or depth == i)):
|
||||
branches_.append('|-')
|
||||
elif (id-(w-1) >= t_branches[i][0]
|
||||
and id-(w-1) < t_branches[i][1]
|
||||
and t_branches[i][1] != t_branches[i+1][1]):
|
||||
branches_.append('| ')
|
||||
# map rbyd tree into B-tree space
|
||||
rtree, rdepth = rbyd.tree()
|
||||
|
||||
# note we adjust our bid/rids to be left-leaning,
|
||||
# this allows a global order and make tree rendering quite
|
||||
# a bit easier
|
||||
for i in range(len(rtree)):
|
||||
a_rid, a_tag = rtree[i]['a']
|
||||
b_rid, b_tag = rtree[i]['b']
|
||||
_, _, _, a_w, _, _, _, _ = rbyd.lookup(a_rid, 0)
|
||||
_, _, _, b_w, _, _, _, _ = rbyd.lookup(b_rid, 0)
|
||||
rtree[i] = {
|
||||
'a': (a_rid-(a_w-1), a_tag),
|
||||
'b': (b_rid-(b_w-1), b_tag),
|
||||
'd': rtree[i]['d'],
|
||||
'c': rtree[i]['c'],
|
||||
}
|
||||
|
||||
# connect our branch to the rbyd's root
|
||||
if leaf is not None:
|
||||
root = min(rtree,
|
||||
key=lambda branch: branch['d'],
|
||||
default=None)
|
||||
|
||||
if root is not None:
|
||||
r_rid, r_tag = root['a']
|
||||
else:
|
||||
branches_.append(' ')
|
||||
else:
|
||||
if (leaf
|
||||
and id-(w-1) == t_branches[i][0]
|
||||
and (not args.get('inner') or i == 0)):
|
||||
branches_.append('+-%s> ' % ('-'*2*(t_depth-i-1)))
|
||||
elif (leaf
|
||||
and id == t_branches[i][1]-1):
|
||||
branches_.append('\'-%s> ' % ('-'*2*(t_depth-i-1)))
|
||||
elif (leaf
|
||||
and id >= t_branches[i][0]
|
||||
and id-(w-1) < t_branches[i][1]):
|
||||
branches_.append('|-%s> ' % ('-'*2*(t_depth-i-1)))
|
||||
elif (id >= t_branches[i][0]
|
||||
and id < t_branches[i][1]-1):
|
||||
branches_.append('|')
|
||||
else:
|
||||
branches_.append(' ')
|
||||
r_rid, r_tag = rid-(w-1), tags[0][0]
|
||||
tree.append({
|
||||
'a': leaf,
|
||||
'b': (bid-rid+r_rid, d, r_rid, r_tag),
|
||||
'd': d_-1,
|
||||
'c': 'b',
|
||||
})
|
||||
|
||||
return '%s%-*s%s' % (
|
||||
'\x1b[90m' if color else '',
|
||||
t_width, ''.join(branches_),
|
||||
'\x1b[m' if color else '')
|
||||
for branch in rtree:
|
||||
# map rbyd branches into our btree space
|
||||
a_rid, a_tag = branch['a']
|
||||
b_rid, b_tag = branch['b']
|
||||
tree.append({
|
||||
'a': (bid-rid+a_rid, d, a_rid, a_tag),
|
||||
'b': (bid-rid+b_rid, d, b_rid, b_tag),
|
||||
'd': branch['d'] + d_ + bdepths.get(d, 0)-rdepth,
|
||||
'c': branch['c'],
|
||||
})
|
||||
|
||||
d_ += max(rdepth, 1)
|
||||
leaf = (bid-(w-1), d, rid-(w-1), TAG_BTREE)
|
||||
|
||||
# remap branches to leaves if we aren't showing inner branches
|
||||
if not args.get('inner'):
|
||||
# step through each layer backwards
|
||||
b_depth = max((branch['a'][1]+1 for branch in tree), default=0)
|
||||
|
||||
# keep track of the original tree to find the original bids,
|
||||
# unfortunately because we store the bids in the branches we
|
||||
# overwrite these
|
||||
tree_ = tree.copy()
|
||||
|
||||
for bd in reversed(range(b_depth-1)):
|
||||
# find leaf-roots at this level
|
||||
roots = {}
|
||||
for branch_, branch in zip(tree_, tree):
|
||||
bid = branch['b'][0] - branch['b'][2]
|
||||
# choose the highest node as the root
|
||||
if (branch_['b'][1] == b_depth-1
|
||||
and (bid not in roots
|
||||
or branch_['d'] < roots[bid]['d'])):
|
||||
roots[bid] = branch_
|
||||
|
||||
# remap branches to leaf-roots
|
||||
tree__ = []
|
||||
for branch_ in tree_:
|
||||
if branch_['a'][1] == bd and branch_['a'][0] in roots:
|
||||
branch_ = {
|
||||
'a': roots[branch_['a'][0]]['b'],
|
||||
'b': branch_['b'],
|
||||
'd': branch_['d'],
|
||||
'c': branch_['c'],
|
||||
}
|
||||
if branch_['b'][1] == bd and branch_['b'][0] in roots:
|
||||
branch_ = {
|
||||
'a': branch_['a'],
|
||||
'b': roots[branch_['b'][0]]['b'],
|
||||
'd': branch_['d'],
|
||||
'c': branch_['c'],
|
||||
}
|
||||
tree__.append(branch_)
|
||||
tree_ = tree__
|
||||
tree = tree_
|
||||
|
||||
# precompute B-trees if requested
|
||||
elif args.get('btree'):
|
||||
# find all branches
|
||||
tree = []
|
||||
root = None
|
||||
branches = {}
|
||||
leaves = {}
|
||||
bid = -1
|
||||
while True:
|
||||
done, bid, w, rbyd, rid, tags, path = btree_lookup(
|
||||
bid+1, depth=args.get('depth'))
|
||||
if done:
|
||||
break
|
||||
|
||||
# if we're not showing inner nodes, prefer names higher in
|
||||
# the tree since this avoids showing vestigial names
|
||||
name = None
|
||||
if not args.get('inner'):
|
||||
name = None
|
||||
for bid_, w_, rbyd_, rid_, tags_ in reversed(path):
|
||||
for tag_, j_, d_, data_ in tags_:
|
||||
if tag_ & 0xf00f == TAG_NAME:
|
||||
name = (tag_, j_, d_, data_)
|
||||
|
||||
if rid_-(w_-1) != 0:
|
||||
break
|
||||
|
||||
a = root
|
||||
for d, (bid, w, rbyd, rid, tags) in enumerate(path):
|
||||
if not tags:
|
||||
continue
|
||||
|
||||
b = (bid-(w-1), d, rid-(w-1),
|
||||
(name if name else tags[0])[0])
|
||||
|
||||
# remap branches to leaves if we aren't showing
|
||||
# inner branches
|
||||
if not args.get('inner'):
|
||||
if b not in branches:
|
||||
bid, w, rbyd, rid, tags = path[-1]
|
||||
if not tags:
|
||||
continue
|
||||
branches[b] = (
|
||||
bid-(w-1), len(path)-1, rid-(w-1),
|
||||
(name if name else tags[0])[0])
|
||||
b = branches[b]
|
||||
|
||||
# found entry point?
|
||||
if root is None:
|
||||
root = b
|
||||
a = root
|
||||
|
||||
tree.append({
|
||||
'a': a,
|
||||
'b': b,
|
||||
'd': d,
|
||||
'c': 'b',
|
||||
})
|
||||
a = b
|
||||
|
||||
# common tree renderer
|
||||
if args.get('tree') or args.get('btree'):
|
||||
# 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(bid, w, bd, rid, tag):
|
||||
# print('%-32s' % repr((bid-(w-1), bd, rid, tag)), end='')
|
||||
if t_depth == 0:
|
||||
return ''
|
||||
|
||||
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 d in range(t_depth):
|
||||
t, c, was = branchrepr(
|
||||
(bid-(w-1), bd, rid-(w-1), 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 and c == 'b'
|
||||
else '',
|
||||
t,
|
||||
('>' if was else ' ') if d == t_depth-1 else '',
|
||||
'\x1b[m' if color and c else ''))
|
||||
|
||||
return '%s ' % ''.join(trunk)
|
||||
|
||||
|
||||
# print header
|
||||
@@ -527,140 +801,93 @@ def main(disk, roots=None, *,
|
||||
if not args.get('no_truncate') else ''))
|
||||
|
||||
# prbyd here means the last rendered rbyd, we update
|
||||
# in dbg_entry to always print interleaved addresses
|
||||
# in dbg_branch to always print interleaved addresses
|
||||
prbyd = None
|
||||
def dbg_entry(id, w, rbyd, rid,
|
||||
name_tag, name_j, name_d, name,
|
||||
struct_tag, struct_j, struct_d, struct_,
|
||||
depth=None):
|
||||
def dbg_branch(bid, w, rbyd, rid, tags, bd):
|
||||
nonlocal prbyd
|
||||
|
||||
# show human-readable representation
|
||||
if name_tag:
|
||||
for i, (tag, j, d, data) in enumerate(tags):
|
||||
print('%10s %s%*s %-22s %s' % (
|
||||
'%04x.%04x:' % (rbyd.block, rbyd.trunk)
|
||||
if prbyd is None or rbyd != prbyd
|
||||
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
|
||||
treerepr(bid, w, bd, rid, tag)
|
||||
if args.get('tree') or args.get('btree') else '',
|
||||
w_width, '' if i != 0
|
||||
else '%d-%d' % (bid-(w-1), bid) if w > 1
|
||||
else bid if w > 0
|
||||
else '',
|
||||
tagrepr(name_tag, w, len(name), None),
|
||||
tagrepr(tag, w if i == 0 else 0, len(data), None),
|
||||
# note we render names a bit different here
|
||||
''.join(
|
||||
b if b >= ' ' and b <= '~' else '.'
|
||||
for b in map(chr, name))))
|
||||
for b in map(chr, data))
|
||||
if tag & 0xf00f == TAG_NAME
|
||||
else next(xxd(data, 8), '')
|
||||
if not args.get('no_truncate')
|
||||
else ''))
|
||||
prbyd = rbyd
|
||||
print('%10s %s%*s %-22s %s' % (
|
||||
'%04x.%04x:' % (rbyd.block, rbyd.trunk)
|
||||
if prbyd is None or rbyd != prbyd
|
||||
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
|
||||
else '',
|
||||
tagrepr(struct_tag,
|
||||
w if not name_tag else 0,
|
||||
len(struct_), None),
|
||||
next(xxd(struct_, 8), '')
|
||||
if not args.get('no_truncate') else ''))
|
||||
prbyd = rbyd
|
||||
|
||||
# show in-device representation
|
||||
if args.get('device'):
|
||||
if name_tag:
|
||||
for i, (tag, j, d, data) in enumerate(tags):
|
||||
print('%9s %*s%*s %-22s%s' % (
|
||||
'',
|
||||
t_width, '',
|
||||
w_width, '',
|
||||
'%04x %08x %07x' % (name_tag, w, len(name)),
|
||||
'%04x %08x %07x' % (tag, w if i == 0 else 0, len(data)),
|
||||
' %s' % ' '.join(
|
||||
'%08x' % fromle32(
|
||||
rbyd.data[name_j+name_d+i*4
|
||||
: name_j+name_d + min(i*4+4,len(name))])
|
||||
for i in range(min(m.ceil(len(name)/4), 3)))[:23]))
|
||||
print('%9s %*s%*s %-22s%s' % (
|
||||
'',
|
||||
t_width, '',
|
||||
w_width, '',
|
||||
'%04x %08x %07x' % (
|
||||
struct_tag, w if not name_tag else 0, len(struct_)),
|
||||
' %s' % ' '.join(
|
||||
'%08x' % fromle32(
|
||||
rbyd.data[struct_j+struct_d+i*4
|
||||
: struct_j+struct_d + min(i*4+4,len(struct_))])
|
||||
for i in range(min(m.ceil(len(struct_)/4), 3)))[:23]))
|
||||
rbyd.data[j+d+i*4 : j+d + min(i*4+4,len(data))])
|
||||
for i in range(min(m.ceil(len(data)/4), 3)))[:23]))
|
||||
|
||||
# show on-disk encoding of tags/data
|
||||
if args.get('raw'):
|
||||
for o, line in enumerate(xxd(
|
||||
rbyd.data[name_j:name_j+name_d])):
|
||||
print('%9s: %*s%*s %s' % (
|
||||
'%04x' % (name_j + o*16),
|
||||
t_width, '',
|
||||
w_width, '',
|
||||
line))
|
||||
if args.get('raw'):
|
||||
for o, line in enumerate(xxd(name)):
|
||||
print('%9s: %*s%*s %s' % (
|
||||
'%04x' % (name_j+name_d + o*16),
|
||||
t_width, '',
|
||||
w_width, '',
|
||||
line))
|
||||
if args.get('raw'):
|
||||
for o, line in enumerate(xxd(
|
||||
rbyd.data[struct_j:struct_j+struct_d])):
|
||||
print('%9s: %*s%*s %s' % (
|
||||
'%04x' % (struct_j + o*16),
|
||||
t_width, '',
|
||||
w_width, '',
|
||||
line))
|
||||
if args.get('raw') or args.get('no_truncate'):
|
||||
for o, line in enumerate(xxd(struct_)):
|
||||
print('%9s: %*s%*s %s' % (
|
||||
'%04x' % (struct_j+struct_d + o*16),
|
||||
t_width, '',
|
||||
w_width, '',
|
||||
line))
|
||||
for i, (tag, j, d, data) in enumerate(tags):
|
||||
if args.get('raw'):
|
||||
for o, line in enumerate(xxd(rbyd.data[j:j+d])):
|
||||
print('%9s: %*s%*s %s' % (
|
||||
'%04x' % (j + o*16),
|
||||
t_width, '',
|
||||
w_width, '',
|
||||
line))
|
||||
# note we don't render name tags with no_truncate
|
||||
if args.get('raw') or (
|
||||
args.get('no_truncate') and tag & 0xf00f != TAG_NAME):
|
||||
for o, line in enumerate(xxd(data)):
|
||||
print('%9s: %*s%*s %s' % (
|
||||
'%04x' % (j+d + o*16),
|
||||
t_width, '',
|
||||
w_width, '',
|
||||
line))
|
||||
|
||||
|
||||
# traverse and print entries
|
||||
id = -1
|
||||
bid = -1
|
||||
prbyd = None
|
||||
ppath = []
|
||||
corrupted = False
|
||||
while True:
|
||||
(done, id, w, rbyd, rid,
|
||||
(name_tag, name_j, name_d, name),
|
||||
(struct_tag, struct_j, struct_d, struct_),
|
||||
path) = (lookup(id+1, depth=args.get('depth')))
|
||||
done, bid, w, rbyd, rid, tags, path = btree_lookup(
|
||||
bid+1, depth=args.get('depth'))
|
||||
if done:
|
||||
break
|
||||
|
||||
if args.get('inner') or args.get('tree'):
|
||||
t_branches = [(0, btree.weight)]
|
||||
if args.get('inner'):
|
||||
changed = False
|
||||
for i, (x, px) in enumerate(
|
||||
it.zip_longest(path[:-1], ppath[:-1])):
|
||||
for (x, px) in it.zip_longest(
|
||||
enumerate(path[:-1]),
|
||||
enumerate(ppath[:-1])):
|
||||
if x is None:
|
||||
break
|
||||
if not (changed or px is None or x != px):
|
||||
continue
|
||||
changed = True
|
||||
|
||||
(id_, w_, rbyd_, rid_,
|
||||
(name_tag_, name_j_, name_d_, name_),
|
||||
(struct_tag_, struct_j_, struct_d_, struct__)) = x
|
||||
t_branches.append((id_-(w_-1), id_+1))
|
||||
|
||||
if args.get('inner'):
|
||||
if not (changed or px is None or x != px):
|
||||
continue
|
||||
changed = True
|
||||
|
||||
# show the inner entry
|
||||
dbg_entry(id_, w_, rbyd_, rid_,
|
||||
name_tag_, name_j_, name_d_, name_,
|
||||
struct_tag_, struct_j_, struct_d_, struct__,
|
||||
i)
|
||||
# show the inner entry
|
||||
d, (bid_, w_, rbyd_, rid_, tags_) = x
|
||||
dbg_branch(bid_, w_, rbyd_, rid_, tags_, d)
|
||||
ppath = path
|
||||
|
||||
# corrupted? try to keep printing the tree
|
||||
if not rbyd:
|
||||
@@ -672,24 +899,28 @@ def main(disk, roots=None, *,
|
||||
'\x1b[m' if color else ''))
|
||||
|
||||
prbyd = rbyd
|
||||
ppath = path
|
||||
corrupted = True
|
||||
continue
|
||||
|
||||
# if we're not showing inner nodes, prefer names higher in the tree
|
||||
# since this avoids showing vestigial names
|
||||
if not args.get('inner'):
|
||||
for id_, w_, rbyd_, rid_, name_, struct__ in reversed(path):
|
||||
name_tag_, name_j, name_d, name = name_
|
||||
name = None
|
||||
for bid_, w_, rbyd_, rid_, tags_ in reversed(path):
|
||||
for tag_, j_, d_, data_ in tags_:
|
||||
if tag_ & 0xf00f == TAG_NAME:
|
||||
name = (tag_, j_, d_, data_)
|
||||
|
||||
if rid_-(w_-1) != 0:
|
||||
break
|
||||
|
||||
# show the entry
|
||||
dbg_entry(id, w, rbyd, rid,
|
||||
name_tag, name_j, name_d, name,
|
||||
struct_tag, struct_j, struct_d, struct_)
|
||||
if name is not None:
|
||||
tags = [name] + [(tag, j, d, data)
|
||||
for tag, j, d, data in tags
|
||||
if tag & 0xf00f != TAG_NAME]
|
||||
|
||||
ppath = path
|
||||
# show the branch
|
||||
dbg_branch(bid, w, rbyd, rid, tags, len(path)-1)
|
||||
|
||||
if args.get('error_on_corrupt') and corrupted:
|
||||
sys.exit(2)
|
||||
@@ -741,6 +972,10 @@ if __name__ == "__main__":
|
||||
parser.add_argument(
|
||||
'-t', '--tree',
|
||||
action='store_true',
|
||||
help="Show the underlying rbyd trees.")
|
||||
parser.add_argument(
|
||||
'-b', '--btree',
|
||||
action='store_true',
|
||||
help="Show the underlying B-tree.")
|
||||
parser.add_argument(
|
||||
'-Z', '--depth',
|
||||
|
||||
Reference in New Issue
Block a user