Printing the values of Fortran allocatable polymorphic variables in gdb is very painful. Given the program below, in order to see the value of alloc_ext
, I have to do the following:
(gdb) p alloc_ext
$1 = ( _data = 0x606260, _vptr = 0x400ce0 <__foo_MOD___vtab_foo_My_extended_type> )
(gdb) ptype alloc_ext
type = Type __class_foo_My_base_type_a
PTR TO -> ( Type my_base_type :: _data)
PTR TO -> ( Type __vtype_foo_My_base_type :: _vptr)
End Type __class_foo_My_base_type_a
(gdb) ptype alloc_ext%_data
type = PTR TO -> ( Type my_base_type
character*4 :: base_char
End Type my_base_type )
(gdb) p alloc_ext%_data
$2 = (PTR TO -> ( Type my_base_type )) 0x606260
(gdb) p *(my_extended_type*)(alloc_ext%_data)
$3 = ( my_base_type = ( base_char = 'base' ), extended_char = 'ext ' )
This quickly gets very painful if a derived type contains, say, an array of other polymorphic derived types. I've tried investigating the python pretty printing API, but I still can't seem to get hold of the actual dynamic type, or even the label on the _vptr
address, which would be enough information to pretty print something.
I'm using gdb 8.0.1 and gfortran 7.2.1.
MVCE:
module foo
implicit none
type my_base_type
character(len=4) :: base_char = "base"
end type my_base_type
type, extends(my_base_type) :: my_extended_type
character(len=4) :: extended_char = "ext "
end type my_extended_type
contains
subroutine bar(arg)
class(my_base_type), intent(in) :: arg
print*, "breakpoint here"
select type(arg)
type is (my_base_type)
print*, "my_base_type ", arg%base_char
type is (my_extended_type)
print*, "my_extended_type ", arg%base_char, " ", arg%extended_char
end select
end subroutine bar
end module foo
program mvce
use foo
implicit none
type(my_base_type) :: base
type(my_extended_type) :: ext
class(my_base_type), allocatable :: alloc_base
class(my_base_type), allocatable :: alloc_ext
allocate(alloc_base, source=base)
allocate(alloc_ext, source=ext)
call bar(alloc_base)
call bar(alloc_ext)
end program mvce
I've made a proof-of-concept that makes this a bit nicer: https://github.com/ZedThree/Fortran-gdb-pp. It's not perfect, but it does demonstrate a way of at least printing dynamic types and see their actual values
instead of just the _data
and _vptr
components. I've included the explanation from the README below.
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:
_vptr
_data
component to a pointer to the dynamic type and
dereferenceFor 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>.nnnn
for types defined in
programsModule 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With