C Mastery / Debugging Optimized Builds
Part 6 — Debugging and Optimization

Debugging Optimized Builds

This chapter explains how to debug code compiled with optimization, where variables may be optimized away, reordered, or inlined.

Why This Matters

Some bugs only appear at -O2. You cannot always reproduce them in a debug build. Debugging optimized code is harder but learnable, and it is a skill every systems programmer needs.

Prerequisites

Core Concept

Optimization transforms the code: variables may live in registers, be eliminated, or be reordered; functions may be inlined. The debugger uses DWARF to map back to source, but the mapping is approximate. You debug the *optimized* machine code, not the naive source.

Examples

Building an optimized-but-debuggable binary

gcc -g -O2 main.c -o app   # keep symbols, optimize

GDB in optimized code

(gdb) break main.c:25      # may hit on a different line than expected
(gdb) print x              # "optimized out" if x has no location
(gdb) disassemble /m       # mixed source + assembly
(gdb) info locals

How It Works

At -O2, the compiler applies inlining, dead-code elimination, and value numbering. DWARF still records the best available location for each variable, but a variable may have no single location (e.g., it is folded into an expression) and shows as "optimized out."

Variations

-Og

-Og optimizes just enough to keep debugging pleasant — a good middle ground when you need optimization but still want reliable stepping.

-fno-omit-frame-pointer

Keeping frame pointers (-fno-omit-frame-pointer) makes backtraces and profiling more reliable at the cost of a register.

Common Mistakes

may just have no debug location).

Undefined Behavior

by optimization (c.opt.2).

Portability

universal.

Under the Hood

The compiler records DWARF location lists that can describe a variable's location across instruction ranges. Optimized code makes these lists complex or empty.

Practical Usage

memory.

Exercises

1. Compile a program at -O2 -g, set a breakpoint, and observe "optimized out" variables. 2. Use disassemble /m to map source to assembly. 3. Compare debugging at -O0, -Og, and -O2.

Deep Challenge

Take a bug that only manifests at -O2 (e.g., a strict-aliasing violation) and debug it in the optimized build: reproduce, disassemble, identify the transformed code, and fix the root cause. Document the process.

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.debug.opt-build06