Pretty print dynamic fortran type in gdb

Printing the values ​​of politically carried Fortran variables in gdb is very painful. Given the program below, 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 becomes very painful if the derived type contains, say, an array of other polymorphic derived types. I tried exploring the pretty printable python API's API, but I still can't get the actual dynamic type, or even a shortcut at _vptr , that would be enough to print something.

I am 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 
+5
source share
1 answer

I made a proof of concept, which makes it a little nicer: https://github.com/ZedThree/Fortran-gdb-pp . It is not ideal, but it demonstrates a way to at least print dynamic types and their actual values ​​and not just the _data and _vptr . I have included an explanation from README below.


How it works?

Unfortunately, we need to get around a few limitations of the gdb python API. First, gdb tells the dynamic_type polymorphic variable as its base type, not its actual dynamic type! This means that we need another way to get its dynamic type. Fortunately, the symbol for the _vptr component (at least with gfortran 7.2) contains a dynamic type, so we can use this. In short, we do the following:

  • Find the character for _vptr
  • Parse a character to get a dynamic type
  • Pass the _data component _data pointer to a dynamic type and dereference

For 1. we need to get the _vptr character. 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 _vptr

Then we need to parse the symbol. With the characters gfortran 7.2, _vptr look like this:

  • __<module name>_MOD___vtab_<module name>_<Dynamic type> for types defined in modules, or
  • __vab_<program name>_<Dynamic type>.nnnn for types defined in the program

The names of modules and programs can contain underscores, but, fortunately, the type begins with a capital letter, and everything else is lowercase.

Finally, we need to actually print the _data component as a dynamic type. Although the python API provides a Value.cast(type) method, the Value.cast(type) argument must be a gdb.Type object. No matter, we can use the gdb.lookup_type(name) function ... except that this does not work with Fortran types. This time we refuse to use 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 a dynamic type. This is basically done *(<dynamic type>)(value%_data) , and then we can pass the resulting value to a good printer, which simply returns str(val) , that is, as the default printer.

+2
source

Source: https://habr.com/ru/post/1273282/


All Articles