debug-optimized-builds
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDebugging Optimized Builds
优化构建调试
Purpose
用途
Guide agents through debugging code compiled with optimization: choosing the right debug-friendly optimization level, reading inlined frames, diagnosing "value optimized out", using split-DWARF for faster debug builds, and applying GDB techniques specific to optimized code.
指导开发者调试经过编译优化的代码:选择合适的调试友好型优化级别、查看内联帧、诊断“value optimized out”问题、使用split-DWARF加速调试构建,以及应用针对优化代码的GDB专属技巧。
Triggers
触发场景
- "GDB says 'value optimized out' — what does that mean?"
- "How do I debug a release build?"
- "How do I see inlined function frames in GDB?"
- "What's the difference between -O0 and -Og for debugging?"
- "How do I use RelWithDebInfo with CMake?"
- "Breakpoints in optimized code land on wrong lines"
- “GDB提示‘value optimized out’是什么意思?”
- “如何调试发布构建版本?”
- “如何在GDB中查看内联函数帧?”
- “调试时-O0和-Og有什么区别?”
- “如何在CMake中使用RelWithDebInfo?”
- “优化代码中的断点定位到错误行”
Workflow
工作流程
1. Choose the right build configuration
1. 选择合适的构建配置
Goal?
├── Full debuggability, no optimization
│ → -O0 -g (slowest, all vars visible)
├── Debuggable, some optimization (recommended for most dev work)
│ → -Og -g (-Og keeps debug experience good)
├── Release build with debug info (shipped, debuggable crashes)
│ → -O2 -g -gsplit-dwarf (or -O2 -g1 for lighter info)
└── Full release (no debug symbols)
→ -O2 -DNDEBUG-Ogbash
undefined目标?
├── 完全可调试,无优化
│ → -O0 -g (速度最慢,所有变量可见)
├── 可调试,带部分优化(推荐用于大多数开发工作)
│ → -Og -g (-Og能保持良好的调试体验)
├── 带调试信息的发布构建(已发布版本,可调试崩溃问题)
│ → -O2 -g -gsplit-dwarf (或使用-O2 -g1获取更精简的信息)
└── 完整发布版本(无调试符号)
→ -O2 -DNDEBUG-Ogbash
undefinedGCC / Clang
GCC / Clang
gcc -Og -g -Wall main.c -o prog
gcc -Og -g -Wall main.c -o prog
CMake build types
CMake构建类型
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug # -O0 -g
cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo # -O2 -g -DNDEBUG
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release # -O2 -DNDEBUG
undefinedcmake -S . -B build -DCMAKE_BUILD_TYPE=Debug # -O0 -g
cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo # -O2 -g -DNDEBUG
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release # -O2 -DNDEBUG
undefined2. "Value optimized out" — causes and workarounds
2. “value optimized out”——原因与解决方法
text
(gdb) print my_variable
$1 = <optimized out>This means the compiler decided the variable's value doesn't need to be stored at this point — it might be:
- Kept only in a register (not the one GDB is looking at)
- Folded into a constant by constant propagation
- Eliminated because it's not used after this point
- Replaced by a later optimized value
Workarounds:
c
// 1. Mark variable volatile (prevents optimization away)
volatile int counter = 0;
// Use sparingly — changes semantics
// 2. Use GCC attribute
int counter __attribute__((used)) = 0;
// 3. Compile problematic TU at lower optimization
// In CMake:
set_source_files_properties(tricky.c PROPERTIES COMPILE_FLAGS "-O0")
// 4. Use -Og instead of -O2 for the whole build
// 5. Look at register values directly
// (gdb) info registers
// (gdb) p/x $rax # value may be in a registertext
(gdb) print my_variable
$1 = <optimized out>这表示编译器判定该变量的值在此处无需存储,可能的情况包括:
- 仅保留在寄存器中(GDB未查看该寄存器)
- 通过常量传播被折叠为常量
- 因为在此之后不再使用而被消除
- 被后续优化后的值替代
解决方法:
c
// 1. 将变量标记为volatile(防止被优化掉)
volatile int counter = 0;
// 谨慎使用——会改变语义
// 2. 使用GCC属性
int counter __attribute__((used)) = 0;
// 3. 针对有问题的编译单元使用较低优化级别
// 在CMake中:
set_source_files_properties(tricky.c PROPERTIES COMPILE_FLAGS "-O0")
// 4. 为整个构建使用-Og而非-O2
// 5. 直接查看寄存器值
// (gdb) info registers
// (gdb) p/x $rax # 值可能存储在寄存器中3. Reading inlined frames in GDB
3. 在GDB中查看内联帧
With optimization, frequently-called small functions get inlined. GDB shows these as extra frames:
text
(gdb) bt
#0 process_packet (data=0x7ff..., len=<optimized out>)
at network.c:45
#1 0x0000... in dispatch_handler (pkt=0x7ff...)
at handler.c:102
#2 (inlined by) event_loop () at main.c:78
#3 0x0000... in main () at main.c:200开启优化后,频繁调用的小函数会被内联。GDB会将这些显示为额外的帧:
text
(gdb) bt
#0 process_packet (data=0x7ff..., len=<optimized out>)
at network.c:45
#1 0x0000... in dispatch_handler (pkt=0x7ff...)
at handler.c:102
#2 (inlined by) event_loop () at main.c:78
#3 0x0000... in main () at main.c:200(inlined by) frames are virtual — they show the call chain
(inlined by) frames are virtual — they show the call chain
that was inlined into the actual frame above
that was inlined into the actual frame above
```bash
```bashNavigate inlined frames
导航内联帧
(gdb) frame 2 # jump to the inlined frame
(gdb) up # move up through frames (including inlined)
(gdb) down # move down
(gdb) frame 2 # 跳转到内联帧
(gdb) up # 向上移动帧(包括内联帧)
(gdb) down # 向下移动
Show all frames including inlined
显示所有帧包括内联帧
(gdb) backtrace full
(gdb) backtrace full
Set breakpoint inside inlined function
在内联函数内设置断点
(gdb) break network.c:45 # may hit multiple inlined call sites
(gdb) break process_packet # hits all inline expansions
undefined(gdb) break network.c:45 # 可能命中多个内联调用点
(gdb) break process_packet # 命中所有内联展开点
undefined4. Line number discrepancies
4. 行号不一致问题
Optimizers reorder instructions, so the "current line" in GDB may jump around:
bash
undefined优化器会重排指令,因此GDB中的“当前行”可能会跳来跳去:
bash
undefinedSee which instructions map to which source lines
查看哪些指令对应哪些源代码行
(gdb) disassemble /s function_name # interleaved source and asm
(gdb) disassemble /s function_name # 交错显示源代码和汇编
Step by machine instruction (more accurate in optimized code)
按机器指令单步执行(在优化代码中更准确)
(gdb) si # stepi — one machine instruction
(gdb) ni # nexti — one machine instruction (no step into)
(gdb) si # stepi — 单步执行一条机器指令
(gdb) ni # nexti — 单步执行一条机器指令(不进入函数)
Show mixed source/asm at current point
在当前位置显示混合的源代码/汇编
(gdb) layout split # TUI mode: source + asm side by side
(gdb) set disassemble-next-line on
(gdb) layout split # TUI模式:源代码 + 汇编分栏显示
(gdb) set disassemble-next-line on
Jump to specific address (when line stepping is unreliable)
跳转到指定地址(当按行单步不可靠时)
(gdb) jump *0x400a2c
undefined(gdb) jump *0x400a2c
undefined5. GDB scheduler-locking for optimized multithreaded code
5. 针对优化后多线程代码的GDB调度锁
With optimization, threads may race in unexpected ways when stepping:
bash
undefined开启优化后,单步调试时线程可能会出现意外的竞态情况:
bash
undefinedLock the scheduler — only the current thread runs while stepping
锁定调度器——单步调试时仅当前线程运行
(gdb) set scheduler-locking on
(gdb) set scheduler-locking on
Modes:
模式:
off — all threads run freely (default)
off — 所有线程自由运行(默认)
on — only current thread runs while stepping
on — 单步调试时仅当前线程运行
step — only current thread runs while single-stepping
step — 仅在单步执行时当前线程运行
(all run on continue)
(继续运行时所有线程都运行)
replay — for reverse debugging
replay — 用于反向调试
Common debugging session
常见调试会话流程
(gdb) set scheduler-locking step # prevent other threads interfering with step
(gdb) break my_function
(gdb) continue
(gdb) set scheduler-locking on # lock while examining
(gdb) next
(gdb) set scheduler-locking off # unlock to continue normally
undefined(gdb) set scheduler-locking step # 防止其他线程干扰单步调试
(gdb) break my_function
(gdb) continue
(gdb) set scheduler-locking on # 查看时锁定
(gdb) next
(gdb) set scheduler-locking off # 恢复正常运行时解锁
undefined6. split-DWARF — faster debug builds
6. split-DWARF——加速调试构建
Split DWARF offloads debug info to files, reducing linker input:
.dwobash
undefinedSplit DWARF将调试信息分流到.dwo文件中,减少链接器的输入:
bash
undefinedCompile with split DWARF
用Split DWARF编译
gcc -g -gsplit-dwarf -O2 -c file.c -o file.o
gcc -g -gsplit-dwarf -O2 -c file.c -o file.o
Creates: file.o (object) + file.dwo (DWARF sidecar)
生成:file.o(目标文件) + file.dwo(DWARF辅助文件)
Link — no debug info in final binary, just references
链接——最终二进制中无调试信息,仅包含引用
gcc -g -gsplit-dwarf file.o -o prog
gcc -g -gsplit-dwarf file.o -o prog
GDB finds .dwo files via the path embedded in the binary
GDB通过二进制中嵌入的路径查找.dwo文件
gdb prog # works automatically if .dwo files are next to the binary
gdb prog # 如果.dwo文件在二进制旁边,会自动生效
Package all .dwo into a single .dwp for distribution
将所有.dwo打包为单个.dwp文件用于分发
dwp -o prog.dwp prog # GNU dwp tool
gdb prog # with .dwp in same directory
dwp -o prog.dwp prog # GNU dwp工具
gdb prog # .dwp文件在同一目录即可
CMake
CMake配置
add_compile_options(-gsplit-dwarf)
undefinedadd_compile_options(-gsplit-dwarf)
undefined7. Useful GDB commands for optimized builds
7. 适用于优化构建的实用GDB命令
bash
undefinedbash
undefinedShow where variables actually live (register vs stack)
显示变量实际存储位置(寄存器 vs 栈)
(gdb) info locals # all locals (may show <optimized out>)
(gdb) info args # function arguments
(gdb) info locals # 所有局部变量(可能显示<optimized out>)
(gdb) info args # 函数参数
Force evaluation of an expression
强制计算表达式
(gdb) call (int)my_func(42) # call actual function to get value
(gdb) call (int)my_func(42) # 调用实际函数获取值
Watch a memory address directly (not a variable name)
直接监视内存地址(而非变量名)
(gdb) watch *0x7fffffffe430
(gdb) watch *0x7fffffffe430
Print memory contents
打印内存内容
(gdb) x/10xw $rsp # 10 words at stack pointer (hex)
(gdb) x/s 0x4008a0 # string at address
(gdb) x/10xw $rsp # 栈指针处的10个十六进制字
(gdb) x/s 0x4008a0 # 指定地址处的字符串
Catch crashes without debug symbols
无调试符号时捕获崩溃
(gdb) bt # backtrace — shows addresses even without symbols
(gdb) info sharedlibrary # shows loaded libs for symbol resolution
(gdb) bt # 回溯——即使无符号也会显示地址
(gdb) info sharedlibrary # 显示已加载的库用于符号解析
.gdbinit helpers for optimized debugging
用于优化调试的.gdbinit辅助配置
set print pretty on
set print pretty on
set print array on
set print array on
set disassembly-flavor intel
set disassembly-flavor intel
undefinedundefinedRelated skills
相关技能
- Use for full GDB session management
skills/debuggers/gdb - Use for DWARF debug info details
skills/debuggers/dwarf-debug-format - Use for post-mortem debugging of optimized crashes
skills/debuggers/core-dumps - Use for
skills/compilers/gcc,-Og, and debug flag selection-g
- 使用进行完整的GDB会话管理
skills/debuggers/gdb - 使用了解DWARF调试信息细节
skills/debuggers/dwarf-debug-format - 使用对优化后的崩溃进行事后调试
skills/debuggers/core-dumps - 使用进行-Og、-g等调试标志的选择
skills/compilers/gcc