【问题标题】:Detecting uninitialized arrays检测未初始化的数组
【发布时间】:2015-11-30 03:32:10
【问题描述】:

在下面的程序中,set_int_arrayset_real_array 两个例程为虚拟数组定义了一些值,但假设 a(:)r(:) 的初始化(第 1 和 2 行)无意中丢失(即,这两行都应该在正确的程序中取消注释)。我的问题是如何使用一些编译器选项自动检测这些错误。

module mymod
implicit none
contains

subroutine set_int_array ( a )
    integer, intent(out) :: a(:)
    integer k
    ! a(:) = 10                              !! (1)

    do k = 1, size(a)
        a(k) = a(k) + k
    enddo
end subroutine

subroutine set_real_array ( r )
    real, intent(out) :: r(:)
    integer k
    ! r(:) = 10.0                            !! (2)

    do k = 1, size(r)
        r(k) = r(k) + k
    enddo
end subroutine

end module

program main
    use mymod
    implicit none
    integer :: a(5)
    real    :: r(5)

    call set_int_array  ( a ) ; print *, a(:)
    call set_real_array ( r ) ; print *, r(:)
end program

为此,我首先尝试了 gfortran4.8(在 Linux x86_64 上)的以下选项:

gfortran -Wall -fcheck=all test.f90

但这未能检测到丢失的初始化并给出了不正确的结果

           1           2   268435459       32730         207
   1.0000000       2.0000000       3.0000000       4.0000000       5.0000000

所以我尝试了以下选项来用NaN 初始化r(:)

gfortran -finit-real=snan -ffpe-trap=invalid test.f90

成功捕获了set_real_array 的错误,但没有捕获set_int_array 的错误

 -1098847551          59 -1034862589       32608        7941

Program received signal 8 (SIGFPE): Floating-point exception.

Backtrace for this error:
#0  0x00000039becac5f4 in wait () from /lib64/libc.so.6
#1  0x00000039c501400d in ?? () from /usr/lib64/libgfortran.so.3
#2  0x00000039c501582e in ?? () from /usr/lib64/libgfortran.so.3
#3  0x00000039c50146ca in ?? () from /usr/lib64/libgfortran.so.3
#4  <signal handler called>
#5  0x0000000000400bd3 in __mymod_MOD_set_real_array ()
#6  0x0000000000400e69 in MAIN__ ()
#7  0x0000000000400f52 in main ()

因此,我也尝试了带有以下选项的 ifort14.0

ifort -check all test.f90
ifort -check uninit test.f90

但两者都未能检测到错误。事实上,check uninit 选项的手册页说

仅检查不带 SAVE 属性的内在类型 INTEGER、REAL、COMPLEX 和 LOGICAL 的局部 标量变量。

那么可能很自然地没有检测到错误。所以我的问题是:有什么方法可以自动捕获a(:)r(:) 的错误?


[ 编辑 ] 我提出这个问题的动机来自 SO 上的a recent post,OP 很难找到错误。原来问题出在evaluate 模块中curve_derivs_alg1() 中使用了未初始化的数组,但我也很难找到错误的精确位置(尽管它可能看起来微不足道)成立!)。 -finit-real-snan -ffpe-trap=invalid 选项在这种情况下有效,但如果整数数组是问题,它应该会变得更加困难......所以我想知道是否有一些方便的选项或方法来跟踪使用未初始化数组的第一个位置。

【问题讨论】:

  • 我这里没有资源来写答案,但我怀疑a(:) 不是(在可执行语句中)数组而是数组部分是相关的。 区别。差异的一个影响是,可以在赋值的 lhs 上对数组执行自动分配,但对 lhs 上的数组部分无效。
  • @HighPerformanceMark 您好,非常感谢您的评论。如果我在问题中的解释具有误导性,我很抱歉,但我没有尝试在两个例程中使用可分配的虚拟数组。它们只是从主程序(其中它们被声明为局部数组)传递过来的。在这里,我正在寻找一种方便的方法来找到使用未初始化数组的第一个位置(请参阅上面的编辑)。

标签: compiler-errors initialization fortran runtime-error


【解决方案1】:

如果您使用的是 Linux,则可以使用 valgrind 来完成此任务。如果你发出

valgrind --track-origins=yes ./a.out

你会得到很多输出 ;-) 第一个警告实际上指向未初始化的变量:

