Finding the Fault
A running program is state: register values, memory contents, and the program counter. A bug is where that state diverges from correct execution.
Debugging makes state observable using a debugger with three primary tools:
| Tool | Action |
|---|---|
| Breakpoint | Pause execution at an instruction |
| Inspect | View actual registers and memory |
| Single-step | Execute one instruction at a time |
The strategy is a binary search in time: verify state at an early point, verify it is wrong later, and narrow the gap to find the first incorrect instruction.
Pitfall: Debugging by guessing. Never change code on intuition; inspect actual values at breakpoints. Reason strictly from evidence.
Bug Families & Evidence
Machine-level bugs cluster into five recognizable families that speed up the debugging hunt:
| Bug Family | Description |
|---|---|
| Off-by-one | Error in loop bound calculations |
| Integer overflow | Value wraps around unexpectedly |
| Wrong address | Reading or writing incorrect memory |
| Uninitialized | Using leftover state in cells/registers |
| Signed/unsigned | Misinterpreting number representations |
The overriding principle is to reason from evidence, not assumption. Observe what the state actually is at each step, letting the discrepancy point directly to the fault.