首页 文章

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

如果派生类型包含,例如,其他多态派生类型的数组,这很快就会变得非常痛苦 . 我似乎 grab 了实际的动态类型,甚至是 _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一样)包含动态类型,因此我们可以使用它 . 简而言之,我们执行以下操作:

    • 查找值的符号 _vptr

    • 解析符号以获取动态类型

    • _data 组件转换为指向动态类型和取消引用的指针

    对于1.,我们需要获得 _vptr 符号 . 我们可以使用 info symbol foo%_vptr 在gdb中执行此操作 . 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) 的漂亮打印机,即默认打印机 .

相关问题