检测未初始化的阵列 [英] Detecting uninitialized arrays

查看:173
本文介绍了检测未初始化的阵列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  module mymod 
implicit none
包含

子程序set_int_array(a)
整数,意图(出):: a(:)
整数k
! a(:) = 10! (1)

do k = 1,size(a)
a(k)= a(k)+ k
enddo
结束子程序

子程序set_real_array(r)
实数,意图(out):: r(:)
整数k
! r(:) = 10.0 !! (2)

do k = 1,size(r)
r(k)= r(k)+ k
enddo
结束子程序

end模块

程序main
使用mymod
隐式无
integer :: a(5)
real :: r(5)

调用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 

编程接收信号8(SIGFPE):浮点异常。

Backtrace为这个错误:
#0 0x00000039becac5f4在等待()从/lib64/libc.so.6
#1 0x00000039c501400d在?? ()从/usr/lib64/libgfortran.so.3
#2 0x00000039c501582e在?? ()from /usr/lib64/libgfortran.so.3
#3 0x00000039c50146ca in ?? ()从/usr/lib64/libgfortran.so.3
#4<信号处理程序调用>
#5 0x0000000000400bd3 in __mymod_MOD_set_real_array()
#6 MAIN__()中的0x0000000000400e69()
#7 0x0000000000400f52在main()中

因此,我还尝试了ifort14.0,并提供以下选项:

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

但都未能检测错误。事实上,检查uninit 选项的手册页显示为


只有本地


然后它可以包含一个标量的内部类型为INTEGER,REAL,COMPLEX和LOGICAL的变量很自然,错误没有被发现。所以我的问题是:是否有任何方法来捕获 a(:) r(:)自动?




我对这个问题的动机来自最近的一篇文章,在这里OP很难找到一个bug。原来问题来自 evaluate 模块中 curve_derivs_alg1()中未初始化数组的使用,但是,我也很难找到错误的确切位置(尽管找到它可能看起来微不足道!)。在这种情况下, -finit-real-snan -ffpe-trap = invalid 选项有效,但如果整数数组成为问题,它应该变得更加困难......所以,我想知道是否有一些方便的选项或方法来跟踪未初始化数组的使用的第一个位置。

解决方案

如果你是在Linux上,您可以使用 valgrind 来执行此任务。如果您发行

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

您将获得大量输出;-)第一个警告实际上指向未初始化的变量:

  == 4426 == Memcheck,一个内存错误检测器
== 4426 == Julian Seward版权所有(C)2002-2015和GNU GPL'd等人。
== 4426 ==使用Valgrind-3.11.0和LibVEX;使用-h获取版权信息
== 4426 ==命令:./a.out
== 4426 ==
== 4426 ==条件跳转或移动取决于未初始化的值( s)
== 4426 ==在0x4F29774:??? (在/usr/lib64/libgfortran.so.3.0.0)
== 4426 == by 0x4F2B2DE:??? (在/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 ==未初始化的值是由堆栈分配创建的
== 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 ==条件跳转或移动取决于未初始化的VA lue(s)
== 4426 ==在0x4F27BC1:??? (在/usr/lib64/libgfortran.so.3.0.0中)
== 4426 == by 0x4F2A6E4:??? (在/usr/lib64/libgfortran.so.3.0.0)
== 4426 == by 0x4F2B29E:??? (在/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 ==未初始化的值是由堆栈分配创建的
== 4426 == at 0x4008CD:MAIN__(test.F90:27)

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


In the following program, the two routines set_int_array and set_real_array define some values to dummy arrays, but suppose that the initialization of a(:) and r(:) (Lines 1 and 2) is missing inadvertently (i.e., both lines should be uncommented in the correct program). My question is how to detect these errors automatically with some compiler option(s).

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

To do so, I first tried the following option for gfortran4.8 (on Linux x86_64):

gfortran -Wall -fcheck=all test.f90

but this failed to detect the missing initialization and gave the incorrect result

           1           2   268435459       32730         207
   1.0000000       2.0000000       3.0000000       4.0000000       5.0000000

So I tried the following option to initialize r(:) with NaN:

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

which successfully caught the error for set_real_array, but not for 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 ()

As such, I also tried ifort14.0 with the following options

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

but both failed to detect the errors. Indeed, the man page for check uninit option says

Only local scalar variables of intrinsic type INTEGER, REAL, COMPLEX, and LOGICAL without the SAVE attribute are checked.

then it may be natural that the errors are not detected. So my question is: Is there any method to catch both the errors for a(:) and r(:) automatically?


[ Edit ] My motivation for this question comes from a recent post on SO, where the OP had a hard time for finding a bug. It turned out that the problem comes from the use of an uninitialized array in curve_derivs_alg1() in the evaluate module, but it was very tough for me also to find the precise location of the bug (although it may appear trivial once it is found!). The -finit-real-snan -ffpe-trap=invalid option works in this case, but it should become much more difficult if integer arrays were the problem... So I was wondering if there is some convenient options or ways to track the first location of the use of uninitialized arrays.

解决方案

If you are on Linux, you could use valgrind for this task. If you issue

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

you will get a lot of output ;-) The first warning actually points towards the uninitialized variable:

==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)

To better analyze the output, I prefer to have one statement per line, so I changed the last lines of your program to

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

Then, test.F90:34 points towards

    print *, a(:)

Later on, you will find the following output which points towards the second use of an uninitialized variable:

==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)

Note that you will need to compile your code with debug options to get the line numbers. With valgrind it is also beneficial to correct one error after the other, starting from the first. This is because undefined behavior might cause subsequent errors/warnings that are gone once you fix the cause of the problem.

这篇关于检测未初始化的阵列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