Debugging Lab

When you hear the person sitting next to you saying “I have a bug in my program”, you don't have to worry about roaches infesting the computer. When we say bugs we really mean logical errors in our program. The first bug, however, was a real live bug. Well, live at first. In 1947 at Harvard University, a moth was found in one of the components of the Mark II computer, and was causing problems. 

.

In this lab you will deal with debugging. You will practice locating and fixing some common types of bugs. The high point of the lab is a tour of the debugger inside the BlueJ environment.

A. Reading Stack Traces

Make a BlueJ project from the files WordAnalyzer.java and WordAnalyzerTester.java.

Have a look at the WordAnalyzer class. A WordAnalyzer is constructed with a string—the word to be analyzed. Right now, we only care about the first (buggy) method: firstRepeatedCharacter. It returns the first repeated character in a word, such as o in roommate.

The WordAnalyzerTester program simply tests the WordAnalyzer class.

Step 1
Without actually running the program, predict its output. Assume that the firstRepeatedCharacter method works correctly.

Step 2
Now run the program. What output do you get? (Include the correct outputs and the error message.)

The program dies with an exception and prints a stack trace:

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 4
at java.lang.String.charAt(String.java:558)
at WordAnalyzer.firstRepeatedCharacter(WordAnalyzer.java:26)
at WordAnalyzerTester.test(WordAnalyzerTester.java:14)
at WordAnalyzerTester.main(WordAnalyzerTester.java:7)

Have a look at the stack trace. The first complaint is about the method charAt of the java.lang.String class. That's a library class. It is extremely unlikely that a library class has a bug. It is far more common that one of its methods was called with bad parameters.

The next complaint is about line 26 of the firstRepeatedCharacter method of the WordAnalyzer class.

Step 3
Exactly what source code do you find in line 26 of WordAnalyzer?

Now look at the call to charAt in line 26.

word.charAt(i + 1)

Step 4
In theory, there are two different exceptions that can be thrown in this call. What are they? 

Of course, we know that word is not null in this case, so we know that the error must be the index. 


Step 5
(a) Explain the nature of the bug, and how to fix it. (b) What output did you get after you fixed the bug?

B. Running a Debugger

Exceptions are useful to pinpoint drastic errors that kill your program. But often, a sick program doesn't die. It limps along and computes the wrong result. In order to find out what such a program does, students often sprinkle their code with print statements like this one:

System.out.println("Yoohoo! I got this far!");
System.out.println("i=" + i);
. Print statements can waste a lot of your time. You put them in, you take them out, you put them back in, you comment them out, you remove the comments, and when it all works you take them out for good. Until the next bug appears.

But there is a better way. Most development environments, including BlueJ and NetBeans, have a debugger, a tool that lets you execute a program in slow motion. You can observe which statements are executed and you can peek inside variables. 

Debuggers can be complex, but fortunately there are only three techniques that you need to master:

  1. Set breakpoint
  2. Single step
  3. Inspect variable
In this lab, we will use the debugger that is a part of BlueJ.

For this section, you need the WordAnalyzerTester3 class that tests the buggy countRepeatedCharacters method. That method counts the substrings consisting of repeated character in a word. For example, the word mississippiiihas a count of 4. 

Step 1
Execute WordAnalyzerTester3 in BlueJ. What output do you get?

The countRepeatedCharacters method does the right thing for the first two test cases, but it mysteriously fails on the string "aabbcdaaaabb". It should report 4 repetitions, but it only reports 3.  

Setting Breakpoints

Unfortunately, the debugger cannot "go back", so you can't simply go to the point of failure and backtrack. Instead, you first run your program at full speed until it comes close the point of failure. Then you slow down and execute small steps, watching what happens. 

We know that the first two calls to the test method in WordAnalyzerTester3 give the correct results. It won't be too interesting to debug them. Instead, let's go directly to the third call. We'll set a breakpoint at that line, so that the debugger stops as soon as it reaches the line.

In BlueJ, open the WordAnalyzerTester3 class for editing. Click left of the vertical line in the line containing the third call to test. A red stop sign should appear, indicating the breakpoint.

.

Now run the program. When it hits the first breakpoint, the program stops and the current line is highlighted:

Step 2
Launch the debugger as described. What output do you get in the console window? Why do you get two lines of output and not three? 

Single stepping

When the debugger stops at a breakpoint, another window appears, with a row of big buttons on the bottom:

You use the Step and Step Into buttons to step through your code in slow motion.

Step 3
Execute the "Step Into" command. What happens? 

Now you are inside the test method. The next call is
WordAnalyzer wa = new WordAnalyzer(s);
That call is boring, and we do not want to step into the constructor. We'll use the "Step" command instead.

Step 4
Execute the "Step" command. What happens? 

Now you are at the line
int result = wa.countRepeatedCharacters();

Step 5
Remember, we want to find out why we get the wrong repetition count for the third test case. Should you execute "Step" or "Step Into" at this point? Why? 

Inspecting Variables

Execute "Step Into" a couple of times. You should get into the lines

int c = 0;

and

for (int i = 1; i < word.length() - 1; i++)

Look inside the Local variables window in the bottom right corner.



Now keep executing the Step command. Watch what happens to the c and i values. You'll see i increase each time the loop is executed.

Step 6
The value of c increases three times. What are the values for i at each increase? 

Step 7
Look at the value of the word instance variable. What is special about the three positions at which c increases?  

We will fix the bug in the next section. For now, click the Continue button. Your program runs again at full speed, until it hits the next breakpoint or until it terminates.

Step 8
What happens when you click the "Continue" button? 

You now know how to use the debugger. You have learned how to set breakpoints, how to single-step, and how to view variables.

Fixing the bug

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

Step 9
Why doesn't the method find the start of the first (aa) group? 

Step 10
Why can't you simply fix the problem by letting i start at 0 in the for loop? 
 
Step 11
Go ahead and fix the bug. (a) What is the code of your  countRepeatedCharacters method now? (b) Run the WordAnalyzerTester3 method again. What is the output now?

. Is the program now free from bugs? That is not a question the debugger can answer. As the famous computer scientist Edsger Dijkstra pointed out: "Program testing can be used to show the presence of bugs, but never to show their absence!" As you have seen in this lab, testing and debugging is a laborious activity. In your computer science education, it pays to pay special attention to the tools and techniques that ensure correctness without testing. 

C. Debugging your Homework

Extra time? Have a bug in your homework assignment? (A failing test case would be great.) Run the debugger.

1. Describe what you do to get close to the point where the bug happens.

2. Describe the values of all variables just before the bug happens.

3. Single-step over the place where the bug happens. Did it happen?

4. When you look at the values of the variables, can you tell why the bug happened? Which variable wasn't set to the right value?

5. Did you get enough information to fix the bug?