Drawing the following diagrams can be a useful heuristic:
A control-flow graph with decisions actually taken
A data-flow graph that shows paths actually taken
Complex data structures that use pointers
A control-flow graph is a directed graph in which executed statements (or procedures) are represented by the nodes, and control flow is represented by the arcs. For the purpose of hypothesis generation, limit your control-flow graph to statements or procedures that were actually executed. You can develop this information using a program trace or an execution profile.
A data-flow graph is a directed graph in which assignments and references to variables are represented by the nodes, and information flow is represented by the arcs. For the purpose of hypothesis generation, limit your data-flow graph to assignments and references that were actually executed. Obviously, it’s much easier to develop this graph if you have first developed a statement-level control-flow graph.
What kind of hypotheses do these diagrams suggest? If you see an unanticipated control-flow path, a reasonable hypothesis is that there is a problem with one of the predicates that controls the path. If you see a missing flow of information, a reasonable hypothesis is that there is a problem with the predicates controlling the statements that did not get executed, in between the assignment and reference of the variable in question.
Given a diagram of the static relationships of a complex structure built from pointers, and given that you know that this structure is being manipulated when the problem manifests itself, ask yourself how this structure could be violated. What assignments could be missed or performed in the wrong order, causing the structure to lose integrity? Each would give rise to a reasonable hypothesis.
Other useful diagrams you may want to draw are application specific. You can work either from a representation of the actual behavior of the program, such as the control-flow or data-flow graph, or from a representation of the ideal behavior of the program, such as the data-structure layout. In the former case, look for actual anomalies. In the latter case, look for potential anomalies.