ソフトウェアが止まったり落ちたりした状態をgdbで解析する

 ソフトウェアがデッドロックや無限ループ等で応答しなくなったり、Segmentation Fault等で強制終了したりする場合での、gdbを使った解析手法について簡単なまとめ。
 なお今回の内容はunixlinux開発での基本知識となっていると思うけれど、例えば組み込みlinux開発などでは活用できるのを知らずに、printfデバッグで頑張っている所が結構あるようだ。開発が楽になるので基礎として知っておいて損はないと思う。

ソフトウェアが落ちる状態の解析

 まず例外発生などでソフトウェアが異常終了する場合の解析について。
 例えば以下のコードを実行すると、メモリアクセスエラーで異常終了する。

//main.c
void hoge1(void)
{
        int *hoge = (int *)0xDEADBEEF;
        hoge[0] = 100;
}

void hoge2(void)
{
        hoge1();
}

int main(void)
{
        hoge2();
        return 0;
}

 このようにコアダンプが出力されるような異常終了の場合は、コアダンプをgdbで解析させて発生箇所を追跡できる。
 まずコアダンプ出力が無効化されているなら「ulimit -c unlimited」などで有効化しておく。
 そして実行ファイルを実行し、異常終了させてコアダンプ出力。

sh-3.2# ulimit -c unlimited
sh-3.2# gcc -g -o main main.c
sh-3.2# ./main
Segmentation fault: 11 (core dumped)

 異常終了したら、コアファイルと実行ファイルをgdbに読み込ませる。

sh-3.2# gdb main /cores/core.1015
(中略)
#0  0x000000010b675ea6 in hoge1 () at main.c:7
7		hoge[0] = 100;

 するとgdbはコアダンプのデータをメモリ展開して強制終了時の状態を再現し、各種解析コマンドを使用可能にする。ここで例えば「bt」コマンドを使えばどの関数で異常終了したか解析できる。

(gdb) bt
#0  0x000000010b675ea6 in hoge1 () at main.c:7
#1  0x000000010b675eb9 in hoge2 () at main.c:12
#2  0x000000010b675ecd in main () at main.c:17

 なお例外発生でソフトウェアが落ちる場合は、gdb上で実行ファイルを実行しつつ、「catch throw」「catch catch」などのコマンドで、例外発生時にブレークさせるようにすると、ソフトが異常終了する直前で解析ができる。

ソフトウェアが止まったり応答不能になる状態の解析

 デッドロックや無限ループなどで実行ファイルが停止し、応答不能になる場合の解析について。例えば単純だけれど以下のコードを扱う。

//main2.c
void hoge1(void)
{
        while (1) {}
}

int main(void)
{
        hoge1();
        return 0;
}

 これを実行すると無限ループでソフトウェアが停止する。

sh-3.2# gcc -g -o loop main2.c 
sh-3.2# ./loop &
(無限ループに)

 
 こういった場合は「ps」「ps x | grep 実行ファイル名」などでプロセスIDを取得し、gdbでアタッチする。アタッチには「gdb -p プロセスID」などを使用。

sh-3.2# ps x|grep loop
 1148 s000  R      2:30.53 ./loop
 1157 s000  R+     0:00.00 grep loop
sh-3.2# gdb -p 1148

 すると各種gdbの解析が有効になる。「where」や「bt」で、どこで止まっているか場所を把握できる。

(gdb) where
#0  hoge1 () at main2.c:3
#1  0x000000010de66efd in main () at main2.c:9

 なおこれは無限ループ以外にも、外部要因でポーリング状態が終わらなくなっている、デッドロックが発生しているといった問題の解析にも使える。ただメモリ不足といったリソース不足でソフトウェアが実行停止状態になる場合は、別の解析手段が必要になる。