Lab: Debugging

The Problem

Consider the following program.

Oddity

public static void main(String[] args) {
    SimpleWriter out = new SimpleWriter1L();
    final int[] values = { 8, -4, 3, 0, -5 };
    int i = 0;
    while (i < values.length) {
        int remainder = values[i] % 2;
        if (remainder == 1) {
            out.println("odd");
        } else {
            out.println("even");
        }
        i = i + 1;
    }
    out.close();
}

Carefully trace the program to determine the program output by filling out the following tracing table. (We recommend that, when tracing inside the loop body, you put the value of the variable in the first iteration on the first line of the text area for that variable, and so on.) Do not move on to the next step until you are convinced you have the correct tracing table.

Statement Variable Values
out.content =
values = [8, -4, 3, 0, -5]
i = 0
while (i < values.length) {

values = [8, -4, 3, 0, -5]
    int remainder = values[i] % 2;

values = [8, -4, 3, 0, -5]

    if (remainder == 1) {

values = [8, -4, 3, 0, -5]

        out.println("odd");

values = [8, -4, 3, 0, -5]

    } else {

values = [8, -4, 3, 0, -5]

        out.println("even");

values = [8, -4, 3, 0, -5]

    }

values = [8, -4, 3, 0, -5]

    i = i + 1;

values = [8, -4, 3, 0, -5]

}

values = [8, -4, 3, 0, -5]

Setup

