从 gdb 调用派生类型的 fortran 子例程

Calling fortran subroutines of derived types from gdb

我想调用一个函数,该函数将 Gnu 调试器 (gdb) 中的派生类型作为参数。我最终遇到了段错误。这是一个“最小”工作示例:

module modSimpleClass
    type Person
        character(len=20) :: Name
        contains
            procedure PrintName
    end type
    contains
        subroutine PrintName(a)
            class(Person) :: a
            print *, a%Name
        end subroutine
        subroutine Print42()
            print *, "42"
        end subroutine
        subroutine PrintNameFromOutside(a)
            class(Person) :: a
            print *, a%Name
        end subroutine
end module
program testgdb
    use modSimpleClass
    implicit none
    type(Person) :: JoeJohnson

    JoeJohnson%Name = "Joe Johnson"
    call JoeJohnson%PrintName
end program testgdb

我用 gfortran 版本 9.3.0 编译它,运行 从 gdb 编译它并在结束前停止它。 以下内容有效:

call Print42()
print JoeJohnson%Name

以下内容不起作用:

call JoeJohnson%PrintName()!There is no member named PrintName.
call PrintName(JoeJohnson)!Error then segfault
call PrintNameFromOutside(JoeJohnson)!Error then segfault

错误如下:

The program being debugged was signaled while in a function called from GDB. GDB remains in the frame where the signal was received. To change this behavior use "set unwindonsignal on". Evaluation of the expression containing the function (modsimpleclass::printname) will be abandoned. When the function is done executing, GDB will silently stop.

如何从gdb正确调用fortran子程序?


感谢@Flusslauf 的回复。我将尝试重新表述您所说的话:

从 gfortran 的角度来看,如果我们在模块 modsimpleclass 中定义派生类型 Person

所以函数实际上为派生类型的每个对象使用两个指针:一个指向对象 另一个指向有关对象的信息。

在 gdb 中,将指向对象的指针传递给函数, 但是传递给它的对象本身不起作用。


@Flusslauf 成立

call PrintName( &JoeJohnson )!works because it is equivalent to
call PrintName( a%_data )!this, which works too
call PrintName( a )!this works and it makes sense
call PrintName( a%_data, a%_vptr )!this also works
call PrintName( a%_vptr)!this outputs something strange

call PrintName( *(a%_data) )!causes segfault since equivalent to
call PrintName( JoeJohnson )!Error then segfault

,其中每条指令都在变量的正确范围内执行。

这里的问题是,虽然正常的 Fortran 语法允许调用 PrintName(JoeJohnson) 传递参数的实际类型似乎是(在 printname 中设置断点并编译 gfortran)

Breakpoint 1, modsimpleclass::printname (a=...) at ./stack.f90:10
10                  print *, a%Name
(gdb) ptype a
type = Type __class_modsimpleclass_Person_t
    PTR TO -> ( Type person :: _data )
    PTR TO -> ( Type __vtype_modsimpleclass_Person :: _vptr )
End Type __class_modsimpleclass_Person_t

JoeJohnson的类型是非指针类型。我猜上面的类型是 gfortran 在 Fortran 中传递类型参数的方式。

在上面的例子中

PrintName(&JoeJohnson)

可以工作,但是很麻烦..


编辑:

只是为了完成 - ifx/ifort 实际上似乎使用了一个指针来传递参数

(gdb) ptype a
type = PTR TO -> ( Type person
character*20 :: name
    void :: printname(void)
End Type person )

这样做 call modsimpleclass::printname(&JoeJohnson) 将不再那么笨拙(GDB 在使用 ifx/ifort 编译时无法解析 PrintName)。我不确定是否可以以一种有意义的方式调整 gdb 以允许这种调用。由于参数处理和调用语法依赖于编译器。