Added dbgmtree.py for debugging the littlefs metadata-tree

This builds on dbgrbyd.py and dbgbtree.py by allowing for quick
debugging of the littlefs mtree, which is a btree of rbyd pairs with a
few bells and whistles.

This also comes with a number of tweaks to dbgrbyd.py and dbgbtree.py,
mostly changing rbyd addresses to support some more mdir friendly
formats.

The syntax for rbyd addresses is starting to converge into a couple
common patterns, which is nice for quickly determining what type of
address you are looking at at a glance:

- 0x12         => An rbyd at block 0x12
- 0x12.34      => An rbyd at block 0x12 with trunk 0x34
- 0x{12,34}    => An rbyd at either block 0x12 or block 0x34 (an mdir)
- 0x{12,34}.56 => An rbyd at either block 0x12 or block 0x34 with trunk 0x56

These scripts have also been updated to support any number of blocks in
an rbyd address, for example 0x{12,34,56,78}. This is a bit of future
proofing. >2 blocks in mdirs may be explored in the future for the
increased redundancy.
This commit is contained in:
Christopher Haster
2023-05-18 21:55:02 -05:00
parent f7d4497b80
commit b67fcb0ee5
3 changed files with 993 additions and 93 deletions
+69 -49
View File
@@ -35,24 +35,41 @@ TAG_ALT = 0x0008
TAG_CRC = 0x0004
TAG_FCRC = 0x1004
# parse some rbyd addr encodings
# 0xa -> [0xa]
# 0xa.b -> ([0xa], b)
# 0x{a,b} -> [0xa, 0xb]
def rbydaddr(s):
if '.' in s:
s = s.strip()
b = 10
if s.startswith('0x') or s.startswith('0X'):
s = s[2:]
b = 16
elif s.startswith('0o') or s.startswith('0O'):
s = s[2:]
b = 8
elif s.startswith('0b') or s.startswith('0B'):
s = s[2:]
b = 2
s = s.strip()
b = 10
if s.startswith('0x') or s.startswith('0X'):
s = s[2:]
b = 16
elif s.startswith('0o') or s.startswith('0O'):
s = s[2:]
b = 8
elif s.startswith('0b') or s.startswith('0B'):
s = s[2:]
b = 2
s0, s1 = s.split('.', 1)
return int(s0, b), int(s1, b)
trunk = None
if '.' in s:
s, s_ = s.split('.', 1)
trunk = int(s_, b)
if s.startswith('{') and '}' in s:
ss = s[1:s.find('}')].split(',')
else:
return int(s, 0)
ss = [s]
addr = []
for s in ss:
if trunk is not None:
addr.append((int(s, b), trunk))
else:
addr.append(int(s, b))
return addr
def crc32c(data, crc=0):
crc ^= 0xffffffff
@@ -162,7 +179,7 @@ def tagrepr(tag, w, size, off=None):
else:
return '0x%04x w%d %d' % (tag, w, size)
def show_log(data, block_size, rev, off, weight, *,
def dbg_log(data, block_size, rev, off, weight, *,
color=False,
**args):
crc = crc32c(data[0:4])
@@ -507,7 +524,7 @@ def show_log(data, block_size, rev, off, weight, *,
'\x1b[m' if color and j >= off else ''))
def show_tree(data, block_size, rev, trunk, weight, *,
def dbg_tree(data, block_size, rev, trunk, weight, *,
color=False,
**args):
if not trunk:
@@ -740,7 +757,7 @@ def show_tree(data, block_size, rev, trunk, weight, *,
tree_width, '',
w_width, '',
'%-22s%s' % (
'%04x %08x %07x' % (tag, 0xffffffff & id, size),
'%04x %08x %07x' % (tag, w, size),
' %s' % ' '.join(
'%08x' % fromle32(
data[j+d+i*4:j+d+min(i*4+4,size)])
@@ -766,7 +783,7 @@ def show_tree(data, block_size, rev, trunk, weight, *,
line))
def main(disk, block1=0, block2=None, *,
def main(disk, blocks=None, *,
block_size=None,
trunk=None,
color='auto',
@@ -779,6 +796,11 @@ def main(disk, block1=0, block2=None, *,
else:
color = False
# flatten blocks, default to block 0
if not blocks:
blocks = [[0]]
blocks = [block for blocks_ in blocks for block in blocks_]
with open(disk, 'rb') as f:
# if block_size is omitted, assume the block device is one big block
if block_size is None:
@@ -786,16 +808,13 @@ def main(disk, block1=0, block2=None, *,
block_size = f.tell()
# blocks may also encode trunks
blocks = [
block[0] if isinstance(block, tuple) else block
for block in [block1, block2]
if block is not None]
trunks = [
trunk if trunk is not None
else block[1] if isinstance(block, tuple)
else None
for block in [block1, block2]
if block is not None]
blocks, trunks = (
[block[0] if isinstance(block, tuple) else block
for block in blocks],
[trunk if trunk is not None
else block[1] if isinstance(block, tuple)
else None
for block in blocks])
# read each block
datas = []
@@ -870,33 +889,39 @@ def main(disk, block1=0, block2=None, *,
revs, offs, trunks_, weights = [], [], [], []
i = 0
for data, trunk in zip(datas, trunks):
rev, off, trunk_, weight = fetch(data, trunk)
for i_, (data, trunk_) in enumerate(zip(datas, trunks)):
rev, off, trunk_, weight = fetch(data, trunk_)
revs.append(rev)
offs.append(off)
trunks_.append(trunk_)
weights.append(weight)
# compare with sequence arithmetic
if off and not ((rev - revs[i]) & 0x80000000):
i = len(revs)-1
if trunk_ and (
not ((rev - revs[i]) & 0x80000000)
or (rev == revs[i] and trunk_ > trunks_[i])):
i = i_
# print contents of the winning metadata block
block, data, rev, off, trunk, weight = (
block, data, rev, off, trunk_, weight = (
blocks[i], datas[i], revs[i], offs[i], trunks_[i], weights[i])
print('rbyd 0x%x.%x, rev %d, size %d, weight %d%s' % (
block, trunk, rev, off, weight,
' (was 0x%x.%x, %d, %d, %d)' % (
blocks[~i], trunks_[~i], revs[~i], offs[~i], weights[~i])
if len(blocks) > 1 else ''))
print('rbyd %s, rev %d, size %d, weight %d' % (
'0x%x.%x' % (block, trunk_)
if len(blocks) == 1
else '0x{%x,%s}.%x' % (
block,
','.join('%x' % blocks[(i+1+j) % len(blocks)]
for j in range(len(blocks)-1)),
trunk_),
rev, off, weight))
if args.get('log'):
show_log(data, block_size, rev, off, weight,
dbg_log(data, block_size, rev, off, weight,
color=color,
**args)
else:
show_tree(data, block_size, rev, trunk, weight,
dbg_tree(data, block_size, rev, trunk_, weight,
color=color,
**args)
@@ -914,15 +939,10 @@ if __name__ == "__main__":
'disk',
help="File containing the block device.")
parser.add_argument(
'block1',
nargs='?',
'blocks',
nargs='*',
type=rbydaddr,
help="Block address of the first metadata block.")
parser.add_argument(
'block2',
nargs='?',
type=rbydaddr,
help="Block address of the second metadata block.")
help="Block address of metadata blocks.")
parser.add_argument(
'-B', '--block-size',
type=lambda x: int(x, 0),