A gdb pretty printer for Fortran dynamic types
This is currently only "proof of concept", and is by no means
guaranteed to work. It works for me with gfortran 7.2 and gdb 8.0.1, and the very small number of programs I've tried.
Start gdb, then:
python exec(open("fortran_printer.py").read())
Polymorphic objects don't get printed nicely in gdb. Take the program
gdb_mvce.f90 with a breakpoint on line 46. Printing
alloc_ext gives:
(gdb) p alloc_ext1
$1 = ( _data = 0x606260, _vptr = 0x400da0
<__foo_module_MOD___vtab_foo_module_My_extended_type> )
which is not very helpful. In order to see the actual value of
alloc_ext, we need to do
(gdb) p *(my_extended_type*)(alloc_ext1%_data)
$2 = ( my_base_type = ( base_char = 'base' ), extended_char = 'ext ' )
because gdb thinks alloc_ext1%_data is a pointer to a my_base_type
and not a my_extended_type. This quickly becomes frustrating if your
type has polymorphic components.
With Fortran-gdb-pp, we instead get:
(gdb) p alloc_ext1
$3 = ( my_base_type = ( base_char = 'base' ), extended_char = 'ext ' )
Unfortunately, we have to work around several limitations of the gdb
python API. Firstly, gdb reports the dynamic_type of a polymorphic
variable as being its base type, and not its actual dynamic type! This
means we need some other way of getting its dynamic type. Luckily, the
symbol for the _vptr component (at least with gfortran 7.2) contains
the dynamic type, so we can use this. Briefly, we do the following:
- Look up symbol for the value's
_vptr - Parse the symbol to get the dynamic type
- Cast the
_datacomponent to a pointer to the dynamic type and dereference
For 1., we need to get the _vptr symbol. We can do this
in gdb with info symbol foo%_vptr. The python API lacks such a
function, so instead we do:
gdb.execute("info symbol {:#x}".format(int(val['_vptr'])))
int(val['_vptr']) gets the address of _vptr
Next, we need to parse the symbol. With gfortran 7.2, _vptr symbols
look like either:
__<module name>_MOD___vtab_<module name>_<Dynamic type>for types defined in modules, or__vab_<program name>_<Dynamic type>.nnnnfor types defined in programs
Module and program names can contain underscores, but luckily the type starts with a capital letter while everything else is in lower case.
Lastly, we need to actually print the _data component as the dynamic
type. While the python API does provide a Value.cast(type) method,
the type argument must be a gdb.Type object. No matter, we can use
the gdb.lookup_type(name) function... except that this doesn't work
with Fortran types. This time, we fallback to using
gdb.parse_and_eval:
cast_string = "*({type}*)({address:#x})".format(
type=real_type, address=int(val['_data']))
real_val = gdb.parse_and_eval(cast_string)
where real_type is a string containing the dynamic type. This
basically executes *(<dynamic type>)(value%_data) and then we can
pass the resulting value to a pretty printer that just returns
str(val), i.e. like the default printer.
- printing allocatable variables before they've been allocated will crash gdb
- accessing components of dynamic types is still annoying, as you
still need to go through the intermediate
_datacomponent, which gdb thinks is a pointer to a base type object