Files
littlefs/scripts/dbg.gdb.py
Christopher Haster 29e1701964 scripts: gdb: Globbed all dbg scripts into dbg.gdb.py
This goes ahead and makes all dbg scripts available in dbg.gdb.py, via
the magic of globbing __file__ relative, and dynamic python class
generation.

Probably one of the more evil scripts I've written, but this means we
don't need to worry about dbg.gdb.py falling out-of-date when adding new
dbg scripts.

Not all of the dbg scripts are useful inside gdb, but most of them are.
After all, what's cooler than this!

  (gdb) dbgrbyd -b4096 "disk" -t \
          file->b.shrub.blocks[0] \
          --trunk lfs3_rbyd_trunk(&file->b.shrub)
  rbyd 0x46.23a w2048, rev 00000000, size 629, cksum 8f5169e1
  00000004:           .->     0-334 data w335 0
  00000009:         .-+->       335 data w1 1                  71
  0000000e:         | .->       336 data w1 1                  67
  00000013:       .-+-+->       337 data w1 1                  66
  ...
  00000144: | | | |   .->       350 data w1 1                  74
  0000019a: | | | | .-+->       351 data w1 1                  78
  000001f5: | | | | | .->   352-739 data w388 1                76
  00000258: +-+-+-+-+-+->  740-2047 data w1308 1               6c

Note some tricks to help interact with bash and gdb:

- Flags are passed as is (-b4096, -t, --trunk)
- All non-flags are parsed as expressions (file->b.shrub.blocks[0])
- String expressions may be useful for paths and stuff ("./disk")
2025-07-04 18:55:46 -05:00

137 lines
3.5 KiB
Python

#
# Hooks for gdb:
# (gdb) source ./scripts/dbg.gdb.py
#
#
import shlex
# split spaces but only outside of parens and quotes
def gdbsplit(v):
parens = 0
quote = None
escape = False
i_ = 0
for i in range(len(v)):
if v[i].isspace() and not parens and not quote:
v_ = v[i_:i].strip()
if v_:
yield v_
i_ = i+1
elif quote:
if escape:
escape = False
elif v[i] == quote:
quote = None
elif v[i] == '\\':
escape = True
elif v[i] in '\'"':
quote = v[i]
elif v[i] in '([{':
parens += 1
elif v[i] in '}])':
parens -= 1
v_ = v[i_:].strip()
if v_:
yield v_
# common wrapper for dbg scripts
#
# Note some tricks to help interact with bash and gdb:
#
# - Flags are passed as is (-b4096, -t, --trunk)
# - All non-flags are parsed as expressions (file->b.shrub.blocks[0])
# - String expressions may be useful for paths and stuff ("./disk")
#
class DbgCommand(gdb.Command):
"""A littlefs debug script. See -h/--help for more info."""
name = None
path = None
def __init__(self):
super().__init__(self.name,
gdb.COMMAND_DATA,
gdb.COMPLETE_EXPRESSION)
def invoke(self, args, *_):
# parse args
args = list(gdbsplit(args))
args_ = []
for a in args:
# pass flags as is
if a.startswith('-'):
args_.append(a)
# parse and eval
else:
try:
v = gdb.parse_and_eval(a)
t = v.type.strip_typedefs()
if t.code in {
gdb.TYPE_CODE_ENUM,
gdb.TYPE_CODE_FLAGS,
gdb.TYPE_CODE_INT,
gdb.TYPE_CODE_RANGE,
gdb.TYPE_CODE_CHAR,
gdb.TYPE_CODE_BOOL}:
v = str(int(v))
elif t.code in {
gdb.TYPE_CODE_FLT}:
v = str(float(v))
else:
try:
v = v.string('utf8')
except gdb.error:
raise gdb.GdbError('Unexpected type: %s' % v.type)
except gdb.error as e:
raise gdb.GdbError(e)
args_.append(shlex.quote(v))
args = args_
# execute
gdb.execute(' '.join(['!'+self.path, *args]))
# at some point this was manual, then I realized I could just glob all
# scripts with this prefix
#
# # dbgerr
# class DbgErr(DbgCommand):
# name = 'dbgerr'
# path = './scripts/dbgerr.py'
#
# # dbgflags
# class DbgFlags(DbgCommand):
# name = 'dbgflags'
# path = './scripts/dbgflags.py'
#
# # dbgtag
# class DbgTag(DbgCommand):
# name = 'dbgtag'
# path = './scripts/dbgtag.py'
import os
import glob
for path in glob.glob(os.path.join(
os.path.dirname(__file__),
'dbg*.py')):
if path == __file__:
continue
# create dbg class
name = os.path.splitext(os.path.basename(path))[0]
type(name, (DbgCommand,), {
'name': name,
'path': path
})
# initialize gdb hooks
for Dbg in DbgCommand.__subclasses__():
if Dbg.__doc__ is None:
Dbg.__doc__ = DbgCommand.__doc__
Dbg()