Debugging by Thinking. A Multidisciplinary Approach
Authors: Metzger R. C.
Published year: 2002
Pages: 47-49/172
Buy this book on amazon.com >>

7.11 Review

Every debugging strategy consists of three parts : a set of assumptions, a control structure, and an evaluation mechanism. The purpose of the evaluation mechanism is to tell you whether you’re making progress.

The binary search strategy is analogous to the binary search algorithm taught in every beginner’s data structures or algorithms class. This strategy assumes that the code segments under investigation have some linear ordering.

The greedy search strategy is analogous to the greedy method taught in introductory algorithms classes. This strategy assumes that the program is divisible into segments that can be tested for responsibility for the defect. The key to the greedy search strategy is finding a selection method that maximizes the benefit and minimizes the cost for each iteration of the strategy.

A program slice gives the set of statements and predicates in a program that may affect the value of a set of variables at specific location in that program. The program slice strategy assumes that a tool is available that will perform control- and data-flow analysis on the application and provide the slicing information.

The deductive-analysis strategy reasons from a general body of knowledge to a specific conclusion about the problem at hand. This strategy assumes that you have extensive experience in programming to draw on and a thorough understanding of the details of the application you’re debugging.

The inductive-analysis strategy reasons from a set of specific facts to an increasingly precise hypothesis that explains them. This strategy assumes that there exists a set of observations to start from and that there is a method for evaluating the quality of the hypotheses generated.

Choose a debugging strategy based on which set of assumptions fit the situation you’re working on.



Chapter 8: Debugging Heuristics

I have found it! (in Greek, “Heurisko!”)

—Archimedes

8.1 Preview

This chapter presents the definitions of debugging heuristics, with examples of using each to find actual bugs . It explains that debugging heuristics don’t lend themselves to easy categorization, as is the case with strategies and tactics. It notes that many debugging heuristics are similar to the mathematical heuristics described in Polya’s book.

We have identified the following debugging heuristics:

  • Modify the program so that you can repeat the problem at will.

  • Create a test case that will demonstrate the problem when desired.

  • Reduce the input required to reproduce the problem.

  • Categorize the problem according to a standard set of questions.

  • Describe the problem according to a standard methodology.

  • Explain the problem to someone else.

  • Recall a similar problem from the past.

  • Draw a diagram.

  • Choose a hypothesis from historical data.



8.2 Stabilize the program

Most works on debugging recommend a process often called stabilizing the problem . The goal of stabilization is reproducibility . Defects that can’t be stabilized can’t be fixed. Stabilizing a defect can be the hardest part of the debugging task.

The first step in stabilizing a bug is to run the defective program several times. If you don’t always get an undesired behavior, you will need to modify the program or the environment in which you’re running it. These types of defects will show a premature termination on one run, an infinite execution on another run, and so on.

Even getting an undesired behavior each time is no guarantee that you don’t need to stabilize at this level. It can just mean that you haven’t run the program enough times for a nondeterministic defect to show itself, or that you haven’t run it in the right environment or on the right platform.

At this point, if you do get different behaviors on different runs, several hypotheses should suggest themselves . These types of bugs often involve memory corruption, data-structure violations, or problems with the environment or platform.

There is one circumstance in which it may not be possible to cause the undesirable behavior on each execution. This occurs when your application has multiple independent streams of control. Parallel execution can occur at the thread level or the process level. If you know that the application has parallel execution, then you may need to be satisfied with increasing the frequency of the problem occurrence. When a problem is caused by nondeterminism, it’s often impossible to cause a problem to manifest on every run.

The second step in stabilizing a bug is to run the defective program several times again if you had to make changes to cause an undesired behavior to occur with each run. If the same undesired behavior occurs with each run, you can move on to the next step. If you don’t always get the same undesired behavior, you will need to modify the program or the environment you’re running it in again.

Once you have completed this task, the same group of hypotheses should suggest themselves. These types of bugs often involve memory corruption, data-structure violations, or problems with the environment or platform.

Now that you’re getting the same undesired behavior with each execution of the program, you would be wise to try another level of stabiliza tion. Try running the offending program in a different environment. By environment, we mean things like user account, environment variables , registry settings, system load, and so forth. A different environment still means on the same platform, by which we mean operating system and hardware architecture.

Under what circumstances is this type of stabilization helpful? If a problem occurs on the submitter’s account, but not on the developer’s account, a hypothesis of initialization errors, data-structure violations, or memory problems is a good place to start. The same hypotheses are also indicated if a program fails when running standalone but works correctly when running under a debugger.

Finally, it’s sometimes useful to try running the offending program on a different platform. Of course, if the program relies on specific features of the operating system or hardware, this may not be possible. Even if a completely different platform is out of the question, you may be able to use related platforms, such as different versions of the same operating system. For example, you might not be able to take a program developed on a version of UNIX and run it on Windows , but you might be able to run it on Linux .

Under what circumstances is this type of stabilization helpful? If you run the program on a different platform and notice that discrepancies in floating-point output change, a hypothesis of value corruption due to different floating-point hardware or libraries is reasonable. If the application is dependent on vendor-provided versions of industry-standard libraries, and you see differences in behavior from one platform to another, a hypothesis of problems in other people’s software is warranted.

Stabilization is one of the first things we do when diagnosing a bug. If done correctly, it can provide you with a number of qualified working hypotheses.


Debugging by Thinking. A Multidisciplinary Approach
Authors: Metzger R. C.
Published year: 2002
Pages: 47-49/172
Buy this book on amazon.com >>

Similar books on Amazon