debug-optimized-builds について
debug-optimized-builds は、GDB で最適化済みコードをデバッグする開発者向けのスキルです。最適化レベル、インラインフレーム、ソース行のずれ、split-DWARF ビルドを解説します。
主な機能
- 01デバッグ向け最適化レベルの選択
- 02「value optimized out」の理解
- 03GDB でのインラインフレーム確認
- 04最適化コードの行番号ずれの診断
- 05デバッグビルドでの split-DWARF の利用
活用シーン
- 01デバッグ情報付きリリースビルドのデバッグ
- 02GDB で確認できない変数の調査
- 03インライン関数を含む呼び出し履歴の追跡
- 04命令とソースの対応関係の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill debug-optimized-buildsインストール後、Claude Code で/debug-optimized-buildsを入力して読み込まれたか確認します。
詳細
最適化ビルドのデバッグ
目的
最適化を有効にしてコンパイルしたコードのデバッグ方法をエージェントに案内します。デバッグに適した最適化レベルの選択、インライン化されたフレームの読み取り、「value optimized out」の診断、デバッグビルドを高速化する split-DWARF の使用、最適化コードに特化した GDB のテクニックを取り上げます。
トリガー
- 「GDB に 'value optimized out' と表示されます。これはどういう意味ですか?」
- 「リリースビルドをデバッグするにはどうすればよいですか?」
- 「GDB でインライン関数のフレームを表示するにはどうすればよいですか?」
- 「デバッグでは -O0 と -Og にどのような違いがありますか?」
- 「CMake で RelWithDebInfo を使うにはどうすればよいですか?」
- 「最適化コードでブレークポイントが誤った行に設定されます」
ワークフロー
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 の「デバッグに適した最適化」です。デバッグを妨げない最適化が有効になります。変数は GDB から参照できるレジスターに保持されます。行番号も正確に保たれます。開発に最適なバランスです。
# GCC / Clang
gcc -Og -g -Wall main.c -o prog
# CMake build types
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
2. 「Value optimized out」— 原因と回避策
(gdb) print my_variable
$1 = <optimized out>
これは、コンパイラーがこの時点では変数の値を保存する必要がないと判断したことを意味します。次のような可能性があります。
- レジスターにのみ保持されている(GDB が参照しているレジスターとは異なる)
- 定数伝播によって定数に畳み込まれている
- この時点以降に使われないため、削除されている
- 後続の最適化された値に置き換えられている
回避策:
// 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
3. GDB でインライン化されたフレームを読み取る
最適化を有効にすると、頻繁に呼び出される小さな関数はインライン化されます。GDB では、これらは追加のフレームとして表示されます。
(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
# that was inlined into the actual frame above
# Navigate inlined frames
(gdb) frame 2 # jump to the inlined frame
(gdb) up # move up through frames (including inlined)
(gdb) down # move down
# Show all frames including inlined
(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
4. 行番号の不一致
最適化によって命令の順序が入れ替わるため、GDB の「現在の行」が飛び回ることがあります。
# See which instructions map to which source lines
(gdb) disassemble /s function_name # interleaved source and asm
# Step by machine instruction (more accurate in optimized code)
(gdb) si # stepi — one machine instruction
(gdb) ni # nexti — one machine instruction (no step into)
# Show mixed source/asm at current point
(gdb) layout split # TUI mode: source + asm side by side
(gdb) set disassemble-next-line on
# Jump to specific address (when line stepping is unreliable)
(gdb) jump *0x400a2c
5. 最適化されたマルチスレッドコードでの GDB のスケジューラーロック
最適化を有効にすると、ステップ実行時に予期しない形でスレッド間の競合が発生することがあります。
# Lock the scheduler — only the current thread runs while stepping
(gdb) set scheduler-locking on
# Modes:
# off — all threads run freely (default)
# on — only current thread runs while stepping
# step — only current thread runs while single-stepping
# (all run on continue)
# replay — for reverse debugging
# 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
6. split-DWARF — デバッグビルドの高速化
split-DWARF はデバッグ情報を .dwo ファイルに分離し、リンカーへの入力を減らします。
# Compile with split DWARF
gcc -g -gsplit-dwarf -O2 -c file.c -o file.o
# Creates: file.o (object) + file.dwo (DWARF sidecar)
# Link — no debug info in final binary, just references
gcc -g -gsplit-dwarf file.o -o prog
# GDB finds .dwo files via the path embedded in the binary
gdb prog # works automatically if .dwo files are next to the binary
# Package all .dwo into a single .dwp for distribution
dwp -o prog.dwp prog # GNU dwp tool
gdb prog # with .dwp in same directory
# CMake
add_compile_options(-gsplit-dwarf)
7. 最適化ビルドで役立つ GDB コマンド
# Show where variables actually live (register vs stack)
(gdb) info locals # all locals (may show <optimized out>)
(gdb) info args # function arguments
# Force evaluation of an expression
(gdb) call (int)my_func(42) # call actual function to get value
# Watch a memory address directly (not a variable name)
(gdb) watch *0x7fffffffe430
# Print memory contents
(gdb) x/10xw $rsp # 10 words at stack pointer (hex)
(gdb) x/s 0x4008a0 # string at address
# Catch crashes without debug symbols
(gdb) bt # backtrace — shows addresses even without symbols
(gdb) info sharedlibrary # shows loaded libs for symbol resolution
# .gdbinit helpers for optimized debugging
# set print pretty on
# set print array on
# set disassembly-flavor intel
関連スキル
- GDB セッション管理全般については
skills/debuggers/gdbを使用します - DWARF デバッグ情報の詳細については
skills/debuggers/dwarf-debug-formatを使用します - 最適化が原因のクラッシュを事後解析するには
skills/debuggers/core-dumpsを使用します -Og、-g、デバッグフラグの選択についてはskills/compilers/gccを使用します
コメント
Markdown 対応