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.
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.
|Without actually running the program, predict its output. Assume that the firstRepeatedCharacter method works correctly.|
|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
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.
|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)
|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.
|(a) Explain the nature of the bug, and how to fix it. (b) What output did you get after you fixed the bug?|
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:
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.
|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.
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.
|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?|
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.
|Execute the "Step Into" command. What happens?|
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.
|Execute the "Step" command. What happens?|
int result = wa.countRepeatedCharacters();
|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?|
Execute "Step Into" a couple of times. You should get into the lines
int c = 0;
for (int i = 1; i < word.length() - 1; i++)
Look inside the Local variables window in
the bottom right corner.
|The value of c increases three times. What are the values for i at each increase?|
|Look at the value of the word instance variable. What is special about the three positions at which c increases?|
|What happens when you click the "Continue" button?|
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?|
|Why can't you simply fix the problem by letting i start at 0 in the for loop?|
|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.|
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?