Java Programming   Java Programming
 |Sofia Home | Content Gallery |
Home
Syllabus
Schedule
Lessons
Assignments
Resources

Lesson 7.5 The Debugging Process

Debugging Your Programs

There you sit with your fancy new applet. You've written all the code, you've fixed all the nagging compiler errors, you've run the program through it's paces to make sure it does what the specs require. It looks like its time to ship it out the door. Right?

Not so fast. 

A program isn't really finished until it has been tested and debugged. As your text points out, testing and debugging are not the same thing, but they are inextricably linked:

  • Testing is the process of repeatedly running your program using representative and exceptional input data, and checking its performance and output against the program specification.
  • Debugging is the process of finding and fixing the errors discovered during testing.
In this section, and the next, you'll get a quick introduction to some methodical techniques for finding and fixing bugs.

The Debugging Process

Debugging is an iterative process that really begins with testing. If you don't know that there's a problem with your code, then why would you bother to fix it.

Illustration of the code-test-debug cycle. Click to enlarge.

Many errors, however, don't really require extensive testing to discover. Just running your program for the first time frequently manifests a horde of runtime errors and other aberrant behaviors. Because of this, the average program usually undergoes quite a bit of debugging, even before formal testing is started.

Back to Top

The Four Steps

If you've ever fixed a car or other mechanical device, you know that you can't just try things at random if you hope for a successful repair. You have to make sure that you're fixing the right problem in the right way, or you're likely to end up worse off than when you started.

The same thing is true in programming. The systematic application of each of these four steps increases your odds of success immeasurably. The four-step debugging plan includes:

  1. Break it
  2. Understand it
  3. Fix it
  4. Test it
Let's take a quick look at each of these.

Break It

Have you ever had a toothache or some medical malady that cleared up just when you finally broke down and went to the dentist or to the doctor? Most of us have gone through a similar experience. Although it sounds weird, you don't want your symptoms disappearing until you know what caused the symptoms in the first place.

Software is like that. Before you can fix the problems in your program, you first have to be able to reproduce the problems--reliably and at will. If you've ever worked in technical support, or if you've ever needed to call technical support for a program you use, you know the drill:

  1. What version of the Operating System (OS) are you using?
  2. What version of the program are you using?
  3. What other software do you have installed on the machine?
  4. What were you doing when the error occurred? What steps lead up to the error?
Although it might sound like such questions are designed to avoid taking responsibility for errors in the company's software, that's not [necessarily] the case. In most technical support organizations, before a bug can be fixed, or even recognized as a bug, the technical support staff must be able to reproduce it.

For your own programs, you can't really get started fixing your program until you can make it fail, any time you please.

Understand It

Once you can make your program fail reliably, the next step is to understand the problem. Just like the fledgling journalist you have to ask the questions who, what, where, why, and how?
  • Who: Which one of your objects is misbehaving?
  • What: What is the state of its attributes and variables?
  • Where: Which method is causing the problem?
  • Why: Why is the method behavior wrong?
  • How: How can the behavior be changed?
To answer this question you have to look inside your program and watch as the state of your program changes while it runs. Modern Integrated Development Environments [IDE]s have symbolic debuggers that let you run your program line-by-line, and watch the state of your variables as your program runs. 

However, you really don't need a symbolic debugger, and many programmers don't use them. You can easily do the same thing by using System.out.println(), and the magic of toString(). If you're not quite sure how to do this, make sure you read through the extended example in the text. 

Back to Top

Fix It

Once you understand the problem, fixing it is easy, right? Well, not always. Even if you understand perfectly what needs to be done, it's not wise to simply make the change and move on. Instead, you should:
  1. document the changes you are making.
  2. provide a way to reverse the changes if things don't work out the way you thought they would.

When you document your changes you should add extensive comments explaining both the problem and your solution at the point where you make your changes. When you add these comments, make sure you explain the problem thoroughly, as though you were explaining it to someone who had never seen the code before. You should do this even if you're the only one working on the code, because the next time you have to work on the program, you simply won't remember all of the little details that are now so fresh in your mind.

Documenting Your Changes
/*************************************
 Calculates fence-posts required
   Input:  Length of fencing
           Length of each section
   Output: Number of posts required
 *************************************/
public int numFencePosts(
                 int fenceLength,
                 int sectionLength)
{
  // return fenceLength/sectionLength;
  /* 07/22/99 sdg ---------------begin--
   * Calculation above incorrect because:
   *   1. Off by 1. Need 11 posts for
   *      100 ft/ 10 ft apart
   *   2. Ignores remainder problem
   *      12 ft / 10 ft requires 3 posts
   * Replaced with code below
   * --------------------------------- */

  int posts = fenceLength/sectionLength;

  if ( fenceLenth % sectionLength > 0)
    posts += 2;
  else
    posts++;

  return posts;
  /* 07/22/99 sdg -------------- end */
}

In addition to these thorough "inline" comments, you should get into the habit of keeping a "change log" at the top or bottom of every file. This is simply a one-line-per-change annotation that allows you to easily see when changes were last made to your program. Think of it as an index or table of contents to your changes.

Professional software developers usually use a revision controlsystem that automatically records all changes made to their source code and which can reconstruct any previous version of their program should things go awry. In the absence of such a system, you should make sure you comment out your old code when you change it--don't delete it.

If you work in a software development organization, most likely they have a change management process in place, designed to protect the integrity of their programs, and to make sure that "fixes" don't introduce more bugs than they remove.

Back to Top

Test It

Once your changes are in place you must make sure that your fix actually works. In the next lesson, you'll learn about how to go about that.

Something to Talk About

DaysInYear.java is a program that tells you how many days are in a year. It assumes that there are 365 days and then it adds one if the year is a leap year. A year is a leap year if it is evenly divided by 4, but not evenly divided by 100. Years that are evenly divisible by 400, however, are leap years. Thus, 2000 is a leap year, but 1900 is not.

Go ahead and test the program to see if it contains an error. Does it? If so, can you correct the error? How did you find it?

Please continue to the next section of this lesson.

 

Back to Top
Content Developed by Stephen Gilbert, Licensed under a Creative Commons License
Published by the Sofia Open Content Initiative
© 2004 Foothill-De Anza Community College District &The William and Flora Hewlett Foundation