Debugging Techniques
My notes of Back to Basics: Debugging Techniques
Relation of report to defects
1..n
/===============\ 1..n /========\-------->/=======\
| Problem Report|-------->|Symptoms| |Defects|
\===============/ \========/<--------\=======/
1..n
Procedure
bool MyJob::Debug(Product const& curr, Problem const& issue)
{
ReviewProblemReport(curr, issue);
CharacterizeAndReproduceProblem(curr, issue);
auto next = Clone(curr);
while (ReproduceProblem(next) && ResourcesAvailableToRepair(next))
{
auto insight = async(launch::async, &MyJob::UnderstandProblem, this, next);
auto location = async(launch::async, &MyJob::LocateProblem, this, next);
auto category = async(launch::async, &MyJob::ClassifyProblem, this, next);
WaitFor(insight, location, category);
next = AttemptToRepair(next);
}
return (ProblemFixed()) ? Deliver(next), true
: PossiblyUpdateResume(), false;
}
ReviewProblemReport
Problem reports can be misleading.
Author of report might know much less about the system or have wrong
assumptions.
Thus: distinguish between facts and guesses, maybe get clarification.
Characterizing
Determining the environment (version, platform, config, ...) in which symptoms
were observed.
Reproducing
- Instantiating an analogous environment in the lab or in the field.
- Run enough of the program/system to observe the reported symptoms.
- Develop test to demonstrate failure.
Understanding the Problem
Gain enough knowledge that you believe you can carry out a repair.
- locate incorrect lines
- determine root cause
- Formulate a set of proposed changes
- How do these changes affect the system?
- Do these changes fix the problem?
Locating the Problem
trace logging
generating output describing the program state during execution.
- simple:
printf
- complex: logging
- adds overhead, might be problematic with weird problems
Tools
- compiler warnings
- static code analysis (cppcheck)
- interactive debuggers
- time-travel debuggers
- sanitizers
- dynmic program analyzers (val-,call-,helgrind)
- call tracers and domain-specific diagnostic tools (strace, wireshark, SQL
analyzers)
assertions
Check pre-, post-conditions, invariants.
backtracking
- Start where you think the problem occured and step backward through the code.
- Understand program state at each backward step.
binary search
- place assertion/breaktpoints in the middle.
- If reached with invalid state, the problem occurs in first half; otherwise in
the second half.
simplification
Gradually remove sections of irrelevant code/input.
make the problem worse
Force failures more frequently.
scientific method
- Form hypothesis consistent with observation.
- Implement tests to refute hypothesis.
- If refuted: for new hypothesis.
- Repeat until hypothesis cannot be refuted.
Classifying the problem
- syntax errors
- simple errors (e.g. syntactically correct typos)
- implementation errors
- logic errors
- configuration/build errors
Repairing the problem
- Try to minimize changes.
- Test repairs.
Delivering the fix
- Don't put more than one fix in one commit.
- Don't put extra stuff in fix commits.
- Include new/updated tests in fix commit.
- Double-check
- create documentation how the defect was noticed and solved.
- maybe review processes and practices.