【问题标题】:Calling fortran subroutines of derived types from gdb从 gdb 调用派生类型的 fortran 子例程
【发布时间】:2021-12-15 09:24:55
【问题描述】:

我想调用一个函数,该函数将 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

错误如下:

正在调试的程序在从 GDB 调用的函数中发出信号。 GDB 保留在接收信号的帧中。 要更改此行为,请使用“set unwindonsignal on”。 评估包含函数的表达式 (modsimpleclass::printname) 将被放弃。 函数执行完毕后,GDB 会静默停止。

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


感谢您对@Flusslauf 的回复。我将尝试改写您所说的内容:

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

  • 只有初始化变量的作用域才能看到原始类型。
  • 任何作为参数的函数或子程序 该派生类型的对象看到类型__class_modsimpleclass_Person_t 带有属性:
    1. _data:指向对象的指针。
    2. _vptr:指向包含对象信息的指针。

因此,一个函数实际上为每个派生类型的对象获取两个指针:一个指向该对象 另一个指向对象的信息。

从 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

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

【问题讨论】:

  • 看起来 GDB不支持它,你可以在sourceware.org/bugzilla打开一个错误报告
  • 传递对象会使 gdb 将其解释为指针(因为这是这里所期望的),或者我认为是指针对。然后 GDB 尝试取消引用它并落在内存中不应该出现的地方,这会导致 SIGSEGV。
  • 一般我认为这个结论是正确的(对于这个版本的gfortran)。传递对象的指针仅有效,因为 _data 是实际传递的 __class_modsimpleclass_Person_t 对象的第一个成员,并且 _vptr(遗憾的是我不知道那是什么)没有被取消引用和使用,我猜。因为从技术上讲,没有人将指向 __class_modsimpleclass_Person_t 的地址交给函数。是否也会使用 _vptr 这个调用应该导致 SIGSEV。
  • @Flusslauf 我将我的编译器版本添加到问题中(gfortran 9.3.0),因为这取决于编译器。我还尝试在函数内部停止并进行不同的调用。他们中的大多数都工作并同意您以前的 cmets。如果你好奇的话,我会在我的问题中添加我尝试过的内容。

标签: segmentation-fault fortran gdb


【解决方案1】:

这里的问题是,虽然正常的 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)

会工作,但很hacky..


编辑:

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

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

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

【讨论】:

  • 人们常说 Fortran 通过引用传递所有参数。虽然不准确,但在正常情况下确实如此。指针也用于传递简单数字类型的普通参数。
  • 非常感谢您的回复@Flusslauf。我想我明白了一点。我想用我从你的回答中理解的内容写一篇长评论,但太长了。我把长评论放在我的问题的末尾。
  • 感谢您的编辑。我以前错过了这是依赖于编译器的。
  • 两个编译器都传递一个地址。 Fortran 通常是 pass-by-reference
  • @MehdiSlimani 我错过了这里的多态性。我刚刚确认,在简单的子例程中,传递派生类型的代码可以很好地协同工作,即使部分是使用 ifort 编译的,部分是由 gfortran 编译的。当然,不能使用模块。但是多态性,这意味着class,确实可以以不同的方式实现。并且肯定会依赖于编译器。
猜你喜欢
  • 1970-01-01
  • 2012-10-23
  • 2019-03-02
  • 1970-01-01
  • 1970-01-01
  • 2015-11-18
  • 1970-01-01
  • 2013-10-07
  • 1970-01-01
相关资源
最近更新 更多