mirror of
https://github.com/littlefs-project/littlefs.git
synced 2025-12-26 17:18:26 +00:00
Added dbgflags.py for easier flag debugging
dbgerr.py and dbgtag.py have proven to be incredibly useful for quick debugging/introspection, so I figured why not have more of that. My favorite part is being able to quickly see all flags set on an open file handle: (gdb) p file.o.o.flags $2 = 24117517 (gdb) !./scripts/dbgflags.py o 24117517 LFS_O_WRONLY 0x00000001 Open a file as write only LFS_O_CREAT 0x00000004 Create a file if it does not exist LFS_O_EXCL 0x00000008 Fail if a file already exists LFS_O_DESYNC 0x00000100 Do not sync or recieve file updates LFS_o_REG 0x01000000 Type = regular-file LFS_o_UNFLUSH 0x00100000 File's data does not match disk LFS_o_UNSYNC 0x00200000 File's metadata does not match disk LFS_o_UNCREAT 0x00400000 File does not exist yet The only concern is if dbgflags.py falls out-of-sync often, I suspect flag encoding will have quite a bit more churn than flags/tags. But we can always drop this script in the future if this turns into a problem. --- While poking around this also ended up with a bunch of other small changes: - Added LFS_*_MODE masks for consistency with other "type<->flag embeddings" - Added compat flag comments - Adopted lowercase prefix for internal flags (LFS_o_ZOMBIE), though not sure if I'll keep this yet... - Tweaked dbgerr.py to also match ERR_ prefixes and to ignore case
This commit is contained in:
313
scripts/dbgflags.py
Executable file
313
scripts/dbgflags.py
Executable file
@@ -0,0 +1,313 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# prevent local imports
|
||||
if __name__ == "__main__":
|
||||
__import__('sys').path.pop(0)
|
||||
|
||||
import collections as co
|
||||
|
||||
|
||||
ALIASES = [
|
||||
{'O', 'OPEN'},
|
||||
{'A', 'ATTR'},
|
||||
{'F', 'FORMAT'},
|
||||
{'M', 'MOUNT'},
|
||||
{'GC'},
|
||||
{'I', 'INFO'},
|
||||
{'T', 'TRAVERSAL'},
|
||||
{'RC', 'RCOMPAT'},
|
||||
{'WC', 'WCOMPAT'},
|
||||
{'OC', 'OCOMPAT'}
|
||||
]
|
||||
|
||||
FLAGS = [
|
||||
# File open flags
|
||||
('O', 'MODE', 3, "The file's access mode" ),
|
||||
('^', 'RDONLY', 0, "Open a file as read only" ),
|
||||
('^', 'WRONLY', 1, "Open a file as write only" ),
|
||||
('^', 'RDWR', 2, "Open a file as read and write" ),
|
||||
('O', 'CREAT', 0x00000004, "Create a file if it does not exist" ),
|
||||
('O', 'EXCL', 0x00000008, "Fail if a file already exists" ),
|
||||
('O', 'TRUNC', 0x00000010, "Truncate the existing file to zero size" ),
|
||||
('O', 'APPEND', 0x00000020, "Move to end of file on every write" ),
|
||||
('O', 'FLUSH', 0x00000040, "Flush data on every write" ),
|
||||
('O', 'SYNC', 0x00000080, "Sync metadata on every write" ),
|
||||
('O', 'DESYNC', 0x00000100, "Do not sync or recieve file updates" ),
|
||||
('O', 'CKMETA', 0x00010000, "Check metadata checksums" ),
|
||||
('O', 'CKDATA', 0x00020000, "Check metadata + data checksums" ),
|
||||
|
||||
('o', 'TYPE', 0xff000000, "The file's type" ),
|
||||
('^', 'REG', 0x01000000, "Type = regular-file" ),
|
||||
('^', 'DIR', 0x02000000, "Type = directory" ),
|
||||
('^', 'BOOKMARK', 0x04000000, "Type = bookmark" ),
|
||||
('^', 'STICKYNOTE',0x05000000, "Type = stickynote" ),
|
||||
('^', 'TRAVERSAL', 0x09000000, "Type = traversal" ),
|
||||
('o', 'UNFLUSH', 0x00100000, "File's data does not match disk" ),
|
||||
('o', 'UNSYNC', 0x00200000, "File's metadata does not match disk" ),
|
||||
('o', 'UNCREAT', 0x00400000, "File does not exist yet" ),
|
||||
('o', 'ZOMBIE', 0x00800000, "File has been removed" ),
|
||||
|
||||
# Custom attribute flags
|
||||
('A', 'MODE', 3, "The attr's access mode" ),
|
||||
('^', 'RDONLY', 0, "Open an attr as read only" ),
|
||||
('^', 'WRONLY', 1, "Open an attr as write only" ),
|
||||
('^', 'RDWR', 2, "Open an attr as read and write" ),
|
||||
('A', 'LAZY', 0x04, "Only write attr if file changed" ),
|
||||
|
||||
# Filesystem format flags
|
||||
('F', 'MODE', 1, "Format's access mode" ),
|
||||
('^', 'RDWR', 0, "Format the filesystem as read and write" ),
|
||||
('F', 'CKPROGS', 0x00100000, "Check progs by reading back progged data" ),
|
||||
('F', 'CKFETCHES', 0x00200000, "Check block checksums before first use" ),
|
||||
('F', 'CKPARITY', 0x00400000, "Check metadata tag parity bits" ),
|
||||
('F', 'CKDATACKSUMS',
|
||||
0x08000000, "Check data checksums on reads" ),
|
||||
|
||||
('F', 'CKMETA', 0x00010000, "Check metadata checksums" ),
|
||||
('F', 'CKDATA', 0x00020000, "Check metadata + data checksums" ),
|
||||
|
||||
# Filesystem mount flags
|
||||
('M', 'MODE', 1, "Mount's access mode" ),
|
||||
('^', 'RDWR', 0, "Mount the filesystem as read and write" ),
|
||||
('^', 'RDONLY', 1, "Mount the filesystem as read only" ),
|
||||
('M', 'FLUSH', 0x00000040, "Open all files with LFS_O_FLUSH" ),
|
||||
('M', 'SYNC', 0x00000080, "Open all files with LFS_O_SYNC" ),
|
||||
('M', 'CKPROGS', 0x00100000, "Check progs by reading back progged data" ),
|
||||
('M', 'CKFETCHES', 0x00200000, "Check block checksums before first use" ),
|
||||
('M', 'CKPARITY', 0x00400000, "Check metadata tag parity bits" ),
|
||||
('M', 'CKDATACKSUMS',
|
||||
0x08000000, "Check data checksums on reads" ),
|
||||
|
||||
('M', 'MKCONSISTENT',
|
||||
0x00001000, "Make the filesystem consistent" ),
|
||||
('M', 'LOOKAHEAD', 0x00002000, "Populate lookahead buffer" ),
|
||||
('M', 'COMPACT', 0x00008000, "Compact metadata logs" ),
|
||||
('M', 'CKMETA', 0x00010000, "Check metadata checksums" ),
|
||||
('M', 'CKDATA', 0x00020000, "Check metadata + data checksums" ),
|
||||
|
||||
('m', 'UNTIDY', 0x00001000, "Filesystem may have orphaned stickynotes" ),
|
||||
|
||||
# GC flags
|
||||
('GC', 'MKCONSISTENT',
|
||||
0x00001000, "Make the filesystem consistent" ),
|
||||
('GC', 'LOOKAHEAD',0x00002000, "Populate lookahead buffer" ),
|
||||
('GC', 'COMPACT', 0x00008000, "Compact metadata logs" ),
|
||||
('GC', 'CKMETA', 0x00010000, "Check metadata checksums" ),
|
||||
('GC', 'CKDATA', 0x00020000, "Check metadata + data checksums" ),
|
||||
|
||||
# Filesystem info flags
|
||||
('I', 'RDONLY', 0x00000001, "Mounted read only" ),
|
||||
('I', 'FLUSH', 0x00000040, "Mounted with LFS_M_FLUSH" ),
|
||||
('I', 'SYNC', 0x00000080, "Mounted with LFS_M_SYNC" ),
|
||||
('I', 'CKPROGS', 0x00100000, "Mounted with LFS_M_CKPROGS" ),
|
||||
('I', 'CKFETCHES', 0x00200000, "Mounted with LFS_M_CKFETCHES" ),
|
||||
('I', 'CKPARITY', 0x00400000, "Mounted with LFS_M_CKPARITY" ),
|
||||
('I', 'CKDATACKSUMS',
|
||||
0x08000000, "Mounted with LFS_M_CKDATACKSUMS" ),
|
||||
|
||||
('I', 'MKCONSISTENT',
|
||||
0x00001000, "Filesystem needs mkconsistent to write" ),
|
||||
('I', 'LOOKAHEAD', 0x00002000, "Lookahead buffer is not full" ),
|
||||
('I', 'COMPACT', 0x00008000, "Filesystem may have uncompacted metadata" ),
|
||||
('I', 'CKMETA', 0x00010000, "Metadata checksums not checked recently" ),
|
||||
('I', 'CKDATA', 0x00020000, "Data checksums not checked recently" ),
|
||||
|
||||
# Traversal flags
|
||||
('T', 'MTREEONLY', 0x00000800, "Only traverse the mtree" ),
|
||||
('T', 'MKCONSISTENT',
|
||||
0x00001000, "Make the filesystem consistent" ),
|
||||
('T', 'LOOKAHEAD', 0x00002000, "Populate lookahead buffer" ),
|
||||
('T', 'COMPACT', 0x00008000, "Compact metadata logs" ),
|
||||
('T', 'CKMETA', 0x00010000, "Check metadata checksums" ),
|
||||
('T', 'CKDATA', 0x00020000, "Check metadata + data checksums" ),
|
||||
|
||||
('t', 'TSTATE', 0x0000000f, "The traversal's current tstate" ),
|
||||
('^', 'MROOTANCHOR',
|
||||
0x00000000, "Tstate = mroot-anchor" ),
|
||||
('^', 'MROOTCHAIN',0x00000001, "Tstate = mroot-chain" ),
|
||||
('^', 'MTREE', 0x00000002, "Tstate = mtree" ),
|
||||
('^', 'MDIRS', 0x00000003, "Tstate = mtree-mdirs" ),
|
||||
('^', 'MDIR', 0x00000004, "Tstate = mdir" ),
|
||||
('^', 'BTREE', 0x00000005, "Tstate = btree" ),
|
||||
('^', 'OMDIRS', 0x00000006, "Tstate = open-mdirs" ),
|
||||
('^', 'OBTREE', 0x00000007, "Tstate = open-btree" ),
|
||||
('^', 'DONE', 0x00000008, "Tstate = done" ),
|
||||
('t', 'BTYPE', 0x000000f0, "The traversal's current btype" ),
|
||||
('^', 'MDIR', 0x00000010, "Btype = mdir" ),
|
||||
('^', 'BTREE', 0x00000020, "Btype = btree" ),
|
||||
('^', 'DATA', 0x00000030, "Btype = data" ),
|
||||
('t', 'DIRTY', 0x00000100, "Filesystem modified during traversal" ),
|
||||
('t', 'MUTATED', 0x00000200, "Filesystem modified by traversal" ),
|
||||
|
||||
# Read-compat flags
|
||||
('RCOMPAT', 'NONSTANDARD',
|
||||
0x0001, "Non-standard filesystem format" ),
|
||||
('RCOMPAT', 'MLEAF', 0x0002, "May use a single mdir pointer" ),
|
||||
('RCOMPAT', 'MTREE', 0x0008, "May use an mdir btree" ),
|
||||
('RCOMPAT', 'BSPROUT', 0x0010, "Files may use inlined data" ),
|
||||
('RCOMPAT', 'BLEAF', 0x0020, "Files may use single block pointers" ),
|
||||
('RCOMPAT', 'BSHRUB', 0x0040, "Files may use inlined btrees" ),
|
||||
('RCOMPAT', 'BTREE', 0x0080, "Files may use btrees" ),
|
||||
('RCOMPAT', 'GRM', 0x0100, "May use a global-remove" ),
|
||||
|
||||
('rcompat', 'OVERFLOW',0x8000, "Can't represent all flags" ),
|
||||
|
||||
# Write-compat flags
|
||||
('WCOMPAT', 'NONSTANDARD',
|
||||
0x0001, "Non-standard filesystem format" ),
|
||||
|
||||
('wcompat', 'OVERFLOW',0x8000, "Can't represent all flags" ),
|
||||
|
||||
# Optional-compat flags
|
||||
('OCOMPAT', 'NONSTANDARD',
|
||||
0x0001, "Non-standard filesystem format" ),
|
||||
|
||||
('ocompat', 'OVERFLOW',0x8000, "Can't represent all flags" ),
|
||||
]
|
||||
|
||||
|
||||
def main(flags, *,
|
||||
list=False,
|
||||
all=False):
|
||||
import builtins
|
||||
list_, list = list, builtins.list
|
||||
all_, all = all, builtins.all
|
||||
|
||||
# first compile prefixes
|
||||
prefixes = {}
|
||||
for a in ALIASES:
|
||||
for k in a:
|
||||
prefixes[k] = a
|
||||
for p, n, f, h in FLAGS:
|
||||
if p not in prefixes:
|
||||
prefixes[p] = {p}
|
||||
|
||||
# only look at specific prefix?
|
||||
flags_ = []
|
||||
prefix = set()
|
||||
for f in flags:
|
||||
if f.upper() in prefixes:
|
||||
prefix.update(prefixes[f.upper()])
|
||||
else:
|
||||
flags_.append(f)
|
||||
|
||||
# filter by prefix
|
||||
flags__ = []
|
||||
types__ = co.defaultdict(lambda: set())
|
||||
for p, n, f, h in FLAGS:
|
||||
if p == '^':
|
||||
p = last_p
|
||||
t = last_t
|
||||
types__[p].add(t)
|
||||
else:
|
||||
t = None
|
||||
last_p = p
|
||||
last_t = f
|
||||
if not prefix or p.upper() in prefix:
|
||||
flags__.append((p, t, n, f, h))
|
||||
|
||||
lines = []
|
||||
# list all known flags
|
||||
if list_:
|
||||
for p, t, n, f, h in flags__:
|
||||
if not all_ and (t is not None or p[0].islower()):
|
||||
continue
|
||||
lines.append(('LFS_%s_%s' % (p, n), '0x%08x' % f, h))
|
||||
|
||||
# find flags by name or value
|
||||
else:
|
||||
for f_ in flags_:
|
||||
found = False
|
||||
# find by LFS_+prefix+_+name
|
||||
for p, t, n, f, h in flags__:
|
||||
if 'LFS_%s_%s' % (p, n) == f_.upper():
|
||||
lines.append(('LFS_%s_%s' % (p, n), '0x%08x' % f, h))
|
||||
found = True
|
||||
if found:
|
||||
continue
|
||||
# find by prefix+_+name
|
||||
for p, t, n, f, h in flags__:
|
||||
if '%s_%s' % (p, n) == f_.upper():
|
||||
lines.append(('LFS_%s_%s' % (p, n), '0x%08x' % f, h))
|
||||
found = True
|
||||
if found:
|
||||
continue
|
||||
# find by name
|
||||
for p, t, n, f, h in flags__:
|
||||
if n == f_.upper():
|
||||
lines.append(('LFS_%s_%s' % (p, n), '0x%08x' % f, h))
|
||||
found = True
|
||||
if found:
|
||||
continue
|
||||
# find by value
|
||||
try:
|
||||
f__ = int(f_, 0)
|
||||
f___ = f__
|
||||
for p, t, n, f, h in flags__:
|
||||
# ignore type masks here
|
||||
if t is None and f in types__[p]:
|
||||
continue
|
||||
# matches flag?
|
||||
if t is None and (f__ & f) == f:
|
||||
lines.append(('LFS_%s_%s' % (p, n), '0x%08x' % f, h))
|
||||
f___ &= ~f
|
||||
# matches type?
|
||||
elif t is not None and (f__ & t) == f:
|
||||
lines.append(('LFS_%s_%s' % (p, n), '0x%08x' % f, h))
|
||||
f___ &= ~t
|
||||
if f___:
|
||||
lines.append(('?', '0x%08x' % f___, 'Unknown flags'))
|
||||
except ValueError:
|
||||
lines.append(('?', f_, 'Unknown flag'))
|
||||
|
||||
# first find widths
|
||||
w = [0, 0]
|
||||
for l in lines:
|
||||
w[0] = max(w[0], len(l[0]))
|
||||
w[1] = max(w[1], len(l[1]))
|
||||
|
||||
# then print results
|
||||
for l in lines:
|
||||
print('%-*s %-*s %s' % (
|
||||
w[0], l[0],
|
||||
w[1], l[1],
|
||||
l[2]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
import sys
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Decode littlefs error codes.",
|
||||
allow_abbrev=False)
|
||||
class AppendFlags(argparse.Action):
|
||||
def __call__(self, parser, namespace, value, option):
|
||||
if getattr(namespace, 'flags', None) is None:
|
||||
namespace.flags = []
|
||||
if value is None:
|
||||
pass
|
||||
elif isinstance(value, str):
|
||||
namespace.flags.append(value)
|
||||
else:
|
||||
namespace.flags.extend(value)
|
||||
parser.add_argument(
|
||||
'prefix',
|
||||
nargs='?',
|
||||
action=AppendFlags,
|
||||
help="Flag prefix to consider, defaults to all flags.")
|
||||
parser.add_argument(
|
||||
'flags',
|
||||
nargs='*',
|
||||
action=AppendFlags,
|
||||
help="Flags or names of flags to decode.")
|
||||
parser.add_argument(
|
||||
'-l', '--list',
|
||||
action='store_true',
|
||||
help="List all known flags.")
|
||||
parser.add_argument(
|
||||
'-a', '--all',
|
||||
action='store_true',
|
||||
help="Also show internal flags and types.")
|
||||
sys.exit(main(**{k: v
|
||||
for k, v in vars(parser.parse_intermixed_args()).items()
|
||||
if v is not None}))
|
||||
Reference in New Issue
Block a user