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. Logging
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!");
 |
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. |
In this section, you will practice the use of logging statements.
Logging is easy. Instead of System.out.println, simply use
Logger.global.info. For example,
Logger.global.info("About to return from find. i=" + i);
Logging is better than System.out.println for two reasons:
- There is no shame in logging. You can leave the logging statements
in your code when you turn it in, demonstrating good software engineering
practice.
- It is easy to turn all logging statements on or off.
Look at WordAnalyzerTester2. It
tests the firstMultipleCharacter method of the WordAnalyzer class.
That method also has a bug.
Step 1
|
| Which input to
the firstMultipleCharacter method does not yield the expected
result? |
We will use logging to find the problem. Add the following statements at
appropriate places of the find method:
Logger.global.info("Entering find. c=" + c + ",pos=" + pos);
Logger.global.info("About to return from find. i=" + i);
The Logger class is in the java.util.logging package.
Don't worry if you get a warning that Logger.global.info is
deprecated. Your program will compile and run correctly. If it really bothers
you, use Logger.getLogger("global").info instead. In Java 7, you
can use Logger.getGlobal().info.
Step 2
|
| What is the code
of your find method? |
Step 3
|
| What output do
you get when you run the WordAnalyzerTester2 class? |
Step 4
|
| Look at the
logging messages. Explain why the firstMultipleCharacter
method does not work. |
Step 5
|
| You can fix the
problem by modifying either the firstMultipleCharacter method
or the find method. Fix the problem. What fix did you
make? |
Step 6
|
| Run the program
again. What output do you get now? |
Of course, now you no longer need the logging messages. To turn them off, add
this statement to the main method in WordAnalyzerTester2:
Logger.global.setLevel(Level.OFF);
Step 7
|
| Add that
statement and run the program again. What output do you get now? |
Step 8
|
| Suppose you find
another bug and want to turn logging back on. How do you do that? |
In a small program, using Logger.global.info works fine. As your
programs grow larger, you can control your logging in more sophisticated ways.
You can use different logging levels. Instead of info, call methods
severe for important messages or fine for "fine-grained"
messages. Then call the setLevel method to set the level of the
messages that you want to see in a particular program run. You can also define
your own logger objects in addition to Logger.global. Keep this in
the back of your mind; it will come in handy in the future.
C. Running a Debugger
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:
- Set breakpoint
- Single step
- 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 Over" 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. |