How do you choose a strategy for a given debugging effort? The assumptions provide the key.
The binary search strategy is very powerful but requires a linear ordering of code segments. This means that it’s applied to programs whose components act as a series of filters on the input, applied one after another. A program that is structured with lots of callbacks to event handlers won’t work with this strategy.
The greedy search strategy requires that the programmer keep good records. He or she must be comfortable with a quantitative approach to decision making and consistent in collecting the metrics required for input to the strategy. A development environment in which it’s easy to run test cases and collect statistics is highly desirable if you’re going to use the greedy search strategy. If that environment includes scripting tools for processing the statistics, it will make using this strategy that much easier.
The breadth-first search strategy makes sense when you’re working on a system that is unfamiliar to you. It works like peeling an onion. You explore each level of the control-flow hierarchy in turn. It allows you to determine whether a part of an application is causing the defect without actually knowing all about the part in question. The breadth-first strategy is more conservative than the depth-first search and tends to require less work when the depth of the leaves varies widely.
The depth-first search strategy makes sense if you have reason to believe that the problem is localized to a particular area of an application. If diagnosis will cause you to look at widely separate sections of the application, this strategy may result in analyzing many procedures that aren’t relevant to the problem. The depth-first strategy is more aggressive than the breadth-first search and tends to evaluate fewer candidates when the depth of the leaves varies little.
The program slicing strategy assumes either that a slicing tool is available or that it’s tractable to do the data-flow analysis by hand. If the problem shows up in a local variable whose values are only fed by arguments to a procedure, manual analysis may be possible. Otherwise, the tool is necessary to apply this strategy in a timely manner. There is a commercial tool for slicing that currently supports the C language, and there are academic projects that have support for C++ as well. See Chapter 14 for more information on slicing tools.
The concept of slicing has been known for over twenty years at the time this book goes to press. The growth in the research understanding of the problems in implementing a slicing system has grown considerably. We have reason to be optimistic about the future availability of slicing tools for other programming languages.
The deductive-analysis strategy works best for experienced programmers who are knowledgeable about all aspects of the system on which they’re working. It requires a large body of knowledge about programming in general and the application in particular. The deductive-analysis strategy is most useful for small- to medium-sized systems that were designed by very small teams. A programmer who uses the deductive strategy should be one of the primary developers of the system.
The inductive-analysis strategy assumes that the programmer can generate lots of observations that will inform his hypotheses. If the programmer cannot have direct access to the application to run experiments for logistical or security reasons, don’t choose this strategy. If turnaround time for experiments will take hours or even days, don’t choose this strategy.