|
|
|
To access the contents, click the chapter and section titles.
Sams Teach Yourself Visual J++ 6 in 21 Days
(Publisher: Macmillan Computer Publishing)
Author(s): Rick Leinecker
ISBN: 0672313510
Publication Date: 11/01/98
What Should I Do to Prevent Bugs Like This?
Ask how you can change your ways to make this kind of bug impossible by definition. By changing methods or tools, its often possible to completely eliminate a whole class of failures instead of shooting the bugs down one by one.
Start by asking when the bug was introduced: when in the development life cycle could the bug have been prevented? You might think:
The design is OK; I introduced this bug in coding.
Examine the reason for the bug in detail. Think about the process that was going on at the moment the bug was introduced, and ask how it could be changed to prevent the bug. For example:
Separate data types for offset and length would have caught this error at
compilation time.
Each text item could be output with a macro that hides the subscripting
calculation. Then I can figure this out just once.
Dont be satisfied with glib answers. Suppose your explanation for a bug is I just forgot. How can the process be changed so that you dont need to remember? The language can be changed so that the detail you omitted is completely hidden, or your omission is detected and causes a compiler diagnostic. You could use a language preprocessor for this problem domain, or a clever editing tool that fills in defaults, checks for errors, or provides hints and rapid documentation. The bug might be a symptom of communication problems in the programming team, or of conflicting design assumptions that need discussion.
Consider the way the bug was found, and ask how it could have been found earlier. How could testing be made more air-tight? Could tests be generated automatically? Could inline checking code be added that would trap errors all the time? You might think these kinds of thoughts:
I should try a zero length string in my unit tests.
I could enable subscript checking and catch this sooner.
Systematic methods and automated tools for compilation, build, and test are always worth creating. They pay for themselves quickly, by eliminating long debugging and fact-finding sessions.
Applications of the Technique
Make a habit of asking the three questions every time you find a bug. You dont even have to wait for a bug to use the three questions.
During design and implementation review, each comment you get can be treated with the three questions. Review comments are the result of an underlying communication process that you can improve. If you feel that a readers comment on a specification is wrong, for example, you might ask what kept your document from being understood, and how you can communicate better with the reviewer.
Three Approaches to Debugging
I have three approaches to debugging. I use each of them at different times. Deciding which method to use is more black art than logical reasoningat least for me. I have an intuition about it that serves me well. Im also ready at any moment during a debugging session to change my approach.
The Wishful-Thinking Approach
The wishful-thinking approach is the easiest approach to debugging. If it works, its the fastest way to arrive at a solution. Its also the least effective. But its where most of us start when we first try to debug applications. I still use it if I feel that the solution is something trivialespecially if I think its something more along the lines of a typographical error in the code that Ill spot pretty quickly.
Heres how you do it. Run the debugger. Set a breakpoint in the code ahead of where you suspect the problem is located. Then step into methods or step over them. Step over methods youre sure are okay, and step into methods youre not sure about.
As you do this, your mind cant help doing a mental simulation of the code execution while the debugger is in operation. When I use this method of debugging, 90% of the time I find the problem when Im getting close to the code with the error. Its because as I get close, I realize in my mind whats wrong. Its almost as if the debugger is nothing more than a crutch to force my mind to carefully simulate the codes execution. |
Tip: Use the wishful-thinking approach to debugging when your program is relatively small, or when you think the problem is fairly trivial. This approach will save you time if the bug is easy to find, but it will be a big waste for bugs that are hard to find.
The Split-the-Difference Approach
Have you ever played the game in which someone thinks of a number, and you have a certain number of guesses to determine the number? Lets say youre guessing a number from 1 to 100. The wisest approach is to guess 50. Then if its above 50, you guess 75. If its below 75, you guess halfway between 50 and 7563. And the game goes on like this. Basically, youre splitting the difference by guessing a number thats midway in the range into which the target number falls. At least thats how the logicians suggest playing that game. You might have a bolder strategy.
This split-the-difference method is similar to the numbers guessing game. You start off with the given information. You know that everything is fine at a certain point, but youre sure its not fine at this other point. Okay, you have a range of source code in which you know the problem occurs.
Start off by splitting the difference. Set a breakpoint halfway into the code. When the debugger stops at your breakpoint, examine all the variables. Is everything okay? If there are no detectable problems, chances are that the error occurred later. If the problem has surfaced, it was in the first half of the code.
Now that you know in which half of the code the problem occurred, clear the breakpoint youve set, and set a breakpoint in the middle of that code. Then restart the program. You proceed with this process, narrowing the area in which the error occurs. Finally, when you get a manageable piece of code, step through it and find the exact line at which the error occurs. |
Tip: Use the split-the-difference approach to debugging if you feel that the error will be somewhat difficult to find. Its also recommended if your program is of a medium to large size.
The Assume-Nothing Approach
The assume-nothing approach to debugging is my last resort. Its the most rigorous approach to debugging, and it takes the most time. You still have to identify the block of code in which the error occurs before you start. If this is the entire program, so be itthats the block of code youll have to work with.
Heres how it works. Lets say you have a method. Somewhere in that method, a variable is becoming negative when it should always be positive. Start by commenting out all the code and recompiling. To comment out source code, just place two / characters at the beginning of each line of code. Two methods follow. The first method is normal; the second one has all the lines of source code commented out:
private void initForm()
{
this.setBackColor(Color.CONTROL);
this.setLocation(new Point(0, 0));
this.setSize(new Point(300, 300));
this.setTabIndex(-1);
this.setTabStop(true);
this.setText("Form1");
this.setAutoScaleBaseSize(13);
this.setClientSize(new Point(292, 273));
this.addOnClick(new EventHandler(this.Form1_click));
}
private void initForm()
{
// this.setBackColor(Color.CONTROL);
// this.setLocation(new Point(0, 0));
// this.setSize(new Point(300, 300));
// this.setTabIndex(-1);
// this.setTabStop(true);
// this.setText("Form1");
// this.setAutoScaleBaseSize(13);
// this.setClientSize(new Point(292, 273));
// this.addOnClick(new EventHandler(this.Form1_click));
}
With all the source code in your method commented out, you know for certain that the variable cant become negative. Rebuild the program and run the debugger. Make sure that the variable never becomes negative. If it does, the problem is elsewhere. If it never becomes negative, youre correct in assuming that the problem is in your method.
Now, add back lines of code to the method by uncommenting them. Try adding one line of code at a time. Sometimes you cant and you have to add two or three lines. Each time you uncomment lines of code, rebuild the application and run the debugger. When you finally add the code that makes the variable become negative, youve found the problem.
This example is actually pretty trivial compared to some situations Ive encountered. Still, though, it shows you how to implement the assume-nothing approach to debugging. |
Tip: Use the assume-nothing approach to debugging as a last resort. It takes the most time but is the most systematic when you need to find an error thats difficult to uncover.
|