// sum.c: 1〜Nまでの総和を計算する(...はずが、バグあり)#include<stdio.h>intsum_to(int n) {
int s = 0;
for (int i = 1; i < n; i++) { // バグ: i <= n が正しい
s += i;
}
return s;
}
intmain(void) {
int r = sum_to(10);
printf("sum = %d\n", r); // 期待: 55、実際: 45return0;
}
基本セッション: break / run / step / print
$ gcc -g sum.c -o sum
$ gdb ./sum
(gdb)break sum_to# 関数先頭にブレークポイント
Breakpoint 1 at 0x1149: file sum.c, line 5.
(gdb)run# 実行
Starting program: /path/sum
Breakpoint 1, sum_to (n=10) at sum.c:5
5 int s = 0;
(gdb)next# 次の行に進む(関数に入らない)
6 for (int i = 1; i < n; i++) {
(gdb)print n# 変数nの値
$1 = 10
(gdb)print i
No symbol "i" in current context. # まだ宣言されていない(gdb)next
7 s += i;
(gdb)print i
$2 = 1 # ループに入ってi=1(gdb)continue# 次のブレークポイントまで
sum = 45 # 期待は55なのにおかしい
[Inferior 1 (process ...) exited normally]
条件付きブレークポイント
(gdb)break sum.c:7 if i == 5# i==5 のときだけ止まる(gdb)run
Breakpoint 2, sum_to at sum.c:7
(gdb)print s
$3 = 10 # 0+1+2+3+4 = 10 ✓
step vs next:step(s) は関数の中に入る、next(n) は関数を1行として飛ばす。ライブラリ関数に誤って step で潜らないよう注意。
printf スタイル出力(display)
(gdb)display i# 停止ごとに i を自動表示(gdb)display s(gdb)next
...
1: i = 3
2: s = 3
スタックトレース(bt / frame)
関数が深く呼ばれている途中でバグを見つけたとき、誰がその関数を呼んだかを知りたい。
(gdb)backtrace# bt と略せる
#0 sum_to (n=10) at sum.c:7
#1 main () at sum.c:13
(gdb)frame 1# 呼び出し元(main)のフレームへ
#1 main () at sum.c:13
13 int r = sum_to(10);
(gdb)print r
$4 = 0 # まだ呼び出し中なので初期値(gdb)frame 0# 元に戻る
再帰関数のデバッグ: 再帰呼び出し中のどの階層にいるかを bt で一覧できる。frame n で各階層に行き来し、引数の値を確認できる。
ウォッチポイント(変数が書き換えられた瞬間に止まる)
「この変数がいつの間にかおかしな値になっている」というバグを追うのに最強。
(gdb)break main(gdb)run(gdb)watch r# 変数rへの書き込み監視
Hardware watchpoint 2: r
(gdb)continue
Hardware watchpoint 2: r
Old value = 0
New value = 45
main () at sum.c:13 # r に書き込まれた瞬間に停止
$ gcc -g crash.c -o crash
$ gdb ./crash
(gdb)run
Program received signal SIGSEGV, Segmentation fault.
__strcpy_avx2 () at .. # libc の中(gdb)bt
#0 __strcpy_avx2 () from /lib/libc.so.6
#1 0x... in main () at crash.c:6 # ← ここが自分のコード!(gdb)frame 1
#1 main () at crash.c:6
6 strcpy(p, "hello");
(gdb)print p
$1 = 0x0 # NULLだと判明