home account info subscribe login search My ITKnowledge FAQ/help site map contact us


 
Brief Full
 Advanced
      Search
 Search Tips
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

Bookmark It

Search this book:
 
Previous Table of Contents Next


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, it’s 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.

Don’t be satisfied with glib answers. Suppose your explanation for a bug is “I just forgot.” How can the process be changed so that you don’t 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 don’t 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 reader’s 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 reasoning—at least for me. I have an intuition about it that serves me well. I’m 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, it’s the fastest way to arrive at a solution. It’s also the least effective. But it’s where most of us start when we first try to debug applications. I still use it if I feel that the solution is something trivial—especially if I think it’s something more along the lines of a typographical error in the code that I’ll spot pretty quickly.

Here’s 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 you’re sure are okay, and step into methods you’re not sure about.

As you do this, your mind can’t 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 I’m getting close to the code with the error. It’s because as I get close, I realize in my mind what’s wrong. It’s almost as if the debugger is nothing more than a crutch to force my mind to carefully simulate the code’s 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? Let’s say you’re guessing a number from 1 to 100. The wisest approach is to guess 50. Then if it’s above 50, you guess 75. If it’s below 75, you guess halfway between 50 and 75—63. And the game goes on like this. Basically, you’re splitting the difference by guessing a number that’s midway in the range into which the target number falls. At least that’s 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 you’re sure it’s 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 you’ve 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. It’s 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. It’s 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 it—that’s the block of code you’ll have to work with.

Here’s how it works. Let’s 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 can’t 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, you’re 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 can’t 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, you’ve found the problem.

This example is actually pretty trivial compared to some situations I’ve 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 that’s difficult to uncover.


Previous Table of Contents Next


Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.