GNU Debugger (GDB)

デバッグC/C++LinuxコマンドラインGNUシステムプログラミングバックトレースブレークポイント

デバッグツール

GNU Debugger (GDB)

概要

GDBは、GNU プロジェクトのデバッガーです。C、C++、Go、Rust等の言語に対応し、コマンドライン操作でプログラムの実行を制御してバグの特定を可能にする、Unix/Linux環境でのデバッグ標準ツールです。

詳細

GNU Debugger(GDB)は、1986年にリチャード・ストールマンによって開発が開始されたGNUプロジェクトの主要なデバッガーです。最新バージョンはGDB 16.3(2025年4月リリース)で、継続的に機能強化が行われています。GDBは「プログラムの内部で何が起こっているかを見る」「プログラムがクラッシュした際に何をしていたかを調べる」という4つの主要機能を提供し、指定した条件でプログラムを開始、停止、実行状況の検査、実験的な変更を行うことができます。

コマンドライン インターフェースによる操作が基本ですが、TUI(テキストユーザーインターフェース)モードも提供され、gdb -tuiオプションでソースコードを表示しながらのデバッグが可能です。マルチスレッドプログラムのデバッグ、リモートデバッグ、コアダンプ解析、逆実行デバッグなど高度な機能を備え、最新版ではマシンコード表示機能も強化されています。

現在もアクティブに開発が継続され、IDEの普及によりGUI経由での利用が増加していますが、サーバー環境やシステムレベルでのデバッグにおいては依然として重要な位置を占めています。コンパイル時に-g3 -O0オプションを使用することで最適なデバッグ体験を提供します。

メリット・デメリット

メリット

  • 無料・オープンソース: GPLライセンスで自由に使用可能
  • 幅広い言語サポート: C、C++、Go、Rust、Fortranなど多言語対応
  • 豊富な機能: ブレークポイント、バックトレース、変数検査、逆実行など
  • クロスプラットフォーム: Linux、macOS、Windows、FreeBSDで動作
  • リモートデバッグ: ネットワーク経由でのデバッグ可能
  • スクリプト対応: Python APIによる自動化とカスタマイズ
  • IDE統合: Visual Studio Code、Eclipse、CLionなどとの統合
  • システムレベル: カーネルデバッグやコアダンプ解析が可能

デメリット

  • 学習コストの高さ: コマンドライン操作の習得が必要
  • 初心者には敷居が高い: 基本的なUnix/Linuxの知識が前提
  • パフォーマンス影響: デバッグ時のプログラム実行速度低下
  • GUI不足: 基本的にテキストベースインターフェース
  • 設定の複雑さ: 高度な機能利用時の設定が煩雑
  • エラーメッセージ: 技術的で初心者には理解困難な場合も
  • マルチプロセス: 複数プロセスのデバッグ時の複雑性

主要リンク

書き方の例

プログラムのコンパイルとGDB起動

# デバッグ情報付きでコンパイル
gcc -g3 -O0 -o myprogram myprogram.c

# GDBでプログラムを起動
gdb ./myprogram

# TUIモードで起動(ソースコード表示)
gdb -tui ./myprogram

# 既存のプロセスにアタッチ
gdb -p <プロセスID>

# コアダンプ解析
gdb ./myprogram core

基本的なGDBコマンド

# プログラムを実行
(gdb) run
(gdb) r

# 引数付きで実行
(gdb) run arg1 arg2
(gdb) set args arg1 arg2
(gdb) run

# ブレークポイント設定
(gdb) break main
(gdb) b main
(gdb) break 25          # 25行目
(gdb) break myfile.c:25 # ファイル指定
(gdb) break func_name   # 関数名指定

# ブレークポイント一覧表示
(gdb) info breakpoints
(gdb) info b

# ブレークポイント削除
(gdb) delete 1          # 番号指定
(gdb) clear            # 現在位置

プログラム実行制御

# ステップ実行(関数呼び出しもステップイン)
(gdb) step
(gdb) s

# ネクスト実行(関数呼び出しはステップオーバー)
(gdb) next
(gdb) n

# 実行継続
(gdb) continue
(gdb) c

# 関数から抜ける
(gdb) finish

# 指定行まで実行
(gdb) until 30
(gdb) u 30

変数とメモリの検査

# 変数の値表示
(gdb) print variable_name
(gdb) p variable_name
(gdb) p *ptr                # ポインタの参照先
(gdb) p array[0]            # 配列要素
(gdb) p struct_var.member   # 構造体メンバ

# 型情報表示
(gdb) ptype variable_name
(gdb) whatis variable_name

# メモリダンプ
(gdb) x/10x $sp            # スタックポインタから10ワード
(gdb) x/s string_ptr       # 文字列として表示
(gdb) x/i $pc              # 命令として表示

# 変数の値変更
(gdb) set variable_name = new_value
(gdb) set var i = 10

バックトレースとスタック情報

# コールスタック表示
(gdb) backtrace
(gdb) bt
(gdb) bt full              # ローカル変数も表示

# スタックフレーム移動
(gdb) frame 0              # フレーム0に移動
(gdb) f 0
(gdb) up                   # 上位フレーム
(gdb) down                 # 下位フレーム

# フレーム情報表示
(gdb) info frame
(gdb) info locals          # ローカル変数
(gdb) info args            # 引数

高度なデバッグ機能

# 条件付きブレークポイント
(gdb) break 25 if i == 100
(gdb) condition 1 i == 100  # 既存ブレークポイントに条件追加

# ウォッチポイント(変数値変更時に停止)
(gdb) watch variable_name
(gdb) rwatch variable_name  # 読み取り時
(gdb) awatch variable_name  # 読み書き両方

# 関数呼び出し
(gdb) call function_name(args)
(gdb) call printf("Debug: %d\n", i)

# マルチスレッドデバッグ
(gdb) info threads         # スレッド一覧
(gdb) thread 2             # スレッド2に切り替え
(gdb) thread apply all bt  # 全スレッドのバックトレース

リモートデバッグ

# ターゲット側(デバッグ対象)
gdbserver :1234 ./myprogram

# ホスト側(GDB実行)
gdb ./myprogram
(gdb) target remote hostname:1234
(gdb) continue

GDBスクリプトとカスタマイズ

# .gdbinitファイルでの初期設定
set confirm off
set verbose off
set prompt (my-gdb) 
set logging on
set print pretty on

# カスタムコマンド定義
define print_and_continue
    print $arg0
    continue
end

# Pythonスクリプト
python
import gdb
class MyCommand(gdb.Command):
    def __init__(self):
        super(MyCommand, self).__init__("mycommand", gdb.COMMAND_USER)
    
    def invoke(self, arg, from_tty):
        print("Custom command executed")

MyCommand()
end

TUIモード操作

# TUI有効化・無効化
(gdb) tui enable
(gdb) tui disable

# ウィンドウ切り替え
Ctrl+X, A       # アクティブウィンドウ切り替え
Ctrl+X, 1       # ソースウィンドウのみ
Ctrl+X, 2       # ソース+アセンブリ

# スクロール
Page Up/Down    # ソースコードスクロール
Ctrl+P/N        # コマンド履歴