Follow these steps to set up a project for this lab.

  1. Create a new Eclipse project by copying ProjectTemplate. Name the new project Debugging.
  2. Open the src folder of this project and then open (default package). As a starting point you can use any of the given programs. Rename one of them Oddity and delete the other files from the project.
  3. Open the Oddity.java program in the Eclipse editor, and replace the main program with the one above (you shouldn't have to type it; just copy and paste from your browser window into Eclipse). You should also replace the comment at the top of the file with something consistent with the behavior of the new program.

Method

Run the Oddity program and compare the output produced with your expected output. Do they match exactly? If not, can you explain why the program is doing something that is different from what you expected?

Trying to identify a bug in code is known as debugging. There are many techniques that can be employed to track down bugs; here we will concentrate on a specific tool provided by Eclipse.

Eclipse includes a debugging perspective, a sophisticated environment to help programmers detect and diagnose errors in running programs. The Eclipse debugging perspective allows you to control the execution of your program by setting breakpoints, suspending launched programs, stepping through your code, and examining the contents of variables.

1. Starting the Debugger

Before you run a program in the debugger, you need to tell the debugger where you want it to pause execution. If you don't, the debugger executes the entire program to completion and you don't get a chance to use the debugger features.

You can have the debugger pause at any statement in the program by setting a breakpoint next to the statement. To set a breakpoint, right click in the vertical bar to the left of the editor window on the line corresponding to the statement where you want the debugger to pause. In the contextual pop-up menu, select Toggle Breakpoint.

Thumbnail for Screenshot001.jpg Eclipse Java editor showing a breakpoint being set; left gutter displays a breakpoint marker beside a source line and a contextual menu with Toggle Breakpoint visible. The editor shows Java source code including the main method and a while loop, with IDE chrome such as tabs and toolbar indicating the Debug perspective. Interface text visible includes Toggle Breakpoint and code tokens like public static void main.

The new breakpoint is indicated by the small dot on the left of the statement.

Thumbnail for Screenshot002.jpg Eclipse Java editor showing the left gutter with a red breakpoint marker beside a source line inside the main method; a contextual menu labeled Toggle Breakpoint is open above the gutter; the editor shows Java code including the text public static void main and a while loop; IDE tabs and toolbar are visible indicating the Debug perspective.

You should set a breakpoint next to the first statement of the main method. That's where program execution will halt in the debugger.

Now to start a program in the debugger, select Debug (instead of Run) from the Run menu.

Thumbnail for Screenshot003.jpg Eclipse IDE window showing the Run menu open with the Debug menu item highlighted; the Java editor is visible with code including public static void main and a while loop and a breakpoint marker in the left gutter beside a source line; visible menu text includes Run and Debug;

The first time you run the debugger, Eclipse asks you whether you want to switch to the Debug perspective. You should check the Remember my decision checkbox and click Switch.

Thumbnail for Screenshot004.jpg Modal dialog in the Eclipse IDE asking whether to switch to the Debug perspective. Dialog text reads Switch to Debug perspective? A checked checkbox labeled Remember my decision is visible. Buttons labeled Switch and Cancel are shown. Background shows the Java editor with source code, a breakpoint marker in the left gutter, and IDE toolbars.

2. The Debug Perspective

An Eclipse perspective is a collection of panels and tabs containing views and editors to support a specific set of development activities. The Debug perspective, as the name implies, provides a debugging environment. Here are some of the more important areas in the default Debug perspective. Here we will not describe all the pieces, just those you will need for today's activity.

Thumbnail for Screenshot005.jpg Eclipse IDE Debug perspective showing the Java editor with execution paused at a breakpoint indicated by a left-hand arrow and a red breakpoint marker; top right panel shows Variables and Breakpoints tabs listing program variables and active breakpoints; bottom panel shows the Console; editor displays Java source code including public static void main and a while loop;

Switching Perspectives

In the top right corner of the perspective, there are buttons corresponding to all the open perspectives. Currently you should see at least the Java perspective and the Debug perspective buttons. You can switch to a different (open) perspective by simply clicking that perspective button. For now, make sure you remain in the Debug perspective.

Top right corner of the Eclipse IDE showing perspective buttons labeled Java and Debug with the Debug perspective highlighted; the Java editor is visible behind showing a main method and a breakpoint marker in the left gutter; surrounding panels and toolbars of the IDE are visible.

The Editor Panel

The left, middle panel is the Editor window where you can see where your program execution is paused, and you can follow execution when stepping through your program. Note that you can see (and set) the breakpoint(s) to the left of the panel and also an arrow to the left of the statement where execution was suspended: this arrow indicates the next statement that will be executed.

Thumbnail for Screenshot007.jpg Eclipse Java editor showing the main method and a while loop; a left-hand arrow marks the next statement to execute and a red breakpoint marker appears in the left gutter; visible code fragments include public static void main, while (i < values.length), and int remainder = values[i] % 2; The Debug perspective is visible with Variables and Breakpoints tabs at the top right and the Console at the bottom; the program is paused at a breakpoint.

The Console

At the bottom of the perspective you can find the Console panel where you can type your input and view the output of your program.

Thumbnail for Screenshot008.jpg Eclipse IDE Debug perspective showing the Console panel at the bottom displaying the program output area and input prompt. The Java editor above is paused at a breakpoint with a left gutter marker and a yellow arrow on the next statement; the editor shows a main method and a while loop. The top right panels show Variables and Breakpoints tabs. Visible interface text includes Console, Variables, Breakpoints, public static void main, while (i < values.length).

Variables and Breakpoints

The panel at the top right of the perspective houses various tabs that allow you to explore the state of the program and control which breakpoints are active. By default two tabs are open: the Variables tab where you can observe the value of the variables in scope at the point where the program is paused, and the Breakpoints tab where you can review the existing breakpoints and activate/deactivate them by simply checking or unchecking the corresponding checkboxes.

Thumbnail for Screenshot009.jpg Eclipse Debug perspective showing the Java editor and debug views. The editor displays Java source code including public static void main and while (i < values.length) with a left arrow marking the next statement to execute. The top right panel shows Variables and Breakpoints tabs with a list of variables and active breakpoints. The bottom panel shows the Console.

Controlling Execution

Finally, at the top left of the perspective, in the toolbar, you can find various buttons that can be used to control the program execution. Hover over each one of them with the mouse and a tooltip will appear telling what the functionality of each button is. In particular, notice the buttons to Resume execution (resumes execution until the next breakpoint is reached, or the program is suspended, or the end of the program is reached), Suspend execution (pauses the program), and Terminate execution (ends execution of the program) and those to Step Into (steps into the method being invoked and pauses at the first statement in the method), Step Over (executes a method call and pauses after that), and Step Return (completes the execution of the current method) and returns to the caller pausing after the call.

Eclipse Debug perspective toolbar showing execution control buttons labeled Resume Suspend Terminate Step Into Step Over Step Return. The main editor below is paused at a breakpoint with a yellow arrow on a line in a Java main method containing a while loop. Variables and Console panels are visible in the surrounding IDE layout.

3. Debugging Oddity

If you look at the Variables tab (top right), you can see that it lists only the args formal parameter to the main method because that is the only variable currently in scope. If you click on it, its value is displayed in the lower part of the Variables tab, but this is not very interesting because here we did not pass any command-line arguments to the program and so the array is empty and displayed simply as []. Let's make things more interesting.

Let's execute the first three statements in main by clicking three times on the Step Over button in the toolbar at the top of the perspective.

Eclipse Debug perspective showing the Variables view listing program variables and their current values: args = [], out.content = , values = [8, -4, 3, 0, -5], i = 0. The Java editor is visible and paused at the start of main with a yellow arrow on the while (i < values.length) statement. The Console panel is visible below.

After each click, notice the following:

  1. The arrow next to the statement in the editor panel is advanced to the next statement, showing progress through the program, and
  2. in the Variables panel a new entry appears corresponding to the new variable that has been declared and initialized.

Now click on each of the variables in the Variables panel and you can see the current value for each.

Thumbnail for Screenshot012.jpg Eclipse Debug perspective showing the Variables view and Java editor. The Variables panel lists args = [], out.content = , values = [8, -4, 3, 0, -5], i = 0. The Java editor is paused at a breakpoint with the while (i < values.length) line highlighted and a yellow arrow marking the next statement to execute. The Console panel is visible at the bottom and the debugging toolbar is visible at the top with controls such as Resume, Suspend, Terminate, Step Into, Step Over, Step Return.

Now single-step through the first iteration of the while loop; before each step think about what you think should happen and after each step verify that indeed the observed behavior matches the expected behavior of each statement. Any time a statement changes the value of a variable, you can see the updated value in the Variables tab.

After printing even for the first element of the array and incrementing the value of i to 1, you will find yourself back to the start of the while loop. You can continue stepping through the code one statement at a time until you get to the point where your trace and the actual observed behavior of the program differed. But, assuming that the issue (bug) you observed was with the last line of output where you probably thought that -5 being an odd number, the program should have printed odd , but instead it printed even , we can speed up getting to that point by setting another breakpoint on the first statement inside the loop (the one declaring and initializing the variable remainder ).

Set a breakpoint on the selected line by right-clicking on the left of the editor pane and selecting Toggle Breakpoint. Now click on the Resume button in the toolbar and see that the execution stops at the new breakpoint. Assuming the current value of i is 1, click on Resume 3 more times. After each click, check that the value of i has been incremented and that the correct output (even or odd) has been generated for the corresponding array element.

The state of the program should now show i = 4 and the program should be suspended at the first statement in the loop body, ready to compute the remainder of the division by 2 of the last element of the values array (element at index 4 with value -5). If by mistake you have already moved past this point, you should simply stop execution (by clicking on the Terminate button in the toolbar), restart the program in the debugger and make sure you follow the steps above to stop execution of the program on the first statement inside the loop with i set to 4.

Thumbnail for Screenshot013.jpg Eclipse IDE Debug perspective showing a Java program paused at a breakpoint on the line computing the remainder values[i] % 2; the editor displays a yellow arrow at the next statement to execute; the Variables panel lists args = [], out.content = , values = [8, -4, 3, 0, -5], i = 4; the Console panel is visible at the bottom and the debugger toolbar is visible at the top with buttons labeled Resume Suspend Terminate Step Into Step Over Step Return.

Now single step over the assignment computing the remainder for the last element of the array and look at the value of the remainder variable in the Variables tab. Can you explain what happened?

  1. How can the value of -5 % 2 be -1?
  2. Can you see why the program is going to print that -5 is even?
  3. How would you fix the program so that it behaves correctly?

It turns out that the definition of the % (remainder) operator in Java satisfies this equation: (a / b) * b + (a % b) = a . Because integer division rounds toward 0, -5 / 2 = -2 and therefore the value of -5 % 2 that satisfies the equation is -1.

Fix the program so that it prints the correct output for all array elements, then run the program and check that it behaves correctly. If it still does not work or some other bug arises as a result of your modifications, debug your code and fix all remaining problems.

One mystery solved! Now that you know the basics of using the debugger in Eclipse, you can try to solve a few more "puzzles".

4. More Examples, More Practice

Now consider the following programs. Again, first trace each of them carefully and determine what the output of each should be. Then enter them in Eclipse, run them, and compare the observed behavior with the predicted behavior. If they do not match exactly and you cannot explain the discrepancies, use the debugger in Eclipse to help you figure out what's going on.

  1. Time for a Change

    public static void main(String[] args) {
        SimpleWriter out = new SimpleWriter1L();
        double x = 456.0;
        double y = 100.0 * 4.56;
        if (x == y) {
            out.println("equal");
        } else {
            out.println("not equal");
        }
        out.close();
    }
    
  2. Integer Division

    public static void main(String[] args) {
        SimpleWriter out = new SimpleWriter1L();
        final int microsPerDay = 24 * 60 * 60 * 1000 * 1000;
        final int millisPerDay = 24 * 60 * 60 * 1000;
        out.println(microsPerDay / millisPerDay);
        out.close();
    }
    
  3. Elementary

    public static void main(String[] args) {
        SimpleWriter out = new SimpleWriter1L();
        out.println(12345 + 5432l);
        out.close();
    }
    

Additional Activities

  1. Implement the following method that computes the modulo function using "clock arithmetic".

    /**
     * Returns a modulo b using "clock arithmetic".
     *
     * @param a
     *            the first operand
     * @param b
     *            the modulus
     * @return a modulo b
     * @requires b > 0
     * @ensures mod = a mod b
     */
    private static int mod(int a, int b) {...}
    
  2. If you are curious to learn more about these and other Java puzzles, you can go right to the source: Java Puzzlers: Traps, Pitfalls, and Corner Cases by J. Bloch and N. Gafter. (The "loopy" puzzles are particularly enlightening, and the majority of puzzles in the book should be readily understandable by everyone in this course by the end of the term.)