diff options
author | Christopher Haster <chaster@utexas.edu> | 2020-03-30 05:19:33 +0300 |
---|---|---|
committer | Christopher Haster <chaster@utexas.edu> | 2020-03-30 05:19:33 +0300 |
commit | 5137e4b0bab12a2d88c1e960acc1acf0f278563f (patch) | |
tree | bcfe94d3c7a5d0444d86d10b62ccd9de87ab0f3c /scripts | |
parent | ff84902970326efc4449f5cb1a8ccd3b466e18a9 (diff) |
Last minute tweaks to debug scripts
- Standardized littlefs debug statements to use hex prefixes and
brackets for printing pairs.
- Removed the entry behavior for readtree and made -t the default.
This is because 1. the CTZ skip-list parsing was broken, which is not
surprising, and 2. the entry parsing was more complicated than useful.
This functionality may be better implemented as a proper filesystem
read script, complete with directory tree dumping.
- Changed test.py's --gdb argument to take [init, main, assert],
this matches the names of the stages in C's startup.
- Added printing of tail to all mdir dumps in readtree/readmdir.
- Added a print for if any mdirs are corrupted in readtree.
- Added debug script side-effects to .gitignore.
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/readmdir.py | 18 | ||||
-rwxr-xr-x | scripts/readtree.py | 162 | ||||
-rwxr-xr-x | scripts/test.py | 4 |
3 files changed, 58 insertions, 126 deletions
diff --git a/scripts/readmdir.py b/scripts/readmdir.py index 75f8b9a..b6c3dcc 100755 --- a/scripts/readmdir.py +++ b/scripts/readmdir.py @@ -233,8 +233,8 @@ class MetadataPair: def __lt__(self, other): # corrupt blocks don't count - if not self and other: - return True + if not self or not other: + return bool(other) # use sequence arithmetic to avoid overflow return not ((other.rev - self.rev) & 0x80000000) @@ -318,14 +318,24 @@ def main(args): # find most recent pair mdir = MetadataPair(blocks) - print("mdir {%s} rev %d%s%s" % ( + + try: + mdir.tail = mdir[Tag('tail', 0, 0)] + if mdir.tail.size != 8 or mdir.tail.data == 8*b'\xff': + mdir.tail = None + except KeyError: + mdir.tail = None + + print("mdir {%s} rev %d%s%s%s" % ( ', '.join('%#x' % b for b in [args.block1, args.block2] if b is not None), mdir.rev, ' (was %s)' % ', '.join('%d' % m.rev for m in mdir.pair[1:]) if len(mdir.pair) > 1 else '', - ' (corrupted)' if not mdir else '')) + ' (corrupted!)' if not mdir else '', + ' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data) + if mdir.tail else '')) if args.all: mdir.dump_all(truncate=not args.no_truncate) elif args.log: diff --git a/scripts/readtree.py b/scripts/readtree.py index 3965a36..36135ab 100755 --- a/scripts/readtree.py +++ b/scripts/readtree.py @@ -7,97 +7,14 @@ import io import itertools as it from readmdir import Tag, MetadataPair -def popc(x): - return bin(x).count('1') - -def ctz(x): - return len(bin(x)) - len(bin(x).rstrip('0')) - -def dumpentries(args, mdir, f): - for k, id_ in enumerate(mdir.ids): - name = mdir[Tag('name', id_, 0)] - struct_ = mdir[Tag('struct', id_, 0)] - - desc = "id %d %s %s" % ( - id_, name.typerepr(), - json.dumps(name.data.decode('utf8'))) - if struct_.is_('dirstruct'): - desc += " dir {%#x, %#x}" % struct.unpack( - '<II', struct_.data[:8].ljust(8, b'\xff')) - if struct_.is_('ctzstruct'): - desc += " ctz {%#x} size %d" % struct.unpack( - '<II', struct_.data[:8].ljust(8, b'\xff')) - if struct_.is_('inlinestruct'): - desc += " inline size %d" % struct_.size - - data = None - if struct_.is_('inlinestruct'): - data = struct_.data - elif struct_.is_('ctzstruct'): - block, size = struct.unpack( - '<II', struct_.data[:8].ljust(8, b'\xff')) - data = [] - i = 0 if size == 0 else (size-1) // (args.block_size - 8) - if i != 0: - i = ((size-1) - 4*popc(i-1)+2) // (args.block_size - 8) - with open(args.disk, 'rb') as f2: - while i >= 0: - f2.seek(block * args.block_size) - dat = f2.read(args.block_size) - data.append(dat[4*(ctz(i)+1) if i != 0 else 0:]) - block, = struct.unpack('<I', dat[:4].ljust(4, b'\xff')) - i -= 1 - data = bytes(it.islice( - it.chain.from_iterable(reversed(data)), size)) - - f.write("%-45s%s\n" % (desc, - "%-23s %-8s" % ( - ' '.join('%02x' % c for c in data[:8]), - ''.join(c if c >= ' ' and c <= '~' else '.' - for c in map(chr, data[:8]))) - if not args.no_truncate and len(desc) < 45 - and data is not None else "")) - - if name.is_('superblock') and struct_.is_('inlinestruct'): - f.write( - " block_size %d\n" - " block_count %d\n" - " name_max %d\n" - " file_max %d\n" - " attr_max %d\n" % struct.unpack( - '<IIIII', struct_.data[4:4+20].ljust(20, b'\xff'))) - - for tag in mdir.tags: - if tag.id==id_ and tag.is_('userattr'): - desc = "%s size %d" % (tag.typerepr(), tag.size) - f.write(" %-43s%s\n" % (desc, - "%-23s %-8s" % ( - ' '.join('%02x' % c for c in tag.data[:8]), - ''.join(c if c >= ' ' and c <= '~' else '.' - for c in map(chr, tag.data[:8]))) - if not args.no_truncate and len(desc) < 43 else "")) - - if args.no_truncate: - for i in range(0, len(tag.data), 16): - f.write(" %08x: %-47s %-16s\n" % ( - i, ' '.join('%02x' % c for c in tag.data[i:i+16]), - ''.join(c if c >= ' ' and c <= '~' else '.' - for c in map(chr, tag.data[i:i+16])))) - - if args.no_truncate and data is not None: - for i in range(0, len(data), 16): - f.write(" %08x: %-47s %-16s\n" % ( - i, ' '.join('%02x' % c for c in data[i:i+16]), - ''.join(c if c >= ' ' and c <= '~' else '.' - for c in map(chr, data[i:i+16])))) - def main(args): + superblock = None + gstate = b'\0\0\0\0\0\0\0\0\0\0\0\0' + dirs = [] + mdirs = [] + corrupted = [] + cycle = False with open(args.disk, 'rb') as f: - dirs = [] - superblock = None - gstate = b'' - mdirs = [] - cycle = False tail = (args.block1, args.block2) hard = False while True: @@ -144,6 +61,10 @@ def main(args): except KeyError: pass + # corrupted? + if not mdir: + corrupted.append(mdir) + # add to directories mdirs.append(mdir) if mdir.tail is None or not mdir.tail.is_('hardtail'): @@ -178,7 +99,7 @@ def main(args): dir[0].path = path.replace('//', '/') - # dump tree + # print littlefs + version info version = ('?', '?') if superblock: version = tuple(reversed( @@ -187,53 +108,56 @@ def main(args): "data (truncated, if it fits)" if not any([args.no_truncate, args.tags, args.log, args.all]) else "")) - if gstate: - print("gstate 0x%s" % ''.join('%02x' % c for c in gstate)) - tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0]) - blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff')) - if tag.size or not tag.isvalid: - print(" orphans >=%d" % max(tag.size, 1)) - if tag.type: - print(" move dir {%#x, %#x} id %d" % ( - blocks[0], blocks[1], tag.id)) - + # print gstate + print("gstate 0x%s" % ''.join('%02x' % c for c in gstate)) + tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0]) + blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff')) + if tag.size or not tag.isvalid: + print(" orphans >=%d" % max(tag.size, 1)) + if tag.type: + print(" move dir {%#x, %#x} id %d" % ( + blocks[0], blocks[1], tag.id)) + + # print mdir info for i, dir in enumerate(dirs): print("dir %s" % (json.dumps(dir[0].path) if hasattr(dir[0], 'path') else '(orphan)')) for j, mdir in enumerate(dir): - print("mdir {%#x, %#x} rev %d%s" % ( - mdir.blocks[0], mdir.blocks[1], mdir.rev, - ' (corrupted)' if not mdir else '')) + print("mdir {%#x, %#x} rev %d (was %d)%s%s" % ( + mdir.blocks[0], mdir.blocks[1], mdir.rev, mdir.pair[1].rev, + ' (corrupted!)' if not mdir else '', + ' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data) + if mdir.tail else '')) f = io.StringIO() - if args.tags: - mdir.dump_tags(f, truncate=not args.no_truncate) - elif args.log: + if args.log: mdir.dump_log(f, truncate=not args.no_truncate) elif args.all: mdir.dump_all(f, truncate=not args.no_truncate) else: - dumpentries(args, mdir, f) + mdir.dump_tags(f, truncate=not args.no_truncate) lines = list(filter(None, f.getvalue().split('\n'))) for k, line in enumerate(lines): print("%s %s" % ( - ' ' if i == len(dirs)-1 and j == len(dir)-1 else + ' ' if j == len(dir)-1 else 'v' if k == len(lines)-1 else - '.' if j == len(dir)-1 else '|', line)) - if cycle: - print("*** cycle detected! -> {%#x, %#x} ***" % (cycle[0], cycle[1])) + errcode = 0 + for mdir in corrupted: + errcode = errcode or 1 + print("*** corrupted mdir {%#x, %#x}! ***" % ( + mdir.blocks[0], mdir.blocks[1])) if cycle: - return 2 - elif not all(mdir for dir in dirs for mdir in dir): - return 1 - else: - return 0; + errcode = errcode or 2 + print("*** cycle detected {%#x, %#x}! ***" % ( + cycle[0], cycle[1])) + + return errcode if __name__ == "__main__": import argparse @@ -246,12 +170,10 @@ if __name__ == "__main__": help="Size of a block in bytes.") parser.add_argument('block1', nargs='?', default=0, type=lambda x: int(x, 0), - help="Optional first block address for finding the root.") + help="Optional first block address for finding the superblock.") parser.add_argument('block2', nargs='?', default=1, type=lambda x: int(x, 0), - help="Optional second block address for finding the root.") - parser.add_argument('-t', '--tags', action='store_true', - help="Show metadata tags instead of reconstructing entries.") + help="Optional second block address for finding the superblock.") parser.add_argument('-l', '--log', action='store_true', help="Show tags in log.") parser.add_argument('-a', '--all', action='store_true', diff --git a/scripts/test.py b/scripts/test.py index 6187028..e5869c2 100755 --- a/scripts/test.py +++ b/scripts/test.py @@ -231,7 +231,7 @@ class TestCase: ncmd.extend(['-ex', 'r']) if failure.assert_: ncmd.extend(['-ex', 'up 2']) - elif gdb == 'start': + elif gdb == 'main': ncmd.extend([ '-ex', 'b %s:%d' % (self.suite.path, self.code_lineno), '-ex', 'r']) @@ -760,7 +760,7 @@ if __name__ == "__main__": help="Store disk image in a file.") parser.add_argument('-b', '--build', action='store_true', help="Only build the tests, do not execute.") - parser.add_argument('-g', '--gdb', choices=['init', 'start', 'assert'], + parser.add_argument('-g', '--gdb', choices=['init', 'main', 'assert'], nargs='?', const='assert', help="Drop into gdb on test failure.") parser.add_argument('--no-internal', action='store_true', |