Debugging the SQUARES Example Program

The example below shows a program called SQUARES that requires debugging. The program was compiled and linked without diagnostic messages from either the compiler or the linker. However, this program contains a logic error in an arithmetic expression.

   PROGRAM SQUARES

      INTEGER INARR(10), OUTARR(10), I, K

! Read the input array from the data file.

      OPEN(UNIT=8, FILE='datafile.dat', STATUS='OLD')

      READ(8,*,END=5) N, (INARR(I), I=1,N)

  5   CLOSE (UNIT=8)

 

! Square all nonzero elements and store in OUTARR.

      K = 0

      DO I = 1, N

        IF (INARR(I) .NE. 0) THEN

        K = K + 1       ! add this line

        OUTARR(K) = INARR(I)**2

        ENDIF

      END DO

 

! Print the squared output values.  Then stop.

      PRINT 20, N

  20  FORMAT (' Total number of elements read is',I4)

      PRINT 30, K

   0  FORMAT (' Number of nonzero elements is',I4)

      DO, I=1,K

        PRINT 40, I, OUTARR(K)

  40    FORMAT(' Element', I4, 'Has value',I6)

      END DO

  END PROGRAM SQUARES

The program SQUARES performs the following functions:

  1. Reads a sequence of integer numbers from a data file and saves these numbers in the array INARR. The file datafile.dat contains one record with the integer values 4, 3, 2, 5, and 2. The first number indicates the number of data items that follow.

  2. Enters a loop in which it copies the square of each nonzero integer into another array OUTARR.

  3. Prints the number of nonzero elements in the original sequence and the square of each such element.

This example assumes that the program was executed without array bounds checking (set by the -check bounds command-line option). When executed with array bounds checking, a run-time error message appears.

When you run SQUARES, it produces the following output, regardless of the number of nonzero elements in the data file:

squares

Number of nonzero elements is   0

The logic error occurs because variable K, which keeps track of the current index into OUTARR, is not incremented in the loop on lines 9 through 13. The statement K = K + 1 should be inserted just before line 11.

The following example shows how to start the debugging session and how to use the character-cell interface to idb to find the error in the sample program shown earlier. Comments keyed to the callouts at the right follow the example:

ifort -g -o squares squares.f90  (1)

idb squares                        (2)

Linux Application Debugger for xx-bit applications, Version x.x, Build xxxx

object file name: squares

reading symbolic information ... done

(idb) list 1,9                        (3)

      1   PROGRAM SQUARES

      2     INTEGER INARR(20), OUTARR(20)

      3 C  ! Read the input array from the data file.

>     4      OPEN(UNIT=8, FILE='datafile.dat', STATUS='OLD')

      5      READ(8,*,END=5) N, (INARR(I), I=1,N)

      6    5 CLOSE (UNIT=8)

      7 C  ! Square all nonzero elements and store in OUTARR.

      8      K = 0

      9      DO 10 I = 1, N

(idb) stop at 8                       (4)

[#1: stop at "squares.f90":8]

(idb) run                             (5)

[1] stopped at ["squares.f90":4 0x120001a88]

>   8      K = 0

(idb) step                            (6)

stopped at [squares:9 0x120001a90]

      9        DO 10 I = 1, N

(idb) print n, k                      (7)

4 0  

(idb) step                            (8)

stopped at [squares:10 0x120001ab0]]

     10       IF(INARR(I) .NE. 0)  THEN

(idb) s

stopped at [squares:11 0x1200011acc]

     11       OUTARR(K) =   INARR(I)**2

(idb) print i, k                      (9)

1 0

(idb) assign k = 1                    (10)

(idb) watch variable k                (11)

[#2: watch variable (write) k 0x1400002c0 to 0x1400002c3 ]

(idb) cont                            (12)

Number of nonzero elements is   1

Element   1 has value     4

Process has exited with status 0

(idb) quit                            (13)

% vi squares.f90                          (14)

   .

   .

   .

10:       IF(INARR(I) .NE. 0) THEN

11:           K = K + 1

12:           OUTARR(K) = INARR(I)**2

13:       ENDIF

   .

   .

   .

% ifort -g -o squares squares.f90     (15)

% idb squares                         

Welcome to the idb Debugger Version x.x-xx

Reading symbolic information ...done

(idb) when at 12 {print k}               (16)

[#1: when at "squares.f90":12 { print K} ]

(idb) run                                (17)

[1] when [squares:12 0x120001ae0]

1

[1] when [squares:12 0x120001ae0]

2

[1] when [squares:12 0x120001ae0]

3

[1] when [squares:12 0x120001ae0]

4

Number of nonzero elements is   4

Element   1 has value     9

Element   2 has value     4

Element   3 has value    25

Element   4 has value     4

Process has exited with status 0

(idb) quit                               (18)

%

  1. On the command line, the -g  option directs the compiler to write the symbol information associated with SQUARES into the object file for the debugger. It also disables most optimizations done by the compiler to ensure that the executable code matches the source code of the program.

  2. The shell command idb squares runs the debugger, which displays its banner and the debugger prompt, (idb). This command specifies the executable program as a file named squares. You can now enter debugger commands. After the idb squares command, execution is initially paused before the start of the main program unit (before program SQUARES, in this example).

  3. The list 1,9 command prints lines 1 through 9.

  4. The command stop at 8 sets a breakpoint (1) at line 8.

  5. The run command begins program execution. The program stops at the first breakpoint, line 8, allowing you to examine variables N and K before program completion.

  6. The step advances the program to line 9. The step command ignores source lines that do not result in executable code; also, by default, the debugger identifies the source line at which execution is paused. To avoid stepping into a subprogram, use the next command instead of step.

  7. The command print n, k displays the current values of variables N and K. Their values are correct at this point in the execution of the program.

  8. The two step commands continue executing the program into the loop (lines 9 to 11) that copies and squares all nonzero elements of INARR into OUTARR. Certain commands can be abbreviated. In this example, the s command is an abbreviation of the step command.

  9. The command print i, k displays the current values of variables I and K. Variable I has the expected value, 1. But variable K has the value 0 instead of the expected value, 1. To fix this error, K should be incremented in the loop just before it is used in line 11.

  10. The assign command assigns K the value 1.

  11. The watch variable k command sets a watchpoint that is triggered every time the value of variable K changes. In the original version of the program, this watchpoint is never triggered, indicating that the value of variable K never changes (a programming error).

  12. To test the patch, the cont command (an abbreviation of continue) resumes execution from the current location. The program output shows that the patched program works properly, but only for the first array element. Because the watchpoint (watch variable k command) does not occur, the value of K did not change and there is a problem. The idb message "Process has exited with status 0" shows that the program executed to completion.

  13. The quit command returns control to the shell so that you can correct the source file and recompile and relink the program.

  14. The shell command vi runs a text editor and the source file is edited to add K = K + 1 after line 10, as shown. (Compiler-assigned line numbers have been added to clarify the example.)

  15. The revised program is compiled and linked. The shell command idb squares starts the debugger, using the revised program so that its correct execution can be verified.

  16. The when at 12 {print k} command reports the value of K at each iteration through the loop.

  17. The run command starts execution.

  18. The displayed values of K confirm that the program is running correctly.

  19. The quit command ends the debugging session, returning control to the shell.