valgrind

Valgrind is tool to analyze the memory on an application at runtime. For instance, It helps in the discovery memory issues. If it is not the case then random errors and/or behaviors might start showing up.

It can be the case when working with pointers as they allow to do several tricks. Which it could easily confuse any programmer at some point.

Furthermore, It is also possible to introduce inconsistencies when calling subroutines, where the indices given for the explicit shape arrays might not match its original dimension. Often, It is more likely to happen when there are too many arguments to pass to a method. More likely due to a potential distraction from the programmer. Check below for a simple example :

subroutine do_something(a,b, data)
  integer, intent(in): a,b
  real, intent(in) :: data(a,b) !< this is called an explicit shape array
  data = data / 2
end subroutine do_something

And then:

real :: temp(2,5)
call do_something(2,3,temp) !< Not consistent 2-3 vs 2-5 shapes

This piece of code will be compiled and then ran. And such issue could go silent for a while until something might break the memory at run time. It is good to keep in mind, explicit shape arrays are meant to work like this.

If you take such example into a large program, it might be challenging to find the problem. That’s why tools like Valgrind exists.

First step towards running Valgrind is to compile the program in debug mode.

Ultimately, run the binary with the following options:

valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all --log-file=val.txt ./yourbinary
  • –leak-check=full
    • Search for memory leaks at the end
  • –track-origins=yes
    • show origins of undefined values
  • –show-leak-kinds=all
    • type of leak to show
  • –log-file=
    • Place output into a file

The example below is a simplified program where the given argument to the calc_ni subroutine is wrong on purpose. As expected, Valgrind detects a problem.

program corruption

 implicit none

 integer, parameter :: sz = 5
 real, allocatable, dimension(:) :: ni 
 real, dimension(sz) :: ni_heap

 allocate(ni(sz))

 call calc_ni(sz+1, ni)         !< complain
 call calc_ni(sz+1, ni_heap)    !< no complain

 deallocate(ni)
 contains

subroutine calc_ni(ndim, ni)
  integer, intent(in) :: ndim
  real, intent(out) :: ni(ndim) !< explicit shaped array
  ni = 25 !< Something is wrong here
end subroutine calc_ni

end program corruption

However, in case of an allocated array ni, Valgrind is capable of detecting the issue. On the other hand, It is not the case for the explicitly defined array ni_heap.

The output from Valgrind:

==108285== Memcheck, a memory error detector
==108285== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==108285== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==108285== Command: ./corruption
==108285== Parent PID: 108278
==108285== 
==108285== Invalid write of size 4
==108285==    at 0x4012A4: calc_ni.0 (main.f90:20)
==108285==    by 0x401408: MAIN__ (main.f90:11)
==108285==    by 0x401496: main (main.f90:14)
==108285==  Address 0x4ecaad4 is 0 bytes after a block of size 20 alloc'd
==108285==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==108285==    by 0x401385: MAIN__ (main.f90:9)
==108285==    by 0x401496: main (main.f90:14)
==108285== 
==108285== 
==108285== HEAP SUMMARY:
==108285==     in use at exit: 0 bytes in 0 blocks
==108285==   total heap usage: 22 allocs, 22 frees, 13,604 bytes allocated
==108285== 
==108285== All heap blocks were freed -- no leaks are possible
==108285== 
==108285== For lists of detected and suppressed errors, rerun with: -s
==108285== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

The output is telling at the line 20 where ni = 25, a position of the array is written but it should not. That’s because it was defined with an extra position sz+1 when calling the subroutine. Size 4 is in Bytes, and a Fortran real type is formed by 4 bytes.

Compilation and valgrind commands used:

gfortran -fcheck=all -O0 -g -o corruption main.f90
valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all ./corruption

2 Comments

  1. Thank you – very useful!

    Any suggestions about running Valgrind on a Fortran code that uses openmpi?

    1. What is your fundamental issue here? As there are many potential outcomes 😉

      Is openmpi the source of your problems? or it is just used within your code but no issues with it? Are you having troubles dealing with multiple outputs due to each MPI process? …

Leave a Reply

Your email address will not be published. Required fields are marked *