==4426== Memcheck, a memory error detector
==4426== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4426== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==4426== Command: ./a.out
==4426== 
==4426== Conditional jump or move depends on uninitialised value(s)
==4426==    at 0x4F29774: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426==    by 0x4F2B2DE: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426==    by 0x4F1F126: _gfortran_transfer_array (in /usr/lib64/libgfortran.so.3.0.0)
==4426==    by 0x40098B: MAIN__ (test.F90:34)
==4426==    by 0x400A75: main (test.F90:28)
==4426==  Uninitialised value was created by a stack allocation
==4426==    at 0x4008CD: MAIN__ (test.F90:27)

为了更好地分析输出,我更喜欢每行一个语句,所以我将程序的最后几行更改为

    call set_int_array  ( a )
    print *, a(:)
    call set_real_array ( r )
    print *, r(:)

然后,test.F90:34 指向

    print *, a(:)

稍后,您会发现以下输出指向未初始化变量的第二次使用:

==4426== Conditional jump or move depends on uninitialised value(s)
==4426==    at 0x4F27BC1: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426==    by 0x4F2A6E4: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426==    by 0x4F2B29E: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426==    by 0x4F1F126: _gfortran_transfer_array (in /usr/lib64/libgfortran.so.3.0.0)
==4426==    by 0x400A44: MAIN__ (test.F90:36)
==4426==    by 0x400A75: main (test.F90:28)
==4426==  Uninitialised value was created by a stack allocation
==4426==    at 0x4008CD: MAIN__ (test.F90:27)

请注意,您需要使用调试选项编译代码才能获取行号。使用valgrind,从第一个开始,一个接一个地纠正一个错误也是有益的。这是因为未定义的行为可能会导致后续错误/警告在您解决问题原因后消失。

【讨论】:

  • 非常感谢您的信息。到目前为止,我对 valgrind 的经验很少(只用过几次来检查一些有缺陷的 C 实用程序的内存泄漏),所以我从未将它用于此目的。是的,在 Fortran 中显示未初始化数组的存在似乎非常有用:)
  • 所以我对this problem进行了实验。我昨天在 SO 上解决了这个问题,并且意外地遇到了很难找到错误的过程。我现在刚刚为这段代码尝试了 valgrind,它给出了大量的错误,但 only 在主程序中未初始化值的打印语句之后。因此,我能够知道__evaluate_MOD_rat_curve_derivs 中存在一个错误,但没有进一步深入到特定的较低子程序......(续)
  • 我想追踪这种错误(首先使用了未初始化的变量),所以如果存在这种技术,我真的很感激任何提示。 (或者如果有必要我会把它变成一个问题,虽然我不知道如何提出一个好的问题......)但无论如何,非常感谢你的信息:)
  • @roygvib 到目前为止,我还没有找到一种方法来找到它们,将它们全部带走并在黑暗中捆绑它们。它通常是工具的组合...我的默认编译选项中有-finit-real=snan -ffpe-trap=invalid(和其他一些),如果它们没有表示错误,我会使用 valgrind 之类的工具。
  • 嗯,我明白了...很高兴知道 -finit-real... 选项的使用是合理的。我将检查 valgrind 的更多选项并尝试习惯使用。谢谢:)
【解决方案2】:

如果检测此类错误对您的工作很重要,您可能需要购买可以在运行时报告此类问题的 NAG 编译器。例如,如果我将您的原始代码保存在 'test.f90' 中并编译它:

nagfor test.f90 -C=undefined -o test.exe

在运行时我得到以下信息:

Runtime Error: test.f90, line 11: Reference to undefined variable A(K)
Program terminated by fatal error
Aborted (core dumped)

如果我取消注释并修复第一个错误,则会报告第二个错误:

11 12 13 14 15
Runtime Error: test.f90, line 21: Reference to undefined variable R(K)
Program terminated by fatal error
Aborted (core dumped)

编译器通常不会执行这种检查,因为它可能很昂贵。也有一些限制。更多信息请访问http://www.nag.co.uk/nagware/np/r60_doc/nagfor.html#UNDEF

请注意,答案来自 NAG 人员。上面的链接提供了有关使用 '-C=undefined' 标志的限制信息,该标志与此问题没有直接关系,但可能有用。

【讨论】:

  • 感谢您的建议。编辑了答案。
  • 感谢@macelee,NAG fortran 在语法检查方面似乎非常严格。我会尝试看看我是否可以以某种方式使用它。谢谢:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-03
  • 2022-08-12
  • 1970-01-01
  • 2012-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多