Detecting uninitialized arrays

空扰寡人 提交于 2019-12-02 05:17:11

问题


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.


回答1:


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.




回答2:


If detecting such error is important to your work, you might want to invest in the NAG compiler which can report such problem at runtime. For example, if I save your original code in 'test.f90' and compile it:

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

At runtime I get the following:

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

If I uncomment and fix the first error, the second one is reported as well:

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)

Compilers do not normally perform this kind of check because it can be expensive. There are some limitations as well. More information is available at http://www.nag.co.uk/nagware/np/r60_doc/nagfor.html#UNDEF

Note that the answer is from a NAG person. The above link gives information on limitations on the use of '-C=undefined' flag, which is not directly related to this question but may be useful.



来源:https://stackoverflow.com/questions/33978485/detecting-uninitialized-arrays

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!