Added scripts/dbgbtree.py for debugging B-trees, tweaked dbgrbyd.py

An example:

  $ ./scripts/dbgbtree.py -B4096 disk 0xaa -t -i
  btree 0xaa.1000, rev 35, weight 278
  block            ids     name     tag                     data
  (truncated)
  00aa.1000: +-+      0-16          branch id16 3           7e d4 10                 ~..
  007e.0854: | |->       0          inlined id0 1           73                       s
             | |->       1          inlined id1 1           74                       t
             | |->       2          inlined id2 1           75                       u
             | |->       3          inlined id3 1           76                       v
             | |->       4          inlined id4 1           77                       w
             | |->       5          inlined id5 1           78                       x
             | |->       6          inlined id6 1           79                       y
             | |->       7          inlined id7 1           7a                       z
             | |->       8          inlined id8 1           61                       a
             | |->       9          inlined id9 1           62                       b
  ...

This added the idea of block+limit addresses such as 0xaa.1000. Added
this as an option to dbgrbyd.py along with a couple other tweaks:

- Added block+limit support (0x<block>.<limit>).
- Fixed in-device representation indentation when trees are present.
- Changed fromtag to implicitly fixup ids/weights off-by-one-ness, this
  is consistent with lfs.c.
This commit is contained in:
Christopher Haster
2023-03-09 15:52:46 -06:00
parent ce18bec66d
commit ce599be70d
2 changed files with 679 additions and 39 deletions
+72 -39
View File
@@ -16,6 +16,25 @@ COLORS = [
]
def blocklim(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
s0, s1 = s.split('.', 1)
return int(s0, b), int(s1, b)
else:
return int(s, 0)
def crc32c(data, crc=0):
crc ^= 0xffffffff
for b in data:
@@ -37,7 +56,7 @@ def fromtag(data):
tag, delta = fromleb128(data)
id, delta_ = fromleb128(data[delta:])
size, delta__ = fromleb128(data[delta+delta_:])
return tag&1, tag&~1, id-1, size, delta+delta_+delta__
return tag&1, tag&~1, id if tag&0x8 else id-1, size, delta+delta_+delta__
def popc(x):
return bin(x).count('1')
@@ -52,15 +71,14 @@ def xxd(data, width=16, crc=False):
b if b >= ' ' and b <= '~' else '.'
for b in map(chr, data[i:i+width])))
def tagrepr(tag, id, w, size, off=None):
def tagrepr(tag, id, size, off=None):
if (tag & ~0x3f0) == 0x0400:
return 'mk%s id%d%s %d' % (
return 'mk%s id%d %d' % (
'branch' if ((tag & 0x3f0) >> 4) == 0x00
else 'reg' if ((tag & 0x3f0) >> 4) == 0x01
else 'dir' if ((tag & 0x3f0) >> 4) == 0x02
else ' 0x%02x' % ((tag & 0x3f0) >> 4),
id,
' w%d' % w if w is not None else '',
size)
elif tag == 0x0800:
return 'inlined id%d %d' % (id, size)
@@ -98,7 +116,7 @@ def tagrepr(tag, id, w, size, off=None):
'r' if tag & 0x2 else 'b',
'gt' if tag & 0x4 else 'le',
tag & 0x3ff0,
id+1,
id,
'0x%x' % (0xffffffff & (off-size))
if off is not None
else '-%d' % off)
@@ -300,7 +318,7 @@ def show_log(block_size, data, rev, off, *,
lifetimerepr(j) if args.get('lifetimes') else '',
'\x1b[90m' if color and j >= off else '',
'%-22s%s' % (
tagrepr(tag, id, None, size, j),
tagrepr(tag, id, size, j),
' %s' % next(xxd(
data[j+delta:j+delta+min(size, 8)], 8), '')
if not args.get('no_truncate')
@@ -334,7 +352,8 @@ def show_log(block_size, data, rev, off, *,
data[j+delta+i*4:j+delta+min(i*4+4,size)]
.ljust(4, b'\0'))
for i in range(min(m.ceil(size/4), 3)))[:23]
if (tag & 0xe) <= 0x4 else ''),
if not args.get('no_truncate')
and (tag & 0xe) <= 0x4 else ''),
crc,
popc(crc) & 1,
'\x1b[m' if color and j >= off else ''))
@@ -370,7 +389,6 @@ def show_tree(block_size, data, rev, trunk, weight, *,
# found an alt?
if alt & 0x8:
weight_ += 1
# follow?
if ((id, tag & ~0xf) > (upper-weight_-1, alt & ~0xf)
if alt & 0x4
@@ -510,9 +528,11 @@ def show_tree(block_size, data, rev, trunk, weight, *,
return ' %s' % ''.join(path)
# print header
print('%-8s %*s%-22s %s' % (
w_width = 2*m.ceil(m.log10(max(1, weight)+1))+1
print('%-8s %*s%-*s %-22s %s' % (
'off',
2*depth+1 if args.get('tree') and depth > 0 else 0, '',
w_width, 'ids',
'tag',
'data (truncated)'
if not args.get('no_truncate') else ''))
@@ -525,29 +545,25 @@ def show_tree(block_size, data, rev, trunk, weight, *,
break
# show human-readable tag representation
print('%08x:%s %-57s' % (
print('%08x:%s %*s %-57s' % (
j,
treerepr(j) if args.get('tree') else '',
w_width, '%d-%d' % (id-(w-1), id)
if w > 1 else id
if w > 0 else '',
'%-22s%s' % (
tagrepr(tag, id, w, size, j),
tagrepr(tag, id, size, j),
' %s' % next(xxd(
data[j+delta:j+delta+min(size, 8)], 8), '')
if not args.get('no_truncate')
and (tag & 0xe) <= 0x4 else '')))
if args.get('raw'):
# show on-disk encoding of tags
for o, line in enumerate(xxd(data[j:j+delta])):
print('%8s: %s' % (
'%04x' % (j + o*16),
line))
# show in-device representation, including some extra
# crc/parity info
# show in-device representation
if args.get('device'):
print('%8s %s%-47s' % (
print('%8s %*s%*s %s' % (
'',
lifetimerepr(0) if args.get('lifetimes') else '',
2*depth+1 if args.get('tree') and depth > 0 else 0, '',
w_width, '',
'%-22s%s' % (
'%04x %08x %07x' % (tag, 0xffffffff & id, size),
' %s' % ' '.join(
@@ -555,7 +571,15 @@ def show_tree(block_size, data, rev, trunk, weight, *,
data[j+delta+i*4:j+delta+min(i*4+4,size)]
.ljust(4, b'\0'))
for i in range(min(m.ceil(size/4), 3)))[:23]
if (tag & 0xe) <= 0x4 else '')))
if not args.get('no_truncate')
and (tag & 0xe) <= 0x4 else '')))
if args.get('raw'):
# show on-disk encoding of tags
for o, line in enumerate(xxd(data[j:j+delta])):
print('%8s: %s' % (
'%04x' % (j + o*16),
line))
if (tag & 0xe) <= 0x4:
# show on-disk encoding of data
@@ -585,16 +609,23 @@ def main(disk, block_size=None, block1=0, block2=None, *,
f.seek(0, os.SEEK_END)
block_size = f.tell()
# default limit to the block_size
if limit is None:
limit = block_size
# blocks may also encode limits
blocks = [
block[0] if isinstance(block, tuple) else block
for block in [block1, block2]
if block is not None]
limits = [
limit if limit is not None
else block[1] if isinstance(block, tuple)
else None
for block in [block1, block2]
if block is not None]
# read each block
blocks = [block for block in [block1, block2] if block is not None]
datas = []
for block in blocks:
for block, limit in zip(blocks, limits):
f.seek(block * block_size)
datas.append(f.read(block_size))
datas.append(f.read(limit if limit is not None else block_size))
# first figure out which block as the most recent revision
def fetch(data):
@@ -607,7 +638,7 @@ def main(disk, block_size=None, block1=0, block2=None, *,
weight = 0
weight_ = 0
wastrunk = False
while j_ < limit:
while j_ < len(data):
v, tag, id, size, delta = fromtag(data[j_:])
if v != (popc(crc) & 1):
break
@@ -644,7 +675,7 @@ def main(disk, block_size=None, block1=0, block2=None, *,
revs, offs, trunks, weights = [], [], [], []
i = 0
for block, data in zip(blocks, datas):
for data in datas:
rev, off, trunk_, weight = fetch(data)
revs.append(rev)
offs.append(off)
@@ -656,15 +687,17 @@ def main(disk, block_size=None, block1=0, block2=None, *,
i = len(revs)-1
# print contents of the winning metadata block
block, data, rev, off, trunk, weight = (
blocks[i], datas[i], revs[i], offs[i],
block, limit, data, rev, off, trunk, weight = (
blocks[i], limits[i], datas[i], revs[i], offs[i],
trunk if trunk is not None else trunks[i],
weights[i])
print('mdir 0x%x, rev %d, size %d, weight %d%s' % (
block, rev, off, weight,
' (was 0x%x, %d, %d, %d)' % (
blocks[~i], revs[~i], offs[~i], weights[~i])
print('rbyd 0x%x%s, rev %d, size %d, weight %d%s' % (
block, '.%x' % limit if limit is not None else '',
rev, off, weight,
' (was 0x%x%s, %d, %d, %d)' % (
blocks[~i], '.%x' % limits[~i] if limits[~i] is not None else '',
revs[~i], offs[~i], weights[~i])
if len(blocks) > 1 else ''))
if args.get('log'):
@@ -692,12 +725,12 @@ if __name__ == "__main__":
parser.add_argument(
'block1',
nargs='?',
type=lambda x: int(x, 0),
type=blocklim,
help="Block address of the first metadata block.")
parser.add_argument(
'block2',
nargs='?',
type=lambda x: int(x, 0),
type=blocklim,
help="Block address of the second metadata block.")
parser.add_argument(
'-B', '--block-size',