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

        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

  30  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:

In the program, 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.

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

Debugging Example 1: The following shows how to debug a Linux-based program using the IDB command line.

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 command 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.

 

Debugging Example 2: The following shows how to debug a Windows-based program using the Microsoft integrated debugger within the integrated development environment. This example assumes a solution already exists.

To debug this program:

  1. From the Microsoft Visual Studio program folder, select Visual Studio to start the integrated development environment.
  2. In the File menu, select Open Solution. Open the solution containing the file.
  3. Edit the file squares.f90: double-click its file name in the Solution Explorer View. The screen appears as follows:

    The following toolbars are shown: Build toolbar, Standard toolbar.
  1. To change the displayed toolbars, select View>Toolbars. To display the debug toolbar, select Debug.

  2. Click the first executable line to set the cursor position. In this case, click the beginning of the OPEN statement line:
    OPEN(UNIT=8, FILE='datafile.dat', STATUS='OLD')
  3. Click in the left margin of the first executable line to set a breakpoint. The red circle in the left margin of the text editor/debugger window shows where a breakpoint is set:
  4. This example assumes you have previously built your application (see Preparing Your Program for Debugging).

    In the Debug menu, select Start (Visual Studio .NET* 2003) or Start Debugging (Visual Studio* 2005).
    The debugger is now active. The current position is marked by a yellow arrow at the first executable line (the initial breakpoint).

  1. If needed, you can set another breakpoint. Position the cursor at the line where you want to add a breakpoint, right click, and select Insert Breakpoint (Visual Studio .NET* 2003) or Breakpoint>Insert Breakpoint (Visual Studio* 2005).

  2. Step through the lines of source code. You can do this with  Debug>Step Over or with the Step Over button on the Debug toolbar.

  1. Repeat the Step Over action and follow program execution into the DO loop and unto the end of the program. Position the cursor over the variable K to view its value in a Data Tip box:

The error seems related to the value of variable K.

  1. In the text editor, add the line K = K + 1 as follows:

! 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

  1. Since you have modified the source, you need to rebuild the application:

    The output screen appears as follows:

    displayed output after fixing the program

  2. The program now generates better results. You can examine the values of both the input array INARR (read from the file) and the output array OUTARR that the program calculates. In the text editor window, the previously set breakpoint remains set.

    In the Debug menu, select Start (Visual Studio .NET 2003) or Start Debugging (Visual Studio 2005).

  3. To view the values of certain variables as the program executes, display the Locals window. In the Debug menu, select Windows> Locals.
  4. You can view the values of the local variables in the Locals window. Click the plus sign to view array values.

    The Locals window does not let you display module variables or other non-local variables. To display non-local variables, display one of the Watch windows.

  5. Although this example does not use module variables or non-local variables, you can drag a variable name into the Watch window so the variable can be displayed. The Watch window allows you to display expressions.

    In the text editor window, select the variable name INARR (without its subscript syntax), drag it, and drop it into the Watch window, Name column:

  6. Also drag the OUTARR array name to the Watch window. Click the Plus sign (+) to the left of the OUTARR variable's name to display the values of its array elements.
  7. Execute lines of the program by using the Step Over button on the Debug toolbar. As the program executes, you can view the values of scalar variables with the data tips feature and view the values of arrays (or other variables) in the Watch window.

If a Disassembly window (shows disassembled code with source-code symbols) unintentionally appears, click the Step Out button on the debugger toolbar (or select Step Out in the Debug menu) as needed to dismiss the Disassembly window.