在gdb中漂亮地显示Fortran动态类型

5

在gdb中打印Fortran可分配的多态变量的值非常痛苦。给定以下程序,为了查看alloc_ext的值,我必须执行以下操作:

(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 ' )

这在派生类型中包含其他多态派生类型的数组时会变得非常痛苦。我尝试调查Python漂亮打印API,但似乎仍然无法获得实际动态类型,甚至无法获取_vptr地址上的标签,这足以打印出某些信息。 我正在使用gdb 8.0.1和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
1个回答

3
我已经制作了一个概念验证,使这个过程更加美好:https://github.com/ZedThree/Fortran-gdb-pp。它不是完美的,但它确实展示了一种打印动态类型并查看其实际值而不仅仅是_data_vptr组件的方法。我在下面包含了自README的解释。

它是如何工作的?

不幸的是,我们必须绕过gdb python API的几个限制。首先,gdb报告多态变量的dynamic_type为其基本类型,而不是其实际动态类型!这意味着我们需要其他方法来获取其动态类型。幸运的是,_vptr组件的符号(至少使用gfortran 7.2)包含动态类型,因此我们可以使用它。简要地说,我们执行以下操作:

  1. 查找值的_vptr符号
  2. 解析符号以获取动态类型
  3. _data组件强制转换为指向动态类型的指针并取消引用

对于1,我们需要获取_vptr符号。我们可以在gdb中使用info symbol foo%_vptr。python API缺少这样的函数,因此我们执行以下操作:

gdb.execute("info symbol {:#x}".format(int(val['_vptr'])))

int(val['_vptr'])获取了_vptr的地址。

接下来,我们需要解析符号。在gfortran 7.2中,_vptr符号的格式如下:

  • 对于定义在模块中的类型:__<module name>_MOD___vtab_<module name>_<Dynamic type>
  • 对于定义在程序中的类型:__vab_<program name>_<Dynamic type>.nnnn

模块名和程序名可能包含下划线,但幸运的是类型名称以大写字母开头,所有其他内容均为小写字母。

最后,我们需要将_data组件实际打印为动态类型。虽然Python API提供了Value.cast(type)方法,但type参数必须是gdb.Type对象。不过没关系,我们可以使用gdb.lookup_type(name)函数... 但是这在Fortran类型中不起作用。这时,我们回到使用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)

其中real_type是包含动态类型的字符串。这基本上执行了*(<dynamic type>)(value%_data),然后我们可以将结果值传递给一个漂亮的打印机,它只返回str(val),即像默认打印机一样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接