debug-optimized-builds

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Debugging 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
-Og
: GCC's "debug-friendly optimization" — enables optimizations that don't interfere with debugging. Variables stay in registers where GDB can see them. Line numbers stay accurate. Best balance for development.
bash
undefined
目标?
├── 完全可调试,无优化
│   → -O0 -g                        (速度最慢,所有变量可见)
├── 可调试,带部分优化(推荐用于大多数开发工作)
│   → -Og -g                        (-Og能保持良好的调试体验)
├── 带调试信息的发布构建(已发布版本,可调试崩溃问题)
│   → -O2 -g -gsplit-dwarf          (或使用-O2 -g1获取更精简的信息)
└── 完整发布版本(无调试符号)
    → -O2 -DNDEBUG
-Og
:GCC的“调试友好型优化”——启用不会干扰调试的优化。变量会保留在GDB可访问的寄存器中,行号保持准确。是开发阶段的最佳平衡选择。
bash
undefined

GCC / 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
undefined
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
undefined

2. "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 register
text
(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

```bash

Navigate 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 # 命中所有内联展开点
undefined

4. Line number discrepancies

4. 行号不一致问题

Optimizers reorder instructions, so the "current line" in GDB may jump around:
bash
undefined
优化器会重排指令,因此GDB中的“当前行”可能会跳来跳去:
bash
undefined

See 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
undefined

5. GDB scheduler-locking for optimized multithreaded code

5. 针对优化后多线程代码的GDB调度锁

With optimization, threads may race in unexpected ways when stepping:
bash
undefined
开启优化后,单步调试时线程可能会出现意外的竞态情况:
bash
undefined

Lock 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 # 恢复正常运行时解锁
undefined

6. split-DWARF — faster debug builds

6. split-DWARF——加速调试构建

Split DWARF offloads debug info to
.dwo
files, reducing linker input:
bash
undefined
Split DWARF将调试信息分流到.dwo文件中,减少链接器的输入:
bash
undefined

Compile 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)
undefined
add_compile_options(-gsplit-dwarf)
undefined

7. Useful GDB commands for optimized builds

7. 适用于优化构建的实用GDB命令

bash
undefined
bash
undefined

Show 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

undefined
undefined

Related skills

相关技能

  • Use
    skills/debuggers/gdb
    for full GDB session management
  • Use
    skills/debuggers/dwarf-debug-format
    for DWARF debug info details
  • Use
    skills/debuggers/core-dumps
    for post-mortem debugging of optimized crashes
  • Use
    skills/compilers/gcc
    for
    -Og
    ,
    -g
    , and debug flag selection
  • 使用
    skills/debuggers/gdb
    进行完整的GDB会话管理
  • 使用
    skills/debuggers/dwarf-debug-format
    了解DWARF调试信息细节
  • 使用
    skills/debuggers/core-dumps
    对优化后的崩溃进行事后调试
  • 使用
    skills/compilers/gcc
    进行-Og、-g等调试标志的选择