Skip to content

Instantly share code, notes, and snippets.

@jimblandy
Last active April 21, 2024 15:09
Show Gist options
  • Save jimblandy/6f1cfce4c1da1ddd7c6babdb42e75c22 to your computer and use it in GitHub Desktop.
Save jimblandy/6f1cfce4c1da1ddd7c6babdb42e75c22 to your computer and use it in GitHub Desktop.
GDB Python script to define a command for inspecting types.
# Put this in a file BLAH/dump-type.py and then put this in your ~/.gdbinit file
# (or type it at the GDB prompt):
#
# source BLAH/dump-type.py
#
# Then you can say:
#
# (gdb) help jimb-dump-type
#
# and you should get a help message.
import gdb
class DumpTypeCommand(gdb.Command):
"""Display the structure of a type in detail.
Usage: jimb-dump-type[/FLAGS] TYPE
Available FLAGS are:
/p Follow pointers: show structure of the pointed-to type.
/E Don't show element structure of heap-allocated types like
vectors and maps. Useful when you're more interested in the
size or layout of the struct itself.
/d Show implementation details of boring types like
Maybe, nsTArray, etc. rather than just jumping to
their interesting contents, like the element type.
/s Include static struct members."""
def __init__(self):
super (DumpTypeCommand, self).__init__("jimb-dump-type", gdb.COMMAND_USER)
def complete(self, text, word):
return gdb.COMPLETE_SYMBOL
def invoke(self, arg, from_tty):
argv = gdb.string_to_argv(arg)
flags = {}
if len(argv) >= 1 and argv[0][:1] == '/':
for flag in argv[0][1:]:
if flag == 'p':
flags['follow_pointers'] = True
elif flag == 'd':
flags['details'] = True
elif flag == 's':
flags['show_static'] = True
elif flag == 'E':
flags['hide_owned_heap'] = True
else:
raise gdb.GdbError("unrecognized flag: {!r}".format(flag))
argv = argv[1:]
if len(argv) != 1:
raise gdb.GdbError("jimb-dump-type takes a type name as an argument.")
try:
typ = gdb.lookup_type(argv[0])
except gdb.error as e:
print str(e)
return
dump_type(typ, flags, set())
def dump_type(typ, flags, seen, indent=''):
def nest(t):
dump_type(t, flags, seen, indent + ' ')
def summary_only(t):
sflags = flags.copy()
sflags['summary-only'] = True
dump_type(t, sflags, seen, indent + ' ')
cn = code_name(typ.code)
summary = indent + cn + ' '
if typ.name and typ.name != cn:
summary += "name: {!r} ".format(typ.name)
if typ.tag and typ.tag != typ.name:
summary += "tag: {!r} ".format(typ.tag)
summary += '{}B'.format(typ.sizeof)
print summary
if 'summary-only' in flags:
return
pointer_target = is_pointer(typ, flags)
if pointer_target:
if 'follow_pointers' in flags:
nest(pointer_target)
else:
summary_only(pointer_target)
return
container_params = is_container(typ, flags)
if container_params:
if 'hide_owned_heap' in flags:
# like pointer
for param in container_params:
summary_only(param)
else:
for param in container_params:
nest(param)
return
if typ.code == gdb.TYPE_CODE_ARRAY:
nest(typ.target())
elif typ.code == gdb.TYPE_CODE_TYPEDEF:
if typ.name in seen:
return
seen.add(typ.name)
nest(typ.target())
elif typ.code == gdb.TYPE_CODE_STRUCT or typ.code == gdb.TYPE_CODE_UNION:
if typ.name in seen:
return
seen.add(typ.name)
def try_shortcuts():
if 'details' in flags:
return False
if not typ.name:
return False
if typ.name.startswith('mozilla::Maybe<'):
nest(typ.template_argument(0))
elif typ.name.startswith('mozilla::Atomic<'):
nest(typ.template_argument(0))
else:
return False
return True
if not try_shortcuts():
indent += ' '
for field in typ.fields():
# Omit static data members. These have no bit position.
if not hasattr(field, 'bitpos') and 'show_static' not in flags:
continue
summary = indent
if field.is_base_class:
summary += 'base class, '
if field.name:
summary += "name: {!r} ".format(field.name)
if field.bitsize != 0:
if field.bitsize > 1:
summary += "bitfield: {}..{}".format(
field.bitpos, field.bitpos + field.bitsize - 1)
else:
summary += "bitfield: {}".format(field.bitpos)
print(summary)
nest(field.type)
code_names = {
gdb.TYPE_CODE_PTR: "pointer",
gdb.TYPE_CODE_REF: "reference",
gdb.TYPE_CODE_RVALUE_REF: "rvalue reference",
gdb.TYPE_CODE_ARRAY: "array",
gdb.TYPE_CODE_TYPEDEF: "typedef",
gdb.TYPE_CODE_STRUCT: "struct",
gdb.TYPE_CODE_UNION: "union",
gdb.TYPE_CODE_ENUM: "enum",
gdb.TYPE_CODE_FLAGS: "bitflags",
gdb.TYPE_CODE_FUNC: "func",
gdb.TYPE_CODE_INT: "int",
gdb.TYPE_CODE_FLT: "flt",
gdb.TYPE_CODE_VOID: "void",
gdb.TYPE_CODE_SET: "set",
gdb.TYPE_CODE_RANGE: "range",
gdb.TYPE_CODE_STRING: "string",
gdb.TYPE_CODE_BITSTRING: "bitstring",
gdb.TYPE_CODE_ERROR: "<erroneous type>",
gdb.TYPE_CODE_METHOD: "method (C++)",
gdb.TYPE_CODE_METHODPTR: "pointer to member function (C++)",
gdb.TYPE_CODE_MEMBERPTR: "pointer to member (C++)",
gdb.TYPE_CODE_CHAR: "character",
gdb.TYPE_CODE_BOOL: "boolean",
gdb.TYPE_CODE_COMPLEX: "complex float",
gdb.TYPE_CODE_TYPEDEF: "typedef",
gdb.TYPE_CODE_NAMESPACE: "namespace (C++)",
gdb.TYPE_CODE_DECFLOAT: "decimal float",
gdb.TYPE_CODE_INTERNAL_FUNCTION: "internal function"
}
def code_name(code):
return code_names.get(code, "<unexpected type code>")
def is_pointer(typ, flags):
if 'details' in flags:
return None
if (typ.code == gdb.TYPE_CODE_PTR or typ.code == gdb.TYPE_CODE_REF):
return typ.target()
if (typ.code == gdb.TYPE_CODE_STRUCT
and typ.name
and (typ.name.startswith('RefPtr<')
or typ.name.startswith('nsCOMPtr<')
or typ.name.startswith('mozilla::UniquePtr<'))):
return typ.template_argument(0)
return None
def is_container(typ, flags):
if 'details' in flags:
return None
if typ.code != gdb.TYPE_CODE_STRUCT:
return None
if not typ.name:
return None
if (typ.name.startswith('nsTArray<') or
typ.name.startswith('AutoTArray<') or
typ.name.startswith('mozilla::SmallPointerArray<') or
typ.name.startswith('std::vector<') or
typ.name.startswith('std::stack<') or
typ.name.startswith('std::deque<') or
typ.name.startswith('std::unordered_set<')):
return [typ.template_argument(0)]
if (typ.name.startswith('std::unordered_map<') or
typ.name.startswith('std::map<') or
typ.name.startswith('nsRefPtrHashtable<')):
return [typ.template_argument(0),
typ.template_argument(1)]
if typ.name.startswith('mozilla::Variant<'):
return template_arguments(typ)
return None
def template_arguments(typ):
args = []
while True:
try:
args.append(typ.template_argument(len(args)))
except:
break
return args
DumpTypeCommand()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment