Debugging Class Exercise


  1. Copy all files from Files, Debugging Exercise folder.
  2. Open eclipse and create a project for the WordAnalyzer program, using WordAnalyzer and WordAnalyzerTester. A WordAnalyzer is constructed with a String and contains methods to analyze this word.
  3. Using Stack Traces

  4. We are looking at the firstRepeatedCharacter method, which constains a bug. The method is supposed to return the first character in the word, such as c in success.
  5. Look at the code and predict the output, assuming that firstRepeatedCharacter works correctly.
  6. Now run the program. Write down the output, including the correct output and the error message.
  7. We see that we get some output and then an exception:
    First repeated character = a
    First repeated character = o
    Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 4
            at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:44)
            at java.base/java.lang.String.charAt(String.java:692)
            at WordAnalyzer.firstRepeatedCharacter(WordAnalyzer.java:26)
            at WordAnalyzerTester.test(WordAnalyzerTester.java:14)
            at WordAnalyzerTester.main(WordAnalyzerTester.java:7)
    
  8. When an exception occurs which is not handled, we get a stack trace, as seen above. The stack trace shows the statement which threw the exception, then the statement that called it, and back through all of the calls to the original call in main. Looking at this stack trace we can see that the exception occurred in the Java api method java.lang.StringLatin1.charAt, which was called by java.lang.String.charAt. Although it's tempting to think the problem is in one of the Java functions, in fact you can be 99.99% certain that the bug is in your code, and that you called charAt with invalid parameters. The stack trace shows that the call at line 26 of WordAnalyzer led to the exception. What is the code at line 26? Write down the statement.
  9. There are two different exceptions that can be thrown by this statement. Write them down.
  10. Which of these exceptions actually occurred? Write it down.
  11. Knowing this, determine what caused the exception. Modify the code and run it again. Copy down the output.

Using a Debugger

Exceptions are very helpful when your program crashes. But many errors don't cause exceptions, they just cause your program to produce incorrect output. One way to find this type of error is to add print statements, to show what statements are being executed and to show the values of your variables. This takes a lot of time: you have to add the print statements, add more, comment them out, take them out, and then maybe put new ones in to find the next bug.

An easier way is to use a debugger. Almost all IDEs -- eclipse, NetBeans, BlueJ, etc. -- have a built in debugger, a tool that lets you step through the statements of your code as it executes, see which statements execute, and look at the values of your variables. This is why you should no longer use textpad. It's fine for small programs but now that you are working on larger programs you will need a debugger. Debuggers have many capabilities, but you only need to know how to do a few things.

Debugging WordAnalyzer

We know from the output that the first two calls of test worked, so we would like to investigate the third call. To set a breakpoint at the third call, double click to the left of the line number at the beginning of the statement. A blue dot will appear if you the breakpoint is set.

Debug the program: either choose debug from the Run menu, or click on the debug button, which looks like a bug and is to the left of the run button.

Eclipse may ask whether you want to shift to the debug perspective; if so, click on yes. Eclipse has different perspectives, which determine the windows visible within eclipse. Normally you are in the java perspective, where you see the package explorer on the left, the edit window in the middle, and the execution window on the bottom. When you debug, you shift to the debug perspective. In this perspective you have the debug window on the top left, where you see the stack of active function calls, the variable window on the top right, where you see the values of the variables in the currently executing function, and the code window in the middle, where you follow the execution through your statements. At the bottom is the output window. At the right end of the eclipse toolbar are the buttons that let you shift between the java perspective (which has a J) and the debug perspective (which looks like a bug). Use these buttons to shift back and forth, but stop in the debug perspective.

Your program will stop at the breakpoint, which is the call to test. When stopping at a statement, the debugger always stops before executing the statement. Once the program stops, you can look at variables or step through your code.

Stepping Through Your Code

Stepping through your code means that you execure one statement at a time. This allows you to see where the execution There are several options for how you want to step. Once you are debugging, and you stop at a breakpoint, these are the stepping options you will find on the Run menu. Each also has a keyboard shortcut, as shown below.

Practice It

  1. Create a project for the WordAnalyzer program, using WordAnalyzer and WordAnalyzerTester3. This is the same WordAnalyzer and a different main method which tests the countRepeatedCharacters method. This method counts the number of substrings that consist of a character which is repeated. For example, the word "mississippiii" has 4 repeated character substrings: "ss", "ss", "pp", and "iii".
  2. Run the program and check the output. Write down the error you see.
  3. Since the program runs correctly on the first two Strings, we would like to examine the third call and see what the code is doing. By paying close attention to the code and variables, and thinking about what is happening, we should be able to see what is wrong. Therefore, we want to set a breakpoint at the third call to the test method.
  4. Now run the program, and it stops at the third call to test. Remember that when it stops, it has not yet executed that statement.
  5. We want to watch the execution of text and countRepeatedCharacters, so choose step into from the Run menu, or press F5. Now you are inside the test method.
  6. The first line of test is the declaration and creation of the WordAnalyzer. If we use step into again, we will be inside the constructor. This is not of interest, so we choose step over from the Run menu, or press F6.
  7. Now we are at the call to countRepeatedCharacters, which contains the bug. We use choose step into from the Run menu, or press F5 to get into the method.
  8. We are at the first line of countRepeatedCharacters, which is int c = 0;. Step over to get to the for statement. To see the values of variables, you can look at the variable window on the upper right. In this window you may need to click on arrow (> symbol) to the right of an object to see the fields of the object. The window contains this which is the invoking object. In our case, this is the WordAnalyzer object containing the String "aabbcdaaaabb". You can also see the values of your variables if you hover over the variables in the code window. Use both methods to see the values of word, i, and c. Write down the value of word.
  9. Use step over to watch the loop execute. What happens to the values of i and c? What would happen if you used step into instead?
  10. The value of c changes 3 times. What are the values for i at each increase in c? Write these values down.
  11. Choose resume from the Run menu, or press F8. What happens?

Debug the Program

The countRepeatedCharacters method looks for character sequences of the form xyy, that is, a character followed by the same character and preceded by a different one. That is the start of a group. Note that there are two conditions. The condition

     if (word.charAt(i) == word.charAt(i + 1))

tests for yy, that is, a character that is followed by another one just like it. But if we have a sequence yyyy, we only want it to count once. That's why we want to make sure that the preceding character is different:

     if (word.charAt(i - 1) != word.charAt(i))

This logic works almost perfectly: it finds three group starts: aabbcdaaaabb. Why doesn't the method find the start of the first ("aa") group? Can you fix the bug by changing the for loop to start at 0 instead of 1? If not, what do you need to change to fix the method?

When you want to stop debugging, and are ready to edit your code, switch back to the Java perspective. Remember it's the button with the J, in the upper right corner of eclipse, at the right end of the toolbar.

Fix the bug and run the program again. When it reaches the breakpoint, choose resume to continue execution.



Email Me | Office Hours | My Home Page | Department Home | MCC Home Page

This exercise is taken from an exercise by Cay S. Horstmann and Kathleen O'Brien and is licensed under a https://creativecommons.org/licenses/by-sa/4.0